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))
102 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
103 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
105 (DONT_COLLIDE_WITH(e) && \
106 IS_FREE_OR_PLAYER(x, y))))
108 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
109 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
112 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
113 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
115 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
116 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
118 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
119 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
121 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
123 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
124 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
125 Feld[x][y] == EL_DIAMOND))
127 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
128 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
129 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
131 #define PACMAN_CAN_ENTER_FIELD(x, y) \
132 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
133 IS_AMOEBOID(Feld[x][y])))
135 #define PIG_CAN_ENTER_FIELD(x, y) \
136 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
137 IS_FOOD_PIG(Feld[x][y])))
139 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
140 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
141 IS_FOOD_PENGUIN(Feld[x][y]) || \
142 Feld[x][y] == EL_EXIT_OPEN || \
143 Feld[x][y] == EL_ACID))
145 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
148 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
149 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
151 /* game button identifiers */
152 #define GAME_CTRL_ID_STOP 0
153 #define GAME_CTRL_ID_PAUSE 1
154 #define GAME_CTRL_ID_PLAY 2
155 #define SOUND_CTRL_ID_MUSIC 3
156 #define SOUND_CTRL_ID_LOOPS 4
157 #define SOUND_CTRL_ID_SIMPLE 5
159 #define NUM_GAME_BUTTONS 6
162 /* forward declaration for internal use */
164 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
165 static boolean MovePlayer(struct PlayerInfo *, int, int);
166 static void ScrollPlayer(struct PlayerInfo *, int);
167 static void ScrollScreen(struct PlayerInfo *, int);
169 static void InitBeltMovement(void);
170 static void CloseAllOpenTimegates(void);
171 static void CheckGravityMovement(struct PlayerInfo *);
172 static void KillHeroUnlessProtected(int, int);
174 static void TestIfPlayerTouchesCustomElement(int, int);
175 static void TestIfElementTouchesCustomElement(int, int);
177 static void ChangeElement(int, int, int);
178 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
179 static boolean CheckTriggeredElementChange(int, int, int, int);
180 static boolean CheckElementSideChange(int, int, int, int, int, int);
181 static boolean CheckElementChange(int, int, int, int);
183 static void PlaySoundLevel(int, int, int);
184 static void PlaySoundLevelNearest(int, int, int);
185 static void PlaySoundLevelAction(int, int, int);
186 static void PlaySoundLevelElementAction(int, int, int, int);
187 static void PlaySoundLevelActionIfLoop(int, int, int);
188 static void StopSoundLevelActionIfLoop(int, int, int);
190 static void MapGameButtons();
191 static void HandleGameButtons(struct GadgetInfo *);
193 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
196 /* ------------------------------------------------------------------------- */
197 /* definition of elements that automatically change to other elements after */
198 /* a specified time, eventually calling a function when changing */
199 /* ------------------------------------------------------------------------- */
201 /* forward declaration for changer functions */
202 static void InitBuggyBase(int x, int y);
203 static void WarnBuggyBase(int x, int y);
205 static void InitTrap(int x, int y);
206 static void ActivateTrap(int x, int y);
207 static void ChangeActiveTrap(int x, int y);
209 static void InitRobotWheel(int x, int y);
210 static void RunRobotWheel(int x, int y);
211 static void StopRobotWheel(int x, int y);
213 static void InitTimegateWheel(int x, int y);
214 static void RunTimegateWheel(int x, int y);
216 struct ChangingElementInfo
221 void (*pre_change_function)(int x, int y);
222 void (*change_function)(int x, int y);
223 void (*post_change_function)(int x, int y);
226 static struct ChangingElementInfo change_delay_list[] =
277 EL_SWITCHGATE_OPENING,
285 EL_SWITCHGATE_CLOSING,
286 EL_SWITCHGATE_CLOSED,
318 EL_ACID_SPLASH_RIGHT,
327 EL_SP_BUGGY_BASE_ACTIVATING,
334 EL_SP_BUGGY_BASE_ACTIVATING,
335 EL_SP_BUGGY_BASE_ACTIVE,
342 EL_SP_BUGGY_BASE_ACTIVE,
366 EL_ROBOT_WHEEL_ACTIVE,
374 EL_TIMEGATE_SWITCH_ACTIVE,
395 int push_delay_fixed, push_delay_random;
400 { EL_BALLOON, 0, 0 },
402 { EL_SOKOBAN_OBJECT, 2, 0 },
403 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
404 { EL_SATELLITE, 2, 0 },
405 { EL_SP_DISK_YELLOW, 2, 0 },
407 { EL_UNDEFINED, 0, 0 },
415 move_stepsize_list[] =
417 { EL_AMOEBA_DROP, 2 },
418 { EL_AMOEBA_DROPPING, 2 },
419 { EL_QUICKSAND_FILLING, 1 },
420 { EL_QUICKSAND_EMPTYING, 1 },
421 { EL_MAGIC_WALL_FILLING, 2 },
422 { EL_BD_MAGIC_WALL_FILLING, 2 },
423 { EL_MAGIC_WALL_EMPTYING, 2 },
424 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
434 collect_count_list[] =
437 { EL_BD_DIAMOND, 1 },
438 { EL_EMERALD_YELLOW, 1 },
439 { EL_EMERALD_RED, 1 },
440 { EL_EMERALD_PURPLE, 1 },
442 { EL_SP_INFOTRON, 1 },
449 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
451 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
452 CH_EVENT_BIT(CE_DELAY))
453 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
454 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
455 IS_JUST_CHANGING(x, y))
457 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
460 void GetPlayerConfig()
462 if (!audio.sound_available)
463 setup.sound_simple = FALSE;
465 if (!audio.loops_available)
466 setup.sound_loops = FALSE;
468 if (!audio.music_available)
469 setup.sound_music = FALSE;
471 if (!video.fullscreen_available)
472 setup.fullscreen = FALSE;
474 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
476 SetAudioMode(setup.sound);
480 static int getBeltNrFromBeltElement(int element)
482 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
483 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
484 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
487 static int getBeltNrFromBeltActiveElement(int element)
489 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
490 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
491 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
494 static int getBeltNrFromBeltSwitchElement(int element)
496 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
497 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
498 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
501 static int getBeltDirNrFromBeltSwitchElement(int element)
503 static int belt_base_element[4] =
505 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
506 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
507 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
508 EL_CONVEYOR_BELT_4_SWITCH_LEFT
511 int belt_nr = getBeltNrFromBeltSwitchElement(element);
512 int belt_dir_nr = element - belt_base_element[belt_nr];
514 return (belt_dir_nr % 3);
517 static int getBeltDirFromBeltSwitchElement(int element)
519 static int belt_move_dir[3] =
526 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
528 return belt_move_dir[belt_dir_nr];
531 static void InitPlayerField(int x, int y, int element, boolean init_game)
533 if (element == EL_SP_MURPHY)
537 if (stored_player[0].present)
539 Feld[x][y] = EL_SP_MURPHY_CLONE;
545 stored_player[0].use_murphy_graphic = TRUE;
548 Feld[x][y] = EL_PLAYER_1;
554 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
555 int jx = player->jx, jy = player->jy;
557 player->present = TRUE;
559 if (!options.network || player->connected)
561 player->active = TRUE;
563 /* remove potentially duplicate players */
564 if (StorePlayer[jx][jy] == Feld[x][y])
565 StorePlayer[jx][jy] = 0;
567 StorePlayer[x][y] = Feld[x][y];
571 printf("Player %d activated.\n", player->element_nr);
572 printf("[Local player is %d and currently %s.]\n",
573 local_player->element_nr,
574 local_player->active ? "active" : "not active");
578 Feld[x][y] = EL_EMPTY;
579 player->jx = player->last_jx = x;
580 player->jy = player->last_jy = y;
584 static void InitField(int x, int y, boolean init_game)
586 int element = Feld[x][y];
595 InitPlayerField(x, y, element, init_game);
599 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
600 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
601 else if (x > 0 && Feld[x-1][y] == EL_ACID)
602 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
603 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
604 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
605 else if (y > 0 && Feld[x][y-1] == EL_ACID)
606 Feld[x][y] = EL_ACID_POOL_BOTTOM;
607 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
608 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
616 case EL_SPACESHIP_RIGHT:
617 case EL_SPACESHIP_UP:
618 case EL_SPACESHIP_LEFT:
619 case EL_SPACESHIP_DOWN:
621 case EL_BD_BUTTERFLY_RIGHT:
622 case EL_BD_BUTTERFLY_UP:
623 case EL_BD_BUTTERFLY_LEFT:
624 case EL_BD_BUTTERFLY_DOWN:
625 case EL_BD_BUTTERFLY:
626 case EL_BD_FIREFLY_RIGHT:
627 case EL_BD_FIREFLY_UP:
628 case EL_BD_FIREFLY_LEFT:
629 case EL_BD_FIREFLY_DOWN:
631 case EL_PACMAN_RIGHT:
655 if (y == lev_fieldy - 1)
657 Feld[x][y] = EL_AMOEBA_GROWING;
658 Store[x][y] = EL_AMOEBA_WET;
662 case EL_DYNAMITE_ACTIVE:
667 local_player->lights_still_needed++;
670 case EL_SOKOBAN_FIELD_EMPTY:
671 local_player->sokobanfields_still_needed++;
675 local_player->friends_still_needed++;
680 MovDir[x][y] = 1 << RND(4);
685 Feld[x][y] = EL_EMPTY;
690 case EL_EM_KEY_1_FILE:
691 Feld[x][y] = EL_EM_KEY_1;
693 case EL_EM_KEY_2_FILE:
694 Feld[x][y] = EL_EM_KEY_2;
696 case EL_EM_KEY_3_FILE:
697 Feld[x][y] = EL_EM_KEY_3;
699 case EL_EM_KEY_4_FILE:
700 Feld[x][y] = EL_EM_KEY_4;
704 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
705 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
706 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
707 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
708 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
709 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
710 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
711 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
712 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
713 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
714 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
715 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
718 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
719 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
720 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
722 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
724 game.belt_dir[belt_nr] = belt_dir;
725 game.belt_dir_nr[belt_nr] = belt_dir_nr;
727 else /* more than one switch -- set it like the first switch */
729 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
734 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
736 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
739 case EL_LIGHT_SWITCH_ACTIVE:
741 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
745 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
751 void DrawGameDoorValues()
755 for (i=0; i<MAX_PLAYERS; i++)
757 if (stored_player[i].key[j])
758 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
759 el2edimg(EL_KEY_1 + j));
761 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
762 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
763 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
764 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
765 DrawText(DX + XX_SCORE, DY + YY_SCORE,
766 int2str(local_player->score, 5), FONT_TEXT_2);
767 DrawText(DX + XX_TIME, DY + YY_TIME,
768 int2str(TimeLeft, 3), FONT_TEXT_2);
773 =============================================================================
775 -----------------------------------------------------------------------------
776 initialize game engine due to level / tape version number
777 =============================================================================
780 static void InitGameEngine()
784 /* set game engine from tape file when re-playing, else from level file */
785 game.engine_version = (tape.playing ? tape.engine_version :
788 /* dynamically adjust element properties according to game engine version */
789 InitElementPropertiesEngine(game.engine_version);
792 printf("level %d: level version == %06d\n", level_nr, level.game_version);
793 printf(" tape version == %06d [%s] [file: %06d]\n",
794 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
796 printf(" => game.engine_version == %06d\n", game.engine_version);
799 /* ---------- initialize player's initial move delay --------------------- */
801 /* dynamically adjust player properties according to game engine version */
802 game.initial_move_delay =
803 (game.engine_version <= VERSION_IDENT(2,0,1) ? INITIAL_MOVE_DELAY_ON :
804 INITIAL_MOVE_DELAY_OFF);
806 /* dynamically adjust player properties according to level information */
807 game.initial_move_delay_value =
808 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
810 /* ---------- initialize changing elements ------------------------------- */
812 /* initialize changing elements information */
813 for (i=0; i < MAX_NUM_ELEMENTS; i++)
815 struct ElementInfo *ei = &element_info[i];
817 /* this pointer might have been changed in the level editor */
818 ei->change = &ei->change_page[0];
820 if (!IS_CUSTOM_ELEMENT(i))
822 ei->change->target_element = EL_EMPTY_SPACE;
823 ei->change->delay_fixed = 0;
824 ei->change->delay_random = 0;
825 ei->change->delay_frames = 1;
828 ei->change_events = CE_BITMASK_DEFAULT;
829 for (j=0; j < NUM_CHANGE_EVENTS; j++)
831 ei->event_page_nr[j] = 0;
832 ei->event_page[j] = &ei->change_page[0];
836 /* add changing elements from pre-defined list */
837 for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
839 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
840 struct ElementInfo *ei = &element_info[ch_delay->element];
842 ei->change->target_element = ch_delay->target_element;
843 ei->change->delay_fixed = ch_delay->change_delay;
845 ei->change->pre_change_function = ch_delay->pre_change_function;
846 ei->change->change_function = ch_delay->change_function;
847 ei->change->post_change_function = ch_delay->post_change_function;
849 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
853 /* add change events from custom element configuration */
854 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
856 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
858 for (j=0; j < ei->num_change_pages; j++)
860 if (!ei->change_page[j].can_change)
863 for (k=0; k < NUM_CHANGE_EVENTS; k++)
865 /* only add event page for the first page found with this event */
866 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
867 !(ei->change_events & CH_EVENT_BIT(k)))
869 ei->change_events |= CH_EVENT_BIT(k);
870 ei->event_page_nr[k] = j;
871 ei->event_page[k] = &ei->change_page[j];
879 /* add change events from custom element configuration */
880 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
882 int element = EL_CUSTOM_START + i;
884 /* only add custom elements that change after fixed/random frame delay */
885 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
886 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
890 /* ---------- initialize trigger events ---------------------------------- */
892 /* initialize trigger events information */
893 for (i=0; i<MAX_NUM_ELEMENTS; i++)
894 trigger_events[i] = EP_BITMASK_DEFAULT;
897 /* add trigger events from element change event properties */
898 for (i=0; i<MAX_NUM_ELEMENTS; i++)
900 struct ElementInfo *ei = &element_info[i];
902 for (j=0; j < ei->num_change_pages; j++)
904 if (!ei->change_page[j].can_change)
907 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
909 int trigger_element = ei->change_page[j].trigger_element;
911 trigger_events[trigger_element] |= ei->change_page[j].events;
916 /* add trigger events from element change event properties */
917 for (i=0; i<MAX_NUM_ELEMENTS; i++)
918 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
919 trigger_events[element_info[i].change->trigger_element] |=
920 element_info[i].change->events;
923 /* ---------- initialize push delay -------------------------------------- */
925 /* initialize push delay values to default */
926 for (i=0; i<MAX_NUM_ELEMENTS; i++)
928 if (!IS_CUSTOM_ELEMENT(i))
930 element_info[i].push_delay_fixed =
931 (game.engine_version < VERSION_IDENT(3,0,7) ? 2 : 8);
932 element_info[i].push_delay_random = 8;
936 /* set push delay value for certain elements from pre-defined list */
937 for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
939 int e = push_delay_list[i].element;
941 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
942 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
945 /* ---------- initialize move stepsize ----------------------------------- */
947 /* initialize move stepsize values to default */
948 for (i=0; i<MAX_NUM_ELEMENTS; i++)
949 if (!IS_CUSTOM_ELEMENT(i))
950 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
952 /* set move stepsize value for certain elements from pre-defined list */
953 for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
955 int e = move_stepsize_list[i].element;
957 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
960 /* ---------- initialize gem count --------------------------------------- */
962 /* initialize gem count values for each element */
963 for (i=0; i<MAX_NUM_ELEMENTS; i++)
964 if (!IS_CUSTOM_ELEMENT(i))
965 element_info[i].collect_count = 0;
967 /* add gem count values for all elements from pre-defined list */
968 for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
969 element_info[collect_count_list[i].element].collect_count =
970 collect_count_list[i].count;
975 =============================================================================
977 -----------------------------------------------------------------------------
978 initialize and start new game
979 =============================================================================
984 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
985 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
986 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
993 #if USE_NEW_AMOEBA_CODE
994 printf("Using new amoeba code.\n");
996 printf("Using old amoeba code.\n");
1001 /* don't play tapes over network */
1002 network_playing = (options.network && !tape.playing);
1004 for (i=0; i<MAX_PLAYERS; i++)
1006 struct PlayerInfo *player = &stored_player[i];
1008 player->index_nr = i;
1009 player->element_nr = EL_PLAYER_1 + i;
1011 player->present = FALSE;
1012 player->active = FALSE;
1015 player->effective_action = 0;
1016 player->programmed_action = 0;
1019 player->gems_still_needed = level.gems_needed;
1020 player->sokobanfields_still_needed = 0;
1021 player->lights_still_needed = 0;
1022 player->friends_still_needed = 0;
1025 player->key[j] = FALSE;
1027 player->dynabomb_count = 0;
1028 player->dynabomb_size = 1;
1029 player->dynabombs_left = 0;
1030 player->dynabomb_xl = FALSE;
1032 player->MovDir = MV_NO_MOVING;
1035 player->GfxDir = MV_NO_MOVING;
1036 player->GfxAction = ACTION_DEFAULT;
1038 player->StepFrame = 0;
1040 player->use_murphy_graphic = FALSE;
1042 player->actual_frame_counter = 0;
1044 player->last_move_dir = MV_NO_MOVING;
1046 player->is_waiting = FALSE;
1047 player->is_moving = FALSE;
1048 player->is_digging = FALSE;
1049 player->is_snapping = FALSE;
1050 player->is_collecting = FALSE;
1051 player->is_pushing = FALSE;
1052 player->is_switching = FALSE;
1054 player->switch_x = -1;
1055 player->switch_y = -1;
1057 player->show_envelope = 0;
1059 player->move_delay = game.initial_move_delay;
1060 player->move_delay_value = game.initial_move_delay_value;
1062 player->push_delay = 0;
1063 player->push_delay_value = 5;
1065 player->last_jx = player->last_jy = 0;
1066 player->jx = player->jy = 0;
1068 player->shield_normal_time_left = 0;
1069 player->shield_deadly_time_left = 0;
1071 player->inventory_size = 0;
1073 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1074 SnapField(player, 0, 0);
1076 player->LevelSolved = FALSE;
1077 player->GameOver = FALSE;
1080 network_player_action_received = FALSE;
1082 #if defined(PLATFORM_UNIX)
1083 /* initial null action */
1084 if (network_playing)
1085 SendToServer_MovePlayer(MV_NO_MOVING);
1093 TimeLeft = level.time;
1095 ScreenMovDir = MV_NO_MOVING;
1099 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1101 AllPlayersGone = FALSE;
1103 game.yamyam_content_nr = 0;
1104 game.magic_wall_active = FALSE;
1105 game.magic_wall_time_left = 0;
1106 game.light_time_left = 0;
1107 game.timegate_time_left = 0;
1108 game.switchgate_pos = 0;
1109 game.balloon_dir = MV_NO_MOVING;
1110 game.gravity = level.initial_gravity;
1111 game.explosions_delayed = TRUE;
1113 game.envelope_active = FALSE;
1117 game.belt_dir[i] = MV_NO_MOVING;
1118 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1121 for (i=0; i<MAX_NUM_AMOEBA; i++)
1122 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1124 for (x=0; x<lev_fieldx; x++)
1126 for (y=0; y<lev_fieldy; y++)
1128 Feld[x][y] = level.field[x][y];
1129 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1130 ChangeDelay[x][y] = 0;
1131 ChangePage[x][y] = -1;
1132 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1134 WasJustMoving[x][y] = 0;
1135 WasJustFalling[x][y] = 0;
1137 Pushed[x][y] = FALSE;
1139 Changed[x][y] = CE_BITMASK_DEFAULT;
1140 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1142 ExplodePhase[x][y] = 0;
1143 ExplodeField[x][y] = EX_NO_EXPLOSION;
1146 GfxAction[x][y] = ACTION_DEFAULT;
1147 GfxRandom[x][y] = INIT_GFX_RANDOM();
1148 GfxElement[x][y] = EL_UNDEFINED;
1152 for(y=0; y<lev_fieldy; y++)
1154 for(x=0; x<lev_fieldx; x++)
1156 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1158 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1160 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1163 InitField(x, y, TRUE);
1169 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1170 emulate_sb ? EMU_SOKOBAN :
1171 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1173 /* correct non-moving belts to start moving left */
1175 if (game.belt_dir[i] == MV_NO_MOVING)
1176 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1178 /* check if any connected player was not found in playfield */
1179 for (i=0; i<MAX_PLAYERS; i++)
1181 struct PlayerInfo *player = &stored_player[i];
1183 if (player->connected && !player->present)
1185 for (j=0; j<MAX_PLAYERS; j++)
1187 struct PlayerInfo *some_player = &stored_player[j];
1188 int jx = some_player->jx, jy = some_player->jy;
1190 /* assign first free player found that is present in the playfield */
1191 if (some_player->present && !some_player->connected)
1193 player->present = TRUE;
1194 player->active = TRUE;
1195 some_player->present = FALSE;
1197 StorePlayer[jx][jy] = player->element_nr;
1198 player->jx = player->last_jx = jx;
1199 player->jy = player->last_jy = jy;
1209 /* when playing a tape, eliminate all players who do not participate */
1211 for (i=0; i<MAX_PLAYERS; i++)
1213 if (stored_player[i].active && !tape.player_participates[i])
1215 struct PlayerInfo *player = &stored_player[i];
1216 int jx = player->jx, jy = player->jy;
1218 player->active = FALSE;
1219 StorePlayer[jx][jy] = 0;
1220 Feld[jx][jy] = EL_EMPTY;
1224 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1226 /* when in single player mode, eliminate all but the first active player */
1228 for (i=0; i<MAX_PLAYERS; i++)
1230 if (stored_player[i].active)
1232 for (j=i+1; j<MAX_PLAYERS; j++)
1234 if (stored_player[j].active)
1236 struct PlayerInfo *player = &stored_player[j];
1237 int jx = player->jx, jy = player->jy;
1239 player->active = FALSE;
1240 StorePlayer[jx][jy] = 0;
1241 Feld[jx][jy] = EL_EMPTY;
1248 /* when recording the game, store which players take part in the game */
1251 for (i=0; i<MAX_PLAYERS; i++)
1252 if (stored_player[i].active)
1253 tape.player_participates[i] = TRUE;
1258 for (i=0; i<MAX_PLAYERS; i++)
1260 struct PlayerInfo *player = &stored_player[i];
1262 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1267 if (local_player == player)
1268 printf("Player %d is local player.\n", i+1);
1272 if (BorderElement == EL_EMPTY)
1275 SBX_Right = lev_fieldx - SCR_FIELDX;
1277 SBY_Lower = lev_fieldy - SCR_FIELDY;
1282 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1284 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1287 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1288 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1290 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1291 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1293 /* if local player not found, look for custom element that might create
1294 the player (make some assumptions about the right custom element) */
1295 if (!local_player->present)
1297 int start_x = 0, start_y = 0;
1298 int found_rating = 0;
1299 int found_element = EL_UNDEFINED;
1301 for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1303 int element = Feld[x][y];
1308 if (!IS_CUSTOM_ELEMENT(element))
1311 if (CAN_CHANGE(element))
1313 for (i=0; i < element_info[element].num_change_pages; i++)
1315 content = element_info[element].change_page[i].target_element;
1316 is_player = ELEM_IS_PLAYER(content);
1318 if (is_player && (found_rating < 3 || element < found_element))
1324 found_element = element;
1329 for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1331 content = element_info[element].content[xx][yy];
1332 is_player = ELEM_IS_PLAYER(content);
1334 if (is_player && (found_rating < 2 || element < found_element))
1336 start_x = x + xx - 1;
1337 start_y = y + yy - 1;
1340 found_element = element;
1343 if (!CAN_CHANGE(element))
1346 for (i=0; i < element_info[element].num_change_pages; i++)
1348 content = element_info[element].change_page[i].content[xx][yy];
1349 is_player = ELEM_IS_PLAYER(content);
1351 if (is_player && (found_rating < 1 || element < found_element))
1353 start_x = x + xx - 1;
1354 start_y = y + yy - 1;
1357 found_element = element;
1363 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1364 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1367 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1368 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1374 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1375 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1376 local_player->jx - MIDPOSX);
1378 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1379 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1380 local_player->jy - MIDPOSY);
1382 scroll_x = SBX_Left;
1383 scroll_y = SBY_Upper;
1384 if (local_player->jx >= SBX_Left + MIDPOSX)
1385 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1386 local_player->jx - MIDPOSX :
1388 if (local_player->jy >= SBY_Upper + MIDPOSY)
1389 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1390 local_player->jy - MIDPOSY :
1395 CloseDoor(DOOR_CLOSE_1);
1400 /* after drawing the level, correct some elements */
1401 if (game.timegate_time_left == 0)
1402 CloseAllOpenTimegates();
1404 if (setup.soft_scrolling)
1405 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1407 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1410 /* copy default game door content to main double buffer */
1411 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1412 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1415 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1418 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1419 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1420 BlitBitmap(drawto, drawto,
1421 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1422 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1423 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1424 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1427 DrawGameDoorValues();
1431 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1432 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1433 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1437 /* copy actual game door content to door double buffer for OpenDoor() */
1438 BlitBitmap(drawto, bitmap_db_door,
1439 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1441 OpenDoor(DOOR_OPEN_ALL);
1443 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1444 if (setup.sound_music)
1445 PlayMusic(level_nr);
1447 KeyboardAutoRepeatOffUnlessAutoplay();
1452 printf("Player %d %sactive.\n",
1453 i + 1, (stored_player[i].active ? "" : "not "));
1457 void InitMovDir(int x, int y)
1459 int i, element = Feld[x][y];
1460 static int xy[4][2] =
1467 static int direction[3][4] =
1469 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1470 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1471 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1480 Feld[x][y] = EL_BUG;
1481 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1484 case EL_SPACESHIP_RIGHT:
1485 case EL_SPACESHIP_UP:
1486 case EL_SPACESHIP_LEFT:
1487 case EL_SPACESHIP_DOWN:
1488 Feld[x][y] = EL_SPACESHIP;
1489 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1492 case EL_BD_BUTTERFLY_RIGHT:
1493 case EL_BD_BUTTERFLY_UP:
1494 case EL_BD_BUTTERFLY_LEFT:
1495 case EL_BD_BUTTERFLY_DOWN:
1496 Feld[x][y] = EL_BD_BUTTERFLY;
1497 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1500 case EL_BD_FIREFLY_RIGHT:
1501 case EL_BD_FIREFLY_UP:
1502 case EL_BD_FIREFLY_LEFT:
1503 case EL_BD_FIREFLY_DOWN:
1504 Feld[x][y] = EL_BD_FIREFLY;
1505 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1508 case EL_PACMAN_RIGHT:
1510 case EL_PACMAN_LEFT:
1511 case EL_PACMAN_DOWN:
1512 Feld[x][y] = EL_PACMAN;
1513 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1516 case EL_SP_SNIKSNAK:
1517 MovDir[x][y] = MV_UP;
1520 case EL_SP_ELECTRON:
1521 MovDir[x][y] = MV_LEFT;
1528 Feld[x][y] = EL_MOLE;
1529 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1533 if (IS_CUSTOM_ELEMENT(element))
1535 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1536 MovDir[x][y] = element_info[element].move_direction_initial;
1537 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1538 element_info[element].move_pattern == MV_TURNING_LEFT ||
1539 element_info[element].move_pattern == MV_TURNING_RIGHT)
1540 MovDir[x][y] = 1 << RND(4);
1541 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1542 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1543 else if (element_info[element].move_pattern == MV_VERTICAL)
1544 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1545 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1546 MovDir[x][y] = element_info[element].move_pattern;
1547 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1548 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1552 int x1 = x + xy[i][0];
1553 int y1 = y + xy[i][1];
1555 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1557 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1558 MovDir[x][y] = direction[0][i];
1560 MovDir[x][y] = direction[1][i];
1569 MovDir[x][y] = 1 << RND(4);
1571 if (element != EL_BUG &&
1572 element != EL_SPACESHIP &&
1573 element != EL_BD_BUTTERFLY &&
1574 element != EL_BD_FIREFLY)
1579 int x1 = x + xy[i][0];
1580 int y1 = y + xy[i][1];
1582 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1584 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1586 MovDir[x][y] = direction[0][i];
1589 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1590 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1592 MovDir[x][y] = direction[1][i];
1602 void InitAmoebaNr(int x, int y)
1605 int group_nr = AmoebeNachbarNr(x, y);
1609 for (i=1; i<MAX_NUM_AMOEBA; i++)
1611 if (AmoebaCnt[i] == 0)
1619 AmoebaNr[x][y] = group_nr;
1620 AmoebaCnt[group_nr]++;
1621 AmoebaCnt2[group_nr]++;
1627 boolean raise_level = FALSE;
1629 if (local_player->MovPos)
1633 if (tape.auto_play) /* tape might already be stopped here */
1634 tape.auto_play_level_solved = TRUE;
1636 if (tape.playing && tape.auto_play)
1637 tape.auto_play_level_solved = TRUE;
1640 local_player->LevelSolved = FALSE;
1642 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1646 if (!tape.playing && setup.sound_loops)
1647 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1648 SND_CTRL_PLAY_LOOP);
1650 while (TimeLeft > 0)
1652 if (!tape.playing && !setup.sound_loops)
1653 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1654 if (TimeLeft > 0 && !(TimeLeft % 10))
1655 RaiseScore(level.score[SC_TIME_BONUS]);
1656 if (TimeLeft > 100 && !(TimeLeft % 10))
1660 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1667 if (!tape.playing && setup.sound_loops)
1668 StopSound(SND_GAME_LEVELTIME_BONUS);
1670 else if (level.time == 0) /* level without time limit */
1672 if (!tape.playing && setup.sound_loops)
1673 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1674 SND_CTRL_PLAY_LOOP);
1676 while (TimePlayed < 999)
1678 if (!tape.playing && !setup.sound_loops)
1679 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1680 if (TimePlayed < 999 && !(TimePlayed % 10))
1681 RaiseScore(level.score[SC_TIME_BONUS]);
1682 if (TimePlayed < 900 && !(TimePlayed % 10))
1686 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1693 if (!tape.playing && setup.sound_loops)
1694 StopSound(SND_GAME_LEVELTIME_BONUS);
1697 /* close exit door after last player */
1698 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1699 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1701 int element = Feld[ExitX][ExitY];
1703 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1704 EL_SP_EXIT_CLOSING);
1706 PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1709 /* Hero disappears */
1710 DrawLevelField(ExitX, ExitY);
1716 CloseDoor(DOOR_CLOSE_1);
1721 SaveTape(tape.level_nr); /* Ask to save tape */
1724 if (level_nr == leveldir_current->handicap_level)
1726 leveldir_current->handicap_level++;
1727 SaveLevelSetup_SeriesInfo();
1730 if (level_editor_test_game)
1731 local_player->score = -1; /* no highscore when playing from editor */
1732 else if (level_nr < leveldir_current->last_level)
1733 raise_level = TRUE; /* advance to next level */
1735 if ((hi_pos = NewHiScore()) >= 0)
1737 game_status = GAME_MODE_SCORES;
1738 DrawHallOfFame(hi_pos);
1747 game_status = GAME_MODE_MAIN;
1764 LoadScore(level_nr);
1766 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1767 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1770 for (k=0; k<MAX_SCORE_ENTRIES; k++)
1772 if (local_player->score > highscore[k].Score)
1774 /* player has made it to the hall of fame */
1776 if (k < MAX_SCORE_ENTRIES - 1)
1778 int m = MAX_SCORE_ENTRIES - 1;
1781 for (l=k; l<MAX_SCORE_ENTRIES; l++)
1782 if (!strcmp(setup.player_name, highscore[l].Name))
1784 if (m == k) /* player's new highscore overwrites his old one */
1790 strcpy(highscore[l].Name, highscore[l - 1].Name);
1791 highscore[l].Score = highscore[l - 1].Score;
1798 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1799 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1800 highscore[k].Score = local_player->score;
1806 else if (!strncmp(setup.player_name, highscore[k].Name,
1807 MAX_PLAYER_NAME_LEN))
1808 break; /* player already there with a higher score */
1814 SaveScore(level_nr);
1819 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1821 if (player->GfxAction != action || player->GfxDir != dir)
1824 printf("Player frame reset! (%d => %d, %d => %d)\n",
1825 player->GfxAction, action, player->GfxDir, dir);
1828 player->GfxAction = action;
1829 player->GfxDir = dir;
1831 player->StepFrame = 0;
1835 static void ResetRandomAnimationValue(int x, int y)
1837 GfxRandom[x][y] = INIT_GFX_RANDOM();
1840 static void ResetGfxAnimation(int x, int y)
1843 GfxAction[x][y] = ACTION_DEFAULT;
1846 void InitMovingField(int x, int y, int direction)
1848 int element = Feld[x][y];
1849 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1850 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1854 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1855 ResetGfxAnimation(x, y);
1857 MovDir[newx][newy] = MovDir[x][y] = direction;
1859 if (Feld[newx][newy] == EL_EMPTY)
1860 Feld[newx][newy] = EL_BLOCKED;
1862 if (direction == MV_DOWN && CAN_FALL(element))
1863 GfxAction[x][y] = ACTION_FALLING;
1865 GfxAction[x][y] = ACTION_MOVING;
1867 GfxFrame[newx][newy] = GfxFrame[x][y];
1868 GfxAction[newx][newy] = GfxAction[x][y];
1869 GfxRandom[newx][newy] = GfxRandom[x][y];
1872 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1874 int direction = MovDir[x][y];
1875 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1876 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
1882 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1884 int oldx = x, oldy = y;
1885 int direction = MovDir[x][y];
1887 if (direction == MV_LEFT)
1889 else if (direction == MV_RIGHT)
1891 else if (direction == MV_UP)
1893 else if (direction == MV_DOWN)
1896 *comes_from_x = oldx;
1897 *comes_from_y = oldy;
1900 int MovingOrBlocked2Element(int x, int y)
1902 int element = Feld[x][y];
1904 if (element == EL_BLOCKED)
1908 Blocked2Moving(x, y, &oldx, &oldy);
1909 return Feld[oldx][oldy];
1915 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1917 /* like MovingOrBlocked2Element(), but if element is moving
1918 and (x,y) is the field the moving element is just leaving,
1919 return EL_BLOCKED instead of the element value */
1920 int element = Feld[x][y];
1922 if (IS_MOVING(x, y))
1924 if (element == EL_BLOCKED)
1928 Blocked2Moving(x, y, &oldx, &oldy);
1929 return Feld[oldx][oldy];
1938 static void RemoveField(int x, int y)
1940 Feld[x][y] = EL_EMPTY;
1947 ChangeDelay[x][y] = 0;
1948 ChangePage[x][y] = -1;
1949 Pushed[x][y] = FALSE;
1951 GfxElement[x][y] = EL_UNDEFINED;
1952 GfxAction[x][y] = ACTION_DEFAULT;
1955 void RemoveMovingField(int x, int y)
1957 int oldx = x, oldy = y, newx = x, newy = y;
1958 int element = Feld[x][y];
1959 int next_element = EL_UNDEFINED;
1961 if (element != EL_BLOCKED && !IS_MOVING(x, y))
1964 if (IS_MOVING(x, y))
1966 Moving2Blocked(x, y, &newx, &newy);
1967 if (Feld[newx][newy] != EL_BLOCKED)
1970 else if (element == EL_BLOCKED)
1972 Blocked2Moving(x, y, &oldx, &oldy);
1973 if (!IS_MOVING(oldx, oldy))
1977 if (element == EL_BLOCKED &&
1978 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1979 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1980 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1981 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
1982 next_element = get_next_element(Feld[oldx][oldy]);
1984 RemoveField(oldx, oldy);
1985 RemoveField(newx, newy);
1987 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1989 if (next_element != EL_UNDEFINED)
1990 Feld[oldx][oldy] = next_element;
1992 DrawLevelField(oldx, oldy);
1993 DrawLevelField(newx, newy);
1996 void DrawDynamite(int x, int y)
1998 int sx = SCREENX(x), sy = SCREENY(y);
1999 int graphic = el2img(Feld[x][y]);
2002 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2005 if (IS_WALKABLE_INSIDE(Back[x][y]))
2009 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2010 else if (Store[x][y])
2011 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2013 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2016 if (Back[x][y] || Store[x][y])
2017 DrawGraphicThruMask(sx, sy, graphic, frame);
2019 DrawGraphic(sx, sy, graphic, frame);
2021 if (game.emulation == EMU_SUPAPLEX)
2022 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2023 else if (Store[x][y])
2024 DrawGraphicThruMask(sx, sy, graphic, frame);
2026 DrawGraphic(sx, sy, graphic, frame);
2030 void CheckDynamite(int x, int y)
2032 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2036 if (MovDelay[x][y] != 0)
2039 PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2046 StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2048 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2049 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2050 StopSound(SND_DYNAMITE_ACTIVE);
2052 StopSound(SND_DYNABOMB_ACTIVE);
2058 void RelocatePlayer(int x, int y, int element)
2060 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2063 RemoveField(x, y); /* temporarily remove newly placed player */
2064 DrawLevelField(x, y);
2067 if (player->present)
2069 while (player->MovPos)
2071 ScrollPlayer(player, SCROLL_GO_ON);
2072 ScrollScreen(NULL, SCROLL_GO_ON);
2078 Delay(GAME_FRAME_DELAY);
2081 DrawPlayer(player); /* needed here only to cleanup last field */
2082 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2084 player->is_moving = FALSE;
2087 Feld[x][y] = element;
2088 InitPlayerField(x, y, element, TRUE);
2090 if (player == local_player)
2092 int scroll_xx = -999, scroll_yy = -999;
2094 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2097 int fx = FX, fy = FY;
2099 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2100 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2101 local_player->jx - MIDPOSX);
2103 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2104 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2105 local_player->jy - MIDPOSY);
2107 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2108 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2113 fx += dx * TILEX / 2;
2114 fy += dy * TILEY / 2;
2116 ScrollLevel(dx, dy);
2119 /* scroll in two steps of half tile size to make things smoother */
2120 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2122 Delay(GAME_FRAME_DELAY);
2124 /* scroll second step to align at full tile size */
2126 Delay(GAME_FRAME_DELAY);
2131 void Explode(int ex, int ey, int phase, int mode)
2135 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2136 int last_phase = num_phase * delay;
2137 int half_phase = (num_phase / 2) * delay;
2138 int first_phase_after_start = EX_PHASE_START + 1;
2140 if (game.explosions_delayed)
2142 ExplodeField[ex][ey] = mode;
2146 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2148 int center_element = Feld[ex][ey];
2151 /* --- This is only really needed (and now handled) in "Impact()". --- */
2152 /* do not explode moving elements that left the explode field in time */
2153 if (game.engine_version >= RELEASE_IDENT(2,2,0,7) &&
2154 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2158 if (mode == EX_NORMAL || mode == EX_CENTER)
2159 PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2161 /* remove things displayed in background while burning dynamite */
2162 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2165 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2167 /* put moving element to center field (and let it explode there) */
2168 center_element = MovingOrBlocked2Element(ex, ey);
2169 RemoveMovingField(ex, ey);
2170 Feld[ex][ey] = center_element;
2173 for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2175 int xx = x - ex + 1;
2176 int yy = y - ey + 1;
2179 if (!IN_LEV_FIELD(x, y) ||
2180 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2181 (x != ex || y != ey)))
2184 element = Feld[x][y];
2186 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2188 element = MovingOrBlocked2Element(x, y);
2190 if (!IS_EXPLOSION_PROOF(element))
2191 RemoveMovingField(x, y);
2197 if (IS_EXPLOSION_PROOF(element))
2200 /* indestructible elements can only explode in center (but not flames) */
2201 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2202 element == EL_FLAMES)
2207 if ((IS_INDESTRUCTIBLE(element) &&
2208 (game.engine_version < VERSION_IDENT(2,2,0) ||
2209 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2210 element == EL_FLAMES)
2214 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2216 if (IS_ACTIVE_BOMB(element))
2218 /* re-activate things under the bomb like gate or penguin */
2219 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2226 /* save walkable background elements while explosion on same tile */
2228 if (IS_INDESTRUCTIBLE(element))
2229 Back[x][y] = element;
2231 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2232 Back[x][y] = element;
2235 /* ignite explodable elements reached by other explosion */
2236 if (element == EL_EXPLOSION)
2237 element = Store2[x][y];
2240 if (AmoebaNr[x][y] &&
2241 (element == EL_AMOEBA_FULL ||
2242 element == EL_BD_AMOEBA ||
2243 element == EL_AMOEBA_GROWING))
2245 AmoebaCnt[AmoebaNr[x][y]]--;
2246 AmoebaCnt2[AmoebaNr[x][y]]--;
2252 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2254 switch(StorePlayer[ex][ey])
2257 Store[x][y] = EL_EMERALD_RED;
2260 Store[x][y] = EL_EMERALD;
2263 Store[x][y] = EL_EMERALD_PURPLE;
2267 Store[x][y] = EL_EMERALD_YELLOW;
2271 if (game.emulation == EMU_SUPAPLEX)
2272 Store[x][y] = EL_EMPTY;
2274 else if (center_element == EL_MOLE)
2275 Store[x][y] = EL_EMERALD_RED;
2276 else if (center_element == EL_PENGUIN)
2277 Store[x][y] = EL_EMERALD_PURPLE;
2278 else if (center_element == EL_BUG)
2279 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2280 else if (center_element == EL_BD_BUTTERFLY)
2281 Store[x][y] = EL_BD_DIAMOND;
2282 else if (center_element == EL_SP_ELECTRON)
2283 Store[x][y] = EL_SP_INFOTRON;
2284 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2285 Store[x][y] = level.amoeba_content;
2286 else if (center_element == EL_YAMYAM)
2287 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2288 else if (IS_CUSTOM_ELEMENT(center_element) &&
2289 element_info[center_element].content[xx][yy] != EL_EMPTY)
2290 Store[x][y] = element_info[center_element].content[xx][yy];
2291 else if (element == EL_WALL_EMERALD)
2292 Store[x][y] = EL_EMERALD;
2293 else if (element == EL_WALL_DIAMOND)
2294 Store[x][y] = EL_DIAMOND;
2295 else if (element == EL_WALL_BD_DIAMOND)
2296 Store[x][y] = EL_BD_DIAMOND;
2297 else if (element == EL_WALL_EMERALD_YELLOW)
2298 Store[x][y] = EL_EMERALD_YELLOW;
2299 else if (element == EL_WALL_EMERALD_RED)
2300 Store[x][y] = EL_EMERALD_RED;
2301 else if (element == EL_WALL_EMERALD_PURPLE)
2302 Store[x][y] = EL_EMERALD_PURPLE;
2303 else if (element == EL_WALL_PEARL)
2304 Store[x][y] = EL_PEARL;
2305 else if (element == EL_WALL_CRYSTAL)
2306 Store[x][y] = EL_CRYSTAL;
2307 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2308 Store[x][y] = element_info[element].content[1][1];
2310 Store[x][y] = EL_EMPTY;
2312 if (x != ex || y != ey ||
2313 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2314 Store2[x][y] = element;
2317 if (AmoebaNr[x][y] &&
2318 (element == EL_AMOEBA_FULL ||
2319 element == EL_BD_AMOEBA ||
2320 element == EL_AMOEBA_GROWING))
2322 AmoebaCnt[AmoebaNr[x][y]]--;
2323 AmoebaCnt2[AmoebaNr[x][y]]--;
2329 MovDir[x][y] = MovPos[x][y] = 0;
2334 Feld[x][y] = EL_EXPLOSION;
2336 GfxElement[x][y] = center_element;
2338 GfxElement[x][y] = EL_UNDEFINED;
2341 ExplodePhase[x][y] = 1;
2345 if (center_element == EL_YAMYAM)
2346 game.yamyam_content_nr =
2347 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2358 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2362 /* activate this even in non-DEBUG version until cause for crash in
2363 getGraphicAnimationFrame() (see below) is found and eliminated */
2367 if (GfxElement[x][y] == EL_UNDEFINED)
2370 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2371 printf("Explode(): This should never happen!\n");
2374 GfxElement[x][y] = EL_EMPTY;
2378 if (phase == first_phase_after_start)
2380 int element = Store2[x][y];
2382 if (element == EL_BLACK_ORB)
2384 Feld[x][y] = Store2[x][y];
2389 else if (phase == half_phase)
2391 int element = Store2[x][y];
2393 if (IS_PLAYER(x, y))
2394 KillHeroUnlessProtected(x, y);
2395 else if (CAN_EXPLODE_BY_FIRE(element))
2397 Feld[x][y] = Store2[x][y];
2401 else if (element == EL_AMOEBA_TO_DIAMOND)
2402 AmoebeUmwandeln(x, y);
2405 if (phase == last_phase)
2409 element = Feld[x][y] = Store[x][y];
2410 Store[x][y] = Store2[x][y] = 0;
2411 GfxElement[x][y] = EL_UNDEFINED;
2413 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2414 element = Feld[x][y] = Back[x][y];
2417 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2418 ChangeDelay[x][y] = 0;
2419 ChangePage[x][y] = -1;
2421 InitField(x, y, FALSE);
2422 if (CAN_MOVE(element))
2424 DrawLevelField(x, y);
2426 TestIfElementTouchesCustomElement(x, y);
2428 if (GFX_CRUMBLED(element))
2429 DrawLevelFieldCrumbledSandNeighbours(x, y);
2431 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2432 StorePlayer[x][y] = 0;
2434 if (ELEM_IS_PLAYER(element))
2435 RelocatePlayer(x, y, element);
2437 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2440 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2442 int stored = Store[x][y];
2443 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2444 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2447 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2450 DrawLevelFieldCrumbledSand(x, y);
2452 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2454 DrawLevelElement(x, y, Back[x][y]);
2455 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2457 else if (IS_WALKABLE_UNDER(Back[x][y]))
2459 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2460 DrawLevelElementThruMask(x, y, Back[x][y]);
2462 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2463 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2467 void DynaExplode(int ex, int ey)
2470 int dynabomb_size = 1;
2471 boolean dynabomb_xl = FALSE;
2472 struct PlayerInfo *player;
2473 static int xy[4][2] =
2481 if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2483 player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2484 dynabomb_size = player->dynabomb_size;
2485 dynabomb_xl = player->dynabomb_xl;
2486 player->dynabombs_left++;
2489 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2493 for (j=1; j<=dynabomb_size; j++)
2495 int x = ex + j * xy[i % 4][0];
2496 int y = ey + j * xy[i % 4][1];
2499 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2502 element = Feld[x][y];
2504 /* do not restart explosions of fields with active bombs */
2505 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2508 Explode(x, y, EX_PHASE_START, EX_BORDER);
2510 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2511 if (element != EL_EMPTY &&
2512 element != EL_SAND &&
2513 element != EL_EXPLOSION &&
2520 void Bang(int x, int y)
2523 int element = MovingOrBlocked2Element(x, y);
2525 int element = Feld[x][y];
2529 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2531 if (IS_PLAYER(x, y))
2534 struct PlayerInfo *player = PLAYERINFO(x, y);
2536 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2537 player->element_nr);
2542 PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2544 if (game.emulation == EMU_SUPAPLEX)
2545 PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2547 PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2552 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2560 case EL_BD_BUTTERFLY:
2563 case EL_DARK_YAMYAM:
2567 RaiseScoreElement(element);
2568 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2570 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2571 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2572 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2573 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2574 case EL_DYNABOMB_INCREASE_NUMBER:
2575 case EL_DYNABOMB_INCREASE_SIZE:
2576 case EL_DYNABOMB_INCREASE_POWER:
2581 case EL_LAMP_ACTIVE:
2582 if (IS_PLAYER(x, y))
2583 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2585 Explode(x, y, EX_PHASE_START, EX_CENTER);
2588 if (CAN_EXPLODE_1X1(element))
2589 Explode(x, y, EX_PHASE_START, EX_CENTER);
2591 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2595 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2598 void SplashAcid(int x, int y)
2600 int element = Feld[x][y];
2602 if (element != EL_ACID_SPLASH_LEFT &&
2603 element != EL_ACID_SPLASH_RIGHT)
2605 PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2607 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2608 (!IN_LEV_FIELD(x-1, y-1) ||
2609 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2610 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2612 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2613 (!IN_LEV_FIELD(x+1, y-1) ||
2614 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2615 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2619 static void InitBeltMovement()
2621 static int belt_base_element[4] =
2623 EL_CONVEYOR_BELT_1_LEFT,
2624 EL_CONVEYOR_BELT_2_LEFT,
2625 EL_CONVEYOR_BELT_3_LEFT,
2626 EL_CONVEYOR_BELT_4_LEFT
2628 static int belt_base_active_element[4] =
2630 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2631 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2632 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2633 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2638 /* set frame order for belt animation graphic according to belt direction */
2645 int element = belt_base_active_element[belt_nr] + j;
2646 int graphic = el2img(element);
2648 if (game.belt_dir[i] == MV_LEFT)
2649 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2651 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2655 for(y=0; y<lev_fieldy; y++)
2657 for(x=0; x<lev_fieldx; x++)
2659 int element = Feld[x][y];
2663 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2665 int e_belt_nr = getBeltNrFromBeltElement(element);
2668 if (e_belt_nr == belt_nr)
2670 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2672 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2680 static void ToggleBeltSwitch(int x, int y)
2682 static int belt_base_element[4] =
2684 EL_CONVEYOR_BELT_1_LEFT,
2685 EL_CONVEYOR_BELT_2_LEFT,
2686 EL_CONVEYOR_BELT_3_LEFT,
2687 EL_CONVEYOR_BELT_4_LEFT
2689 static int belt_base_active_element[4] =
2691 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2692 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2693 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2694 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2696 static int belt_base_switch_element[4] =
2698 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2699 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2700 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2701 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2703 static int belt_move_dir[4] =
2711 int element = Feld[x][y];
2712 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2713 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2714 int belt_dir = belt_move_dir[belt_dir_nr];
2717 if (!IS_BELT_SWITCH(element))
2720 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2721 game.belt_dir[belt_nr] = belt_dir;
2723 if (belt_dir_nr == 3)
2726 /* set frame order for belt animation graphic according to belt direction */
2729 int element = belt_base_active_element[belt_nr] + i;
2730 int graphic = el2img(element);
2732 if (belt_dir == MV_LEFT)
2733 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2735 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2738 for (yy=0; yy<lev_fieldy; yy++)
2740 for (xx=0; xx<lev_fieldx; xx++)
2742 int element = Feld[xx][yy];
2744 if (IS_BELT_SWITCH(element))
2746 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2748 if (e_belt_nr == belt_nr)
2750 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2751 DrawLevelField(xx, yy);
2754 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2756 int e_belt_nr = getBeltNrFromBeltElement(element);
2758 if (e_belt_nr == belt_nr)
2760 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2762 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2763 DrawLevelField(xx, yy);
2766 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2768 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2770 if (e_belt_nr == belt_nr)
2772 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2774 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2775 DrawLevelField(xx, yy);
2782 static void ToggleSwitchgateSwitch(int x, int y)
2786 game.switchgate_pos = !game.switchgate_pos;
2788 for (yy=0; yy<lev_fieldy; yy++)
2790 for (xx=0; xx<lev_fieldx; xx++)
2792 int element = Feld[xx][yy];
2794 if (element == EL_SWITCHGATE_SWITCH_UP ||
2795 element == EL_SWITCHGATE_SWITCH_DOWN)
2797 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2798 DrawLevelField(xx, yy);
2800 else if (element == EL_SWITCHGATE_OPEN ||
2801 element == EL_SWITCHGATE_OPENING)
2803 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2805 PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2807 PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2810 else if (element == EL_SWITCHGATE_CLOSED ||
2811 element == EL_SWITCHGATE_CLOSING)
2813 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2815 PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2817 PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2824 static int getInvisibleActiveFromInvisibleElement(int element)
2826 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2827 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
2828 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
2832 static int getInvisibleFromInvisibleActiveElement(int element)
2834 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2835 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
2836 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
2840 static void RedrawAllLightSwitchesAndInvisibleElements()
2844 for (y=0; y<lev_fieldy; y++)
2846 for (x=0; x<lev_fieldx; x++)
2848 int element = Feld[x][y];
2850 if (element == EL_LIGHT_SWITCH &&
2851 game.light_time_left > 0)
2853 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2854 DrawLevelField(x, y);
2856 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2857 game.light_time_left == 0)
2859 Feld[x][y] = EL_LIGHT_SWITCH;
2860 DrawLevelField(x, y);
2862 else if (element == EL_INVISIBLE_STEELWALL ||
2863 element == EL_INVISIBLE_WALL ||
2864 element == EL_INVISIBLE_SAND)
2866 if (game.light_time_left > 0)
2867 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2869 DrawLevelField(x, y);
2871 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2872 element == EL_INVISIBLE_WALL_ACTIVE ||
2873 element == EL_INVISIBLE_SAND_ACTIVE)
2875 if (game.light_time_left == 0)
2876 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2878 DrawLevelField(x, y);
2884 static void ToggleLightSwitch(int x, int y)
2886 int element = Feld[x][y];
2888 game.light_time_left =
2889 (element == EL_LIGHT_SWITCH ?
2890 level.time_light * FRAMES_PER_SECOND : 0);
2892 RedrawAllLightSwitchesAndInvisibleElements();
2895 static void ActivateTimegateSwitch(int x, int y)
2899 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2901 for (yy=0; yy<lev_fieldy; yy++)
2903 for (xx=0; xx<lev_fieldx; xx++)
2905 int element = Feld[xx][yy];
2907 if (element == EL_TIMEGATE_CLOSED ||
2908 element == EL_TIMEGATE_CLOSING)
2910 Feld[xx][yy] = EL_TIMEGATE_OPENING;
2911 PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2915 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2917 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2918 DrawLevelField(xx, yy);
2925 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2928 inline static int getElementMoveStepsize(int x, int y)
2930 int element = Feld[x][y];
2931 int direction = MovDir[x][y];
2932 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2933 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2934 int horiz_move = (dx != 0);
2935 int sign = (horiz_move ? dx : dy);
2936 int step = sign * element_info[element].move_stepsize;
2938 /* special values for move stepsize for spring and things on conveyor belt */
2941 if (CAN_FALL(element) &&
2942 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2943 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2944 else if (element == EL_SPRING)
2945 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2951 void Impact(int x, int y)
2953 boolean lastline = (y == lev_fieldy-1);
2954 boolean object_hit = FALSE;
2955 boolean impact = (lastline || object_hit);
2956 int element = Feld[x][y];
2957 int smashed = EL_UNDEFINED;
2959 if (!lastline) /* check if element below was hit */
2961 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2964 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2965 MovDir[x][y + 1] != MV_DOWN ||
2966 MovPos[x][y + 1] <= TILEY / 2));
2968 /* do not smash moving elements that left the smashed field in time */
2969 if (game.engine_version >= RELEASE_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2970 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2974 smashed = MovingOrBlocked2Element(x, y + 1);
2976 impact = (lastline || object_hit);
2979 if (!lastline && smashed == EL_ACID) /* element falls into acid */
2985 /* only reset graphic animation if graphic really changes after impact */
2987 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
2989 ResetGfxAnimation(x, y);
2990 DrawLevelField(x, y);
2993 if (impact && CAN_EXPLODE_IMPACT(element))
2998 else if (impact && element == EL_PEARL)
3000 Feld[x][y] = EL_PEARL_BREAKING;
3001 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3004 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3006 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3011 if (impact && element == EL_AMOEBA_DROP)
3013 if (object_hit && IS_PLAYER(x, y + 1))
3014 KillHeroUnlessProtected(x, y + 1);
3015 else if (object_hit && smashed == EL_PENGUIN)
3019 Feld[x][y] = EL_AMOEBA_GROWING;
3020 Store[x][y] = EL_AMOEBA_WET;
3022 ResetRandomAnimationValue(x, y);
3027 if (object_hit) /* check which object was hit */
3029 if (CAN_PASS_MAGIC_WALL(element) &&
3030 (smashed == EL_MAGIC_WALL ||
3031 smashed == EL_BD_MAGIC_WALL))
3034 int activated_magic_wall =
3035 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3036 EL_BD_MAGIC_WALL_ACTIVE);
3038 /* activate magic wall / mill */
3039 for (yy=0; yy<lev_fieldy; yy++)
3040 for (xx=0; xx<lev_fieldx; xx++)
3041 if (Feld[xx][yy] == smashed)
3042 Feld[xx][yy] = activated_magic_wall;
3044 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3045 game.magic_wall_active = TRUE;
3047 PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3048 SND_MAGIC_WALL_ACTIVATING :
3049 SND_BD_MAGIC_WALL_ACTIVATING));
3052 if (IS_PLAYER(x, y + 1))
3054 if (CAN_SMASH_PLAYER(element))
3056 KillHeroUnlessProtected(x, y + 1);
3060 else if (smashed == EL_PENGUIN)
3062 if (CAN_SMASH_PLAYER(element))
3068 else if (element == EL_BD_DIAMOND)
3070 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3076 else if ((element == EL_SP_INFOTRON ||
3077 element == EL_SP_ZONK) &&
3078 (smashed == EL_SP_SNIKSNAK ||
3079 smashed == EL_SP_ELECTRON ||
3080 smashed == EL_SP_DISK_ORANGE))
3086 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3092 else if (CAN_SMASH_EVERYTHING(element))
3094 if (IS_CLASSIC_ENEMY(smashed) ||
3095 CAN_EXPLODE_SMASHED(smashed))
3100 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3102 if (smashed == EL_LAMP ||
3103 smashed == EL_LAMP_ACTIVE)
3108 else if (smashed == EL_NUT)
3110 Feld[x][y + 1] = EL_NUT_BREAKING;
3111 PlaySoundLevel(x, y, SND_NUT_BREAKING);
3112 RaiseScoreElement(EL_NUT);
3115 else if (smashed == EL_PEARL)
3117 Feld[x][y + 1] = EL_PEARL_BREAKING;
3118 PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3121 else if (smashed == EL_DIAMOND)
3123 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3124 PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3127 else if (IS_BELT_SWITCH(smashed))
3129 ToggleBeltSwitch(x, y + 1);
3131 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3132 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3134 ToggleSwitchgateSwitch(x, y + 1);
3136 else if (smashed == EL_LIGHT_SWITCH ||
3137 smashed == EL_LIGHT_SWITCH_ACTIVE)
3139 ToggleLightSwitch(x, y + 1);
3143 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3145 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3146 CE_OTHER_IS_SWITCHING);
3147 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3153 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3158 /* play sound of magic wall / mill */
3160 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3161 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3163 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3164 PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3165 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3166 PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3171 /* play sound of object that hits the ground */
3172 if (lastline || object_hit)
3173 PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3176 void TurnRound(int x, int y)
3188 { 0, 0 }, { 0, 0 }, { 0, 0 },
3193 int left, right, back;
3197 { MV_DOWN, MV_UP, MV_RIGHT },
3198 { MV_UP, MV_DOWN, MV_LEFT },
3200 { MV_LEFT, MV_RIGHT, MV_DOWN },
3204 { MV_RIGHT, MV_LEFT, MV_UP }
3207 int element = Feld[x][y];
3208 int old_move_dir = MovDir[x][y];
3209 int left_dir = turn[old_move_dir].left;
3210 int right_dir = turn[old_move_dir].right;
3211 int back_dir = turn[old_move_dir].back;
3213 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3214 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3215 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3216 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3218 int left_x = x + left_dx, left_y = y + left_dy;
3219 int right_x = x + right_dx, right_y = y + right_dy;
3220 int move_x = x + move_dx, move_y = y + move_dy;
3224 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3226 TestIfBadThingTouchesOtherBadThing(x, y);
3228 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3229 MovDir[x][y] = right_dir;
3230 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3231 MovDir[x][y] = left_dir;
3233 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3235 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3238 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3239 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3241 TestIfBadThingTouchesOtherBadThing(x, y);
3243 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3244 MovDir[x][y] = left_dir;
3245 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3246 MovDir[x][y] = right_dir;
3248 if ((element == EL_SPACESHIP ||
3249 element == EL_SP_SNIKSNAK ||
3250 element == EL_SP_ELECTRON)
3251 && MovDir[x][y] != old_move_dir)
3253 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3256 else if (element == EL_YAMYAM)
3258 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3259 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3261 if (can_turn_left && can_turn_right)
3262 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3263 else if (can_turn_left)
3264 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3265 else if (can_turn_right)
3266 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3268 MovDir[x][y] = back_dir;
3270 MovDelay[x][y] = 16 + 16 * RND(3);
3272 else if (element == EL_DARK_YAMYAM)
3274 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3275 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3277 if (can_turn_left && can_turn_right)
3278 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3279 else if (can_turn_left)
3280 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3281 else if (can_turn_right)
3282 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3284 MovDir[x][y] = back_dir;
3286 MovDelay[x][y] = 16 + 16 * RND(3);
3288 else if (element == EL_PACMAN)
3290 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3291 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3293 if (can_turn_left && can_turn_right)
3294 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3295 else if (can_turn_left)
3296 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3297 else if (can_turn_right)
3298 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3300 MovDir[x][y] = back_dir;
3302 MovDelay[x][y] = 6 + RND(40);
3304 else if (element == EL_PIG)
3306 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3307 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3308 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3309 boolean should_turn_left, should_turn_right, should_move_on;
3311 int rnd = RND(rnd_value);
3313 should_turn_left = (can_turn_left &&
3315 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3316 y + back_dy + left_dy)));
3317 should_turn_right = (can_turn_right &&
3319 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3320 y + back_dy + right_dy)));
3321 should_move_on = (can_move_on &&
3324 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3325 y + move_dy + left_dy) ||
3326 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3327 y + move_dy + right_dy)));
3329 if (should_turn_left || should_turn_right || should_move_on)
3331 if (should_turn_left && should_turn_right && should_move_on)
3332 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3333 rnd < 2 * rnd_value / 3 ? right_dir :
3335 else if (should_turn_left && should_turn_right)
3336 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3337 else if (should_turn_left && should_move_on)
3338 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3339 else if (should_turn_right && should_move_on)
3340 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3341 else if (should_turn_left)
3342 MovDir[x][y] = left_dir;
3343 else if (should_turn_right)
3344 MovDir[x][y] = right_dir;
3345 else if (should_move_on)
3346 MovDir[x][y] = old_move_dir;
3348 else if (can_move_on && rnd > rnd_value / 8)
3349 MovDir[x][y] = old_move_dir;
3350 else if (can_turn_left && can_turn_right)
3351 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3352 else if (can_turn_left && rnd > rnd_value / 8)
3353 MovDir[x][y] = left_dir;
3354 else if (can_turn_right && rnd > rnd_value/8)
3355 MovDir[x][y] = right_dir;
3357 MovDir[x][y] = back_dir;
3359 xx = x + move_xy[MovDir[x][y]].x;
3360 yy = y + move_xy[MovDir[x][y]].y;
3362 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3363 MovDir[x][y] = old_move_dir;
3367 else if (element == EL_DRAGON)
3369 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3370 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3371 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3373 int rnd = RND(rnd_value);
3375 if (can_move_on && rnd > rnd_value / 8)
3376 MovDir[x][y] = old_move_dir;
3377 else if (can_turn_left && can_turn_right)
3378 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3379 else if (can_turn_left && rnd > rnd_value / 8)
3380 MovDir[x][y] = left_dir;
3381 else if (can_turn_right && rnd > rnd_value / 8)
3382 MovDir[x][y] = right_dir;
3384 MovDir[x][y] = back_dir;
3386 xx = x + move_xy[MovDir[x][y]].x;
3387 yy = y + move_xy[MovDir[x][y]].y;
3389 if (!IS_FREE(xx, yy))
3390 MovDir[x][y] = old_move_dir;
3394 else if (element == EL_MOLE)
3396 boolean can_move_on =
3397 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3398 IS_AMOEBOID(Feld[move_x][move_y]) ||
3399 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3402 boolean can_turn_left =
3403 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3404 IS_AMOEBOID(Feld[left_x][left_y])));
3406 boolean can_turn_right =
3407 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3408 IS_AMOEBOID(Feld[right_x][right_y])));
3410 if (can_turn_left && can_turn_right)
3411 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3412 else if (can_turn_left)
3413 MovDir[x][y] = left_dir;
3415 MovDir[x][y] = right_dir;
3418 if (MovDir[x][y] != old_move_dir)
3421 else if (element == EL_BALLOON)
3423 MovDir[x][y] = game.balloon_dir;
3426 else if (element == EL_SPRING)
3428 if (MovDir[x][y] & MV_HORIZONTAL &&
3429 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3430 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3431 MovDir[x][y] = MV_NO_MOVING;
3435 else if (element == EL_ROBOT ||
3436 element == EL_SATELLITE ||
3437 element == EL_PENGUIN)
3439 int attr_x = -1, attr_y = -1;
3450 for (i=0; i<MAX_PLAYERS; i++)
3452 struct PlayerInfo *player = &stored_player[i];
3453 int jx = player->jx, jy = player->jy;
3455 if (!player->active)
3459 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3467 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3473 if (element == EL_PENGUIN)
3476 static int xy[4][2] =
3486 int ex = x + xy[i % 4][0];
3487 int ey = y + xy[i % 4][1];
3489 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3498 MovDir[x][y] = MV_NO_MOVING;
3500 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3501 else if (attr_x > x)
3502 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3504 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3505 else if (attr_y > y)
3506 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3508 if (element == EL_ROBOT)
3512 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3513 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3514 Moving2Blocked(x, y, &newx, &newy);
3516 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3517 MovDelay[x][y] = 8 + 8 * !RND(3);
3519 MovDelay[x][y] = 16;
3521 else if (element == EL_PENGUIN)
3527 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3529 boolean first_horiz = RND(2);
3530 int new_move_dir = MovDir[x][y];
3533 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3534 Moving2Blocked(x, y, &newx, &newy);
3536 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3540 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3541 Moving2Blocked(x, y, &newx, &newy);
3543 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3546 MovDir[x][y] = old_move_dir;
3550 else /* (element == EL_SATELLITE) */
3556 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3558 boolean first_horiz = RND(2);
3559 int new_move_dir = MovDir[x][y];
3562 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3563 Moving2Blocked(x, y, &newx, &newy);
3565 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3569 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3570 Moving2Blocked(x, y, &newx, &newy);
3572 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3575 MovDir[x][y] = old_move_dir;
3580 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3581 element_info[element].move_pattern == MV_TURNING_LEFT ||
3582 element_info[element].move_pattern == MV_TURNING_RIGHT)
3584 boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3585 boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3587 if (element_info[element].move_pattern == MV_TURNING_LEFT)
3588 MovDir[x][y] = left_dir;
3589 else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3590 MovDir[x][y] = right_dir;
3591 else if (can_turn_left && can_turn_right)
3592 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3593 else if (can_turn_left)
3594 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3595 else if (can_turn_right)
3596 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3598 MovDir[x][y] = back_dir;
3600 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3602 else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3603 element_info[element].move_pattern == MV_VERTICAL)
3605 if (element_info[element].move_pattern & old_move_dir)
3606 MovDir[x][y] = back_dir;
3607 else if (element_info[element].move_pattern == MV_HORIZONTAL)
3608 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3609 else if (element_info[element].move_pattern == MV_VERTICAL)
3610 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3612 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3614 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3616 MovDir[x][y] = element_info[element].move_pattern;
3617 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3619 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3621 if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3622 MovDir[x][y] = left_dir;
3623 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3624 MovDir[x][y] = right_dir;
3626 if (MovDir[x][y] != old_move_dir)
3627 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3629 else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3631 if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3632 MovDir[x][y] = right_dir;
3633 else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3634 MovDir[x][y] = left_dir;
3636 if (MovDir[x][y] != old_move_dir)
3637 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3639 else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3640 element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3642 int attr_x = -1, attr_y = -1;
3645 (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3656 for (i=0; i<MAX_PLAYERS; i++)
3658 struct PlayerInfo *player = &stored_player[i];
3659 int jx = player->jx, jy = player->jy;
3661 if (!player->active)
3665 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3673 MovDir[x][y] = MV_NO_MOVING;
3675 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3676 else if (attr_x > x)
3677 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3679 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3680 else if (attr_y > y)
3681 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3683 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3685 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3687 boolean first_horiz = RND(2);
3688 int new_move_dir = MovDir[x][y];
3691 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3692 Moving2Blocked(x, y, &newx, &newy);
3694 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3698 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3699 Moving2Blocked(x, y, &newx, &newy);
3701 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3704 MovDir[x][y] = old_move_dir;
3707 else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3709 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3710 MovDir[x][y] = MV_NO_MOVING;
3716 static boolean JustBeingPushed(int x, int y)
3720 for (i=0; i<MAX_PLAYERS; i++)
3722 struct PlayerInfo *player = &stored_player[i];
3724 if (player->active && player->is_pushing && player->MovPos)
3726 int next_jx = player->jx + (player->jx - player->last_jx);
3727 int next_jy = player->jy + (player->jy - player->last_jy);
3729 if (x == next_jx && y == next_jy)
3737 void StartMoving(int x, int y)
3739 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
3740 boolean started_moving = FALSE; /* some elements can fall _and_ move */
3741 int element = Feld[x][y];
3746 /* !!! this should be handled more generic (not only for mole) !!! */
3747 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3748 GfxAction[x][y] = ACTION_DEFAULT;
3750 if (CAN_FALL(element) && y < lev_fieldy - 1)
3752 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3753 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3754 if (JustBeingPushed(x, y))
3757 if (element == EL_QUICKSAND_FULL)
3759 if (IS_FREE(x, y + 1))
3761 InitMovingField(x, y, MV_DOWN);
3762 started_moving = TRUE;
3764 Feld[x][y] = EL_QUICKSAND_EMPTYING;
3765 Store[x][y] = EL_ROCK;
3767 PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3769 PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3772 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3774 if (!MovDelay[x][y])
3775 MovDelay[x][y] = TILEY + 1;
3784 Feld[x][y] = EL_QUICKSAND_EMPTY;
3785 Feld[x][y + 1] = EL_QUICKSAND_FULL;
3786 Store[x][y + 1] = Store[x][y];
3789 PlaySoundLevelAction(x, y, ACTION_FILLING);
3791 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3795 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3796 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3798 InitMovingField(x, y, MV_DOWN);
3799 started_moving = TRUE;
3801 Feld[x][y] = EL_QUICKSAND_FILLING;
3802 Store[x][y] = element;
3804 PlaySoundLevelAction(x, y, ACTION_FILLING);
3806 PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3809 else if (element == EL_MAGIC_WALL_FULL)
3811 if (IS_FREE(x, y + 1))
3813 InitMovingField(x, y, MV_DOWN);
3814 started_moving = TRUE;
3816 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3817 Store[x][y] = EL_CHANGED(Store[x][y]);
3819 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3821 if (!MovDelay[x][y])
3822 MovDelay[x][y] = TILEY/4 + 1;
3831 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3832 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3833 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3837 else if (element == EL_BD_MAGIC_WALL_FULL)
3839 if (IS_FREE(x, y + 1))
3841 InitMovingField(x, y, MV_DOWN);
3842 started_moving = TRUE;
3844 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3845 Store[x][y] = EL_CHANGED2(Store[x][y]);
3847 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3849 if (!MovDelay[x][y])
3850 MovDelay[x][y] = TILEY/4 + 1;
3859 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3860 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3861 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3865 else if (CAN_PASS_MAGIC_WALL(element) &&
3866 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3867 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3869 InitMovingField(x, y, MV_DOWN);
3870 started_moving = TRUE;
3873 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3874 EL_BD_MAGIC_WALL_FILLING);
3875 Store[x][y] = element;
3878 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3880 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3885 InitMovingField(x, y, MV_DOWN);
3886 started_moving = TRUE;
3888 Store[x][y] = EL_ACID;
3890 /* !!! TEST !!! better use "_FALLING" etc. !!! */
3891 GfxAction[x][y + 1] = ACTION_ACTIVE;
3895 else if ((game.engine_version < RELEASE_IDENT(2,2,0,7) &&
3896 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3897 (Feld[x][y + 1] == EL_BLOCKED)) ||
3898 (game.engine_version >= VERSION_IDENT(3,0,7) &&
3899 CAN_SMASH(element) && WasJustFalling[x][y] &&
3900 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3904 else if (game.engine_version < RELEASE_IDENT(2,2,0,7) &&
3905 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3906 WasJustMoving[x][y] && !Pushed[x][y + 1])
3908 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3909 WasJustMoving[x][y])
3914 /* this is needed for a special case not covered by calling "Impact()"
3915 from "ContinueMoving()": if an element moves to a tile directly below
3916 another element which was just falling on that tile (which was empty
3917 in the previous frame), the falling element above would just stop
3918 instead of smashing the element below (in previous version, the above
3919 element was just checked for "moving" instead of "falling", resulting
3920 in incorrect smashes caused by horizontal movement of the above
3921 element; also, the case of the player being the element to smash was
3922 simply not covered here... :-/ ) */
3926 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3928 if (MovDir[x][y] == MV_NO_MOVING)
3930 InitMovingField(x, y, MV_DOWN);
3931 started_moving = TRUE;
3934 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3936 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3937 MovDir[x][y] = MV_DOWN;
3939 InitMovingField(x, y, MV_DOWN);
3940 started_moving = TRUE;
3942 else if (element == EL_AMOEBA_DROP)
3944 Feld[x][y] = EL_AMOEBA_GROWING;
3945 Store[x][y] = EL_AMOEBA_WET;
3947 /* Store[x][y + 1] must be zero, because:
3948 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3951 #if OLD_GAME_BEHAVIOUR
3952 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
3954 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
3955 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3956 element != EL_DX_SUPABOMB)
3959 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
3960 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
3961 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3962 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
3965 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
3966 (IS_FREE(x - 1, y + 1) ||
3967 Feld[x - 1][y + 1] == EL_ACID));
3968 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
3969 (IS_FREE(x + 1, y + 1) ||
3970 Feld[x + 1][y + 1] == EL_ACID));
3971 boolean can_fall_any = (can_fall_left || can_fall_right);
3972 boolean can_fall_both = (can_fall_left && can_fall_right);
3974 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
3976 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
3978 if (slippery_type == SLIPPERY_ONLY_LEFT)
3979 can_fall_right = FALSE;
3980 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
3981 can_fall_left = FALSE;
3982 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
3983 can_fall_right = FALSE;
3984 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
3985 can_fall_left = FALSE;
3987 can_fall_any = (can_fall_left || can_fall_right);
3988 can_fall_both = (can_fall_left && can_fall_right);
3993 if (can_fall_both &&
3994 (game.emulation != EMU_BOULDERDASH &&
3995 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
3996 can_fall_left = !(can_fall_right = RND(2));
3998 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
3999 started_moving = TRUE;
4002 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4004 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4005 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4006 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4007 int belt_dir = game.belt_dir[belt_nr];
4009 if ((belt_dir == MV_LEFT && left_is_free) ||
4010 (belt_dir == MV_RIGHT && right_is_free))
4012 InitMovingField(x, y, belt_dir);
4013 started_moving = TRUE;
4015 GfxAction[x][y] = ACTION_DEFAULT;
4020 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4021 if (CAN_MOVE(element) && !started_moving)
4026 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4029 if ((element == EL_SATELLITE ||
4030 element == EL_BALLOON ||
4031 element == EL_SPRING)
4032 && JustBeingPushed(x, y))
4038 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4039 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4041 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4043 Moving2Blocked(x, y, &newx, &newy);
4044 if (Feld[newx][newy] == EL_BLOCKED)
4045 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4050 if (!MovDelay[x][y]) /* start new movement phase */
4052 /* all objects that can change their move direction after each step
4053 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4055 if (element != EL_YAMYAM &&
4056 element != EL_DARK_YAMYAM &&
4057 element != EL_PACMAN &&
4058 !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4059 element_info[element].move_pattern != MV_TURNING_LEFT &&
4060 element_info[element].move_pattern != MV_TURNING_RIGHT)
4064 if (MovDelay[x][y] && (element == EL_BUG ||
4065 element == EL_SPACESHIP ||
4066 element == EL_SP_SNIKSNAK ||
4067 element == EL_SP_ELECTRON ||
4068 element == EL_MOLE))
4069 DrawLevelField(x, y);
4073 if (MovDelay[x][y]) /* wait some time before next movement */
4078 if (element == EL_YAMYAM)
4081 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4082 DrawLevelElementAnimation(x, y, element);
4086 if (MovDelay[x][y]) /* element still has to wait some time */
4089 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4090 ResetGfxAnimation(x, y);
4092 GfxAction[x][y] = ACTION_WAITING;
4095 if (element == EL_ROBOT ||
4097 element == EL_PACMAN ||
4099 element == EL_YAMYAM ||
4100 element == EL_DARK_YAMYAM)
4103 DrawLevelElementAnimation(x, y, element);
4105 DrawLevelElementAnimationIfNeeded(x, y, element);
4107 PlaySoundLevelAction(x, y, ACTION_WAITING);
4109 else if (element == EL_SP_ELECTRON)
4110 DrawLevelElementAnimationIfNeeded(x, y, element);
4111 else if (element == EL_DRAGON)
4114 int dir = MovDir[x][y];
4115 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4116 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4117 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4118 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4119 dir == MV_UP ? IMG_FLAMES_1_UP :
4120 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4121 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4124 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4127 GfxAction[x][y] = ACTION_ATTACKING;
4129 if (IS_PLAYER(x, y))
4130 DrawPlayerField(x, y);
4132 DrawLevelField(x, y);
4134 PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4136 for (i=1; i <= 3; i++)
4138 int xx = x + i * dx;
4139 int yy = y + i * dy;
4140 int sx = SCREENX(xx);
4141 int sy = SCREENY(yy);
4142 int flame_graphic = graphic + (i - 1);
4144 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4149 int flamed = MovingOrBlocked2Element(xx, yy);
4151 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4154 RemoveMovingField(xx, yy);
4156 Feld[xx][yy] = EL_FLAMES;
4157 if (IN_SCR_FIELD(sx, sy))
4159 DrawLevelFieldCrumbledSand(xx, yy);
4160 DrawGraphic(sx, sy, flame_graphic, frame);
4165 if (Feld[xx][yy] == EL_FLAMES)
4166 Feld[xx][yy] = EL_EMPTY;
4167 DrawLevelField(xx, yy);
4172 if (MovDelay[x][y]) /* element still has to wait some time */
4174 PlaySoundLevelAction(x, y, ACTION_WAITING);
4179 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4180 for all other elements GfxAction will be set by InitMovingField() */
4181 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4182 GfxAction[x][y] = ACTION_MOVING;
4185 /* now make next step */
4187 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4189 if (DONT_COLLIDE_WITH(element) &&
4190 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4191 !PLAYER_PROTECTED(newx, newy))
4194 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4197 /* player killed by element which is deadly when colliding with */
4199 KillHero(PLAYERINFO(newx, newy));
4204 else if ((element == EL_PENGUIN ||
4205 element == EL_ROBOT ||
4206 element == EL_SATELLITE ||
4207 element == EL_BALLOON ||
4208 IS_CUSTOM_ELEMENT(element)) &&
4209 IN_LEV_FIELD(newx, newy) &&
4210 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4213 Store[x][y] = EL_ACID;
4215 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4217 if (Feld[newx][newy] == EL_EXIT_OPEN)
4219 Feld[x][y] = EL_EMPTY;
4220 DrawLevelField(x, y);
4222 PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4223 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4224 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4226 local_player->friends_still_needed--;
4227 if (!local_player->friends_still_needed &&
4228 !local_player->GameOver && AllPlayersGone)
4229 local_player->LevelSolved = local_player->GameOver = TRUE;
4233 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4235 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4236 DrawLevelField(newx, newy);
4238 MovDir[x][y] = MV_NO_MOVING;
4240 else if (!IS_FREE(newx, newy))
4242 GfxAction[x][y] = ACTION_WAITING;
4244 if (IS_PLAYER(x, y))
4245 DrawPlayerField(x, y);
4247 DrawLevelField(x, y);
4251 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4253 if (IS_FOOD_PIG(Feld[newx][newy]))
4255 if (IS_MOVING(newx, newy))
4256 RemoveMovingField(newx, newy);
4259 Feld[newx][newy] = EL_EMPTY;
4260 DrawLevelField(newx, newy);
4263 PlaySoundLevel(x, y, SND_PIG_DIGGING);
4265 else if (!IS_FREE(newx, newy))
4267 if (IS_PLAYER(x, y))
4268 DrawPlayerField(x, y);
4270 DrawLevelField(x, y);
4274 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4276 if (!IS_FREE(newx, newy))
4278 if (IS_PLAYER(x, y))
4279 DrawPlayerField(x, y);
4281 DrawLevelField(x, y);
4287 boolean wanna_flame = !RND(10);
4288 int dx = newx - x, dy = newy - y;
4289 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4290 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4291 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4292 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4293 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4294 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4297 IS_CLASSIC_ENEMY(element1) ||
4298 IS_CLASSIC_ENEMY(element2)) &&
4299 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4300 element1 != EL_FLAMES && element2 != EL_FLAMES)
4303 ResetGfxAnimation(x, y);
4304 GfxAction[x][y] = ACTION_ATTACKING;
4307 if (IS_PLAYER(x, y))
4308 DrawPlayerField(x, y);
4310 DrawLevelField(x, y);
4312 PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4314 MovDelay[x][y] = 50;
4316 Feld[newx][newy] = EL_FLAMES;
4317 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4318 Feld[newx1][newy1] = EL_FLAMES;
4319 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4320 Feld[newx2][newy2] = EL_FLAMES;
4326 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4327 Feld[newx][newy] == EL_DIAMOND)
4329 if (IS_MOVING(newx, newy))
4330 RemoveMovingField(newx, newy);
4333 Feld[newx][newy] = EL_EMPTY;
4334 DrawLevelField(newx, newy);
4337 PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4339 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4340 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4342 if (AmoebaNr[newx][newy])
4344 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4345 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4346 Feld[newx][newy] == EL_BD_AMOEBA)
4347 AmoebaCnt[AmoebaNr[newx][newy]]--;
4350 if (IS_MOVING(newx, newy))
4351 RemoveMovingField(newx, newy);
4354 Feld[newx][newy] = EL_EMPTY;
4355 DrawLevelField(newx, newy);
4358 PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4360 else if ((element == EL_PACMAN || element == EL_MOLE)
4361 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4363 if (AmoebaNr[newx][newy])
4365 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4366 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4367 Feld[newx][newy] == EL_BD_AMOEBA)
4368 AmoebaCnt[AmoebaNr[newx][newy]]--;
4371 if (element == EL_MOLE)
4373 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4374 PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4376 ResetGfxAnimation(x, y);
4377 GfxAction[x][y] = ACTION_DIGGING;
4378 DrawLevelField(x, y);
4380 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4381 return; /* wait for shrinking amoeba */
4383 else /* element == EL_PACMAN */
4385 Feld[newx][newy] = EL_EMPTY;
4386 DrawLevelField(newx, newy);
4387 PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4390 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4391 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4392 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4394 /* wait for shrinking amoeba to completely disappear */
4397 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4399 /* object was running against a wall */
4404 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4405 DrawLevelElementAnimation(x, y, element);
4407 if (element == EL_BUG ||
4408 element == EL_SPACESHIP ||
4409 element == EL_SP_SNIKSNAK)
4410 DrawLevelField(x, y);
4411 else if (element == EL_MOLE)
4412 DrawLevelField(x, y);
4413 else if (element == EL_BD_BUTTERFLY ||
4414 element == EL_BD_FIREFLY)
4415 DrawLevelElementAnimationIfNeeded(x, y, element);
4416 else if (element == EL_SATELLITE)
4417 DrawLevelElementAnimationIfNeeded(x, y, element);
4418 else if (element == EL_SP_ELECTRON)
4419 DrawLevelElementAnimationIfNeeded(x, y, element);
4422 if (DONT_TOUCH(element))
4423 TestIfBadThingTouchesHero(x, y);
4426 PlaySoundLevelAction(x, y, ACTION_WAITING);
4432 InitMovingField(x, y, MovDir[x][y]);
4434 PlaySoundLevelAction(x, y, ACTION_MOVING);
4438 ContinueMoving(x, y);
4441 void ContinueMoving(int x, int y)
4443 int element = Feld[x][y];
4444 int direction = MovDir[x][y];
4445 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4446 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4447 int newx = x + dx, newy = y + dy;
4448 int nextx = newx + dx, nexty = newy + dy;
4449 boolean pushed = Pushed[x][y];
4451 MovPos[x][y] += getElementMoveStepsize(x, y);
4453 if (pushed) /* special case: moving object pushed by player */
4454 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4456 if (ABS(MovPos[x][y]) < TILEX)
4458 DrawLevelField(x, y);
4460 return; /* element is still moving */
4463 /* element reached destination field */
4465 Feld[x][y] = EL_EMPTY;
4466 Feld[newx][newy] = element;
4467 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4469 if (element == EL_MOLE)
4471 Feld[x][y] = EL_SAND;
4473 DrawLevelFieldCrumbledSandNeighbours(x, y);
4475 else if (element == EL_QUICKSAND_FILLING)
4477 element = Feld[newx][newy] = get_next_element(element);
4478 Store[newx][newy] = Store[x][y];
4480 else if (element == EL_QUICKSAND_EMPTYING)
4482 Feld[x][y] = get_next_element(element);
4483 element = Feld[newx][newy] = Store[x][y];
4485 else if (element == EL_MAGIC_WALL_FILLING)
4487 element = Feld[newx][newy] = get_next_element(element);
4488 if (!game.magic_wall_active)
4489 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4490 Store[newx][newy] = Store[x][y];
4492 else if (element == EL_MAGIC_WALL_EMPTYING)
4494 Feld[x][y] = get_next_element(element);
4495 if (!game.magic_wall_active)
4496 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4497 element = Feld[newx][newy] = Store[x][y];
4499 else if (element == EL_BD_MAGIC_WALL_FILLING)
4501 element = Feld[newx][newy] = get_next_element(element);
4502 if (!game.magic_wall_active)
4503 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4504 Store[newx][newy] = Store[x][y];
4506 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4508 Feld[x][y] = get_next_element(element);
4509 if (!game.magic_wall_active)
4510 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4511 element = Feld[newx][newy] = Store[x][y];
4513 else if (element == EL_AMOEBA_DROPPING)
4515 Feld[x][y] = get_next_element(element);
4516 element = Feld[newx][newy] = Store[x][y];
4518 else if (element == EL_SOKOBAN_OBJECT)
4521 Feld[x][y] = Back[x][y];
4523 if (Back[newx][newy])
4524 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4526 Back[x][y] = Back[newx][newy] = 0;
4528 else if (Store[x][y] == EL_ACID)
4530 element = Feld[newx][newy] = EL_ACID;
4534 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4535 MovDelay[newx][newy] = 0;
4537 /* copy element change control values to new field */
4538 ChangeDelay[newx][newy] = ChangeDelay[x][y];
4539 ChangePage[newx][newy] = ChangePage[x][y];
4540 Changed[newx][newy] = Changed[x][y];
4541 ChangeEvent[newx][newy] = ChangeEvent[x][y];
4543 ChangeDelay[x][y] = 0;
4544 ChangePage[x][y] = -1;
4545 Changed[x][y] = CE_BITMASK_DEFAULT;
4546 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4548 /* copy animation control values to new field */
4549 GfxFrame[newx][newy] = GfxFrame[x][y];
4550 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
4551 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
4553 Pushed[x][y] = Pushed[newx][newy] = FALSE;
4555 ResetGfxAnimation(x, y); /* reset animation values for old field */
4558 /* 2.1.1 (does not work correctly for spring) */
4559 if (!CAN_MOVE(element))
4560 MovDir[newx][newy] = 0;
4564 /* (does not work for falling objects that slide horizontally) */
4565 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4566 MovDir[newx][newy] = 0;
4569 if (!CAN_MOVE(element) ||
4570 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4571 MovDir[newx][newy] = 0;
4574 if (!CAN_MOVE(element) ||
4575 (CAN_FALL(element) && direction == MV_DOWN))
4576 MovDir[newx][newy] = 0;
4581 DrawLevelField(x, y);
4582 DrawLevelField(newx, newy);
4584 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
4586 /* prevent pushed element from moving on in pushed direction */
4587 if (pushed && CAN_MOVE(element) &&
4588 element_info[element].move_pattern & MV_ANY_DIRECTION &&
4589 !(element_info[element].move_pattern & direction))
4590 TurnRound(newx, newy);
4592 if (!pushed) /* special case: moving object pushed by player */
4594 WasJustMoving[newx][newy] = 3;
4596 if (CAN_FALL(element) && direction == MV_DOWN)
4597 WasJustFalling[newx][newy] = 3;
4600 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
4602 TestIfBadThingTouchesHero(newx, newy);
4603 TestIfBadThingTouchesFriend(newx, newy);
4604 TestIfBadThingTouchesOtherBadThing(newx, newy);
4606 else if (element == EL_PENGUIN)
4607 TestIfFriendTouchesBadThing(newx, newy);
4609 if (CAN_FALL(element) && direction == MV_DOWN &&
4610 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4614 TestIfElementTouchesCustomElement(x, y); /* for empty space */
4618 if (ChangePage[newx][newy] != -1) /* delayed change */
4619 ChangeElement(newx, newy, ChangePage[newx][newy]);
4622 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4623 CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4626 TestIfPlayerTouchesCustomElement(newx, newy);
4627 TestIfElementTouchesCustomElement(newx, newy);
4630 int AmoebeNachbarNr(int ax, int ay)
4633 int element = Feld[ax][ay];
4635 static int xy[4][2] =
4645 int x = ax + xy[i][0];
4646 int y = ay + xy[i][1];
4648 if (!IN_LEV_FIELD(x, y))
4651 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4652 group_nr = AmoebaNr[x][y];
4658 void AmoebenVereinigen(int ax, int ay)
4660 int i, x, y, xx, yy;
4661 int new_group_nr = AmoebaNr[ax][ay];
4662 static int xy[4][2] =
4670 if (new_group_nr == 0)
4678 if (!IN_LEV_FIELD(x, y))
4681 if ((Feld[x][y] == EL_AMOEBA_FULL ||
4682 Feld[x][y] == EL_BD_AMOEBA ||
4683 Feld[x][y] == EL_AMOEBA_DEAD) &&
4684 AmoebaNr[x][y] != new_group_nr)
4686 int old_group_nr = AmoebaNr[x][y];
4688 if (old_group_nr == 0)
4691 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4692 AmoebaCnt[old_group_nr] = 0;
4693 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4694 AmoebaCnt2[old_group_nr] = 0;
4696 for (yy=0; yy<lev_fieldy; yy++)
4698 for (xx=0; xx<lev_fieldx; xx++)
4700 if (AmoebaNr[xx][yy] == old_group_nr)
4701 AmoebaNr[xx][yy] = new_group_nr;
4708 void AmoebeUmwandeln(int ax, int ay)
4712 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4714 int group_nr = AmoebaNr[ax][ay];
4719 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4720 printf("AmoebeUmwandeln(): This should never happen!\n");
4725 for (y=0; y<lev_fieldy; y++)
4727 for (x=0; x<lev_fieldx; x++)
4729 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4732 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4736 PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4737 SND_AMOEBA_TURNING_TO_GEM :
4738 SND_AMOEBA_TURNING_TO_ROCK));
4743 static int xy[4][2] =
4756 if (!IN_LEV_FIELD(x, y))
4759 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4761 PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4762 SND_AMOEBA_TURNING_TO_GEM :
4763 SND_AMOEBA_TURNING_TO_ROCK));
4770 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4773 int group_nr = AmoebaNr[ax][ay];
4774 boolean done = FALSE;
4779 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4780 printf("AmoebeUmwandelnBD(): This should never happen!\n");
4785 for (y=0; y<lev_fieldy; y++)
4787 for (x=0; x<lev_fieldx; x++)
4789 if (AmoebaNr[x][y] == group_nr &&
4790 (Feld[x][y] == EL_AMOEBA_DEAD ||
4791 Feld[x][y] == EL_BD_AMOEBA ||
4792 Feld[x][y] == EL_AMOEBA_GROWING))
4795 Feld[x][y] = new_element;
4796 InitField(x, y, FALSE);
4797 DrawLevelField(x, y);
4804 PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4805 SND_BD_AMOEBA_TURNING_TO_ROCK :
4806 SND_BD_AMOEBA_TURNING_TO_GEM));
4809 void AmoebeWaechst(int x, int y)
4811 static unsigned long sound_delay = 0;
4812 static unsigned long sound_delay_value = 0;
4814 if (!MovDelay[x][y]) /* start new growing cycle */
4818 if (DelayReached(&sound_delay, sound_delay_value))
4821 PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4823 if (Store[x][y] == EL_BD_AMOEBA)
4824 PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4826 PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4828 sound_delay_value = 30;
4832 if (MovDelay[x][y]) /* wait some time before growing bigger */
4835 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4837 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4838 6 - MovDelay[x][y]);
4840 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4843 if (!MovDelay[x][y])
4845 Feld[x][y] = Store[x][y];
4847 DrawLevelField(x, y);
4852 void AmoebaDisappearing(int x, int y)
4854 static unsigned long sound_delay = 0;
4855 static unsigned long sound_delay_value = 0;
4857 if (!MovDelay[x][y]) /* start new shrinking cycle */
4861 if (DelayReached(&sound_delay, sound_delay_value))
4862 sound_delay_value = 30;
4865 if (MovDelay[x][y]) /* wait some time before shrinking */
4868 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4870 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4871 6 - MovDelay[x][y]);
4873 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4876 if (!MovDelay[x][y])
4878 Feld[x][y] = EL_EMPTY;
4879 DrawLevelField(x, y);
4881 /* don't let mole enter this field in this cycle;
4882 (give priority to objects falling to this field from above) */
4888 void AmoebeAbleger(int ax, int ay)
4891 int element = Feld[ax][ay];
4892 int graphic = el2img(element);
4893 int newax = ax, neway = ay;
4894 static int xy[4][2] =
4902 if (!level.amoeba_speed)
4904 Feld[ax][ay] = EL_AMOEBA_DEAD;
4905 DrawLevelField(ax, ay);
4909 if (IS_ANIMATED(graphic))
4910 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4912 if (!MovDelay[ax][ay]) /* start making new amoeba field */
4913 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4915 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
4918 if (MovDelay[ax][ay])
4922 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4925 int x = ax + xy[start][0];
4926 int y = ay + xy[start][1];
4928 if (!IN_LEV_FIELD(x, y))
4931 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4932 if (IS_FREE(x, y) ||
4933 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4939 if (newax == ax && neway == ay)
4942 else /* normal or "filled" (BD style) amoeba */
4945 boolean waiting_for_player = FALSE;
4949 int j = (start + i) % 4;
4950 int x = ax + xy[j][0];
4951 int y = ay + xy[j][1];
4953 if (!IN_LEV_FIELD(x, y))
4956 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4957 if (IS_FREE(x, y) ||
4958 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4964 else if (IS_PLAYER(x, y))
4965 waiting_for_player = TRUE;
4968 if (newax == ax && neway == ay) /* amoeba cannot grow */
4970 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
4972 Feld[ax][ay] = EL_AMOEBA_DEAD;
4973 DrawLevelField(ax, ay);
4974 AmoebaCnt[AmoebaNr[ax][ay]]--;
4976 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
4978 if (element == EL_AMOEBA_FULL)
4979 AmoebeUmwandeln(ax, ay);
4980 else if (element == EL_BD_AMOEBA)
4981 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
4986 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
4988 /* amoeba gets larger by growing in some direction */
4990 int new_group_nr = AmoebaNr[ax][ay];
4993 if (new_group_nr == 0)
4995 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
4996 printf("AmoebeAbleger(): This should never happen!\n");
5001 AmoebaNr[newax][neway] = new_group_nr;
5002 AmoebaCnt[new_group_nr]++;
5003 AmoebaCnt2[new_group_nr]++;
5005 /* if amoeba touches other amoeba(s) after growing, unify them */
5006 AmoebenVereinigen(newax, neway);
5008 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5010 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5016 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5017 (neway == lev_fieldy - 1 && newax != ax))
5019 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5020 Store[newax][neway] = element;
5022 else if (neway == ay)
5024 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5026 PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5028 PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5033 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5034 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5035 Store[ax][ay] = EL_AMOEBA_DROP;
5036 ContinueMoving(ax, ay);
5040 DrawLevelField(newax, neway);
5043 void Life(int ax, int ay)
5046 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5048 int element = Feld[ax][ay];
5049 int graphic = el2img(element);
5050 boolean changed = FALSE;
5052 if (IS_ANIMATED(graphic))
5053 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5058 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5059 MovDelay[ax][ay] = life_time;
5061 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5064 if (MovDelay[ax][ay])
5068 for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5070 int xx = ax+x1, yy = ay+y1;
5073 if (!IN_LEV_FIELD(xx, yy))
5076 for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5078 int x = xx+x2, y = yy+y2;
5080 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5083 if (((Feld[x][y] == element ||
5084 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5086 (IS_FREE(x, y) && Stop[x][y]))
5090 if (xx == ax && yy == ay) /* field in the middle */
5092 if (nachbarn < life[0] || nachbarn > life[1])
5094 Feld[xx][yy] = EL_EMPTY;
5096 DrawLevelField(xx, yy);
5097 Stop[xx][yy] = TRUE;
5101 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5102 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5103 { /* free border field */
5104 if (nachbarn >= life[2] && nachbarn <= life[3])
5106 Feld[xx][yy] = element;
5107 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5109 DrawLevelField(xx, yy);
5110 Stop[xx][yy] = TRUE;
5117 PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5118 SND_GAME_OF_LIFE_GROWING);
5121 static void InitRobotWheel(int x, int y)
5123 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5126 static void RunRobotWheel(int x, int y)
5128 PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5131 static void StopRobotWheel(int x, int y)
5133 if (ZX == x && ZY == y)
5137 static void InitTimegateWheel(int x, int y)
5139 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5142 static void RunTimegateWheel(int x, int y)
5144 PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5147 void CheckExit(int x, int y)
5149 if (local_player->gems_still_needed > 0 ||
5150 local_player->sokobanfields_still_needed > 0 ||
5151 local_player->lights_still_needed > 0)
5153 int element = Feld[x][y];
5154 int graphic = el2img(element);
5156 if (IS_ANIMATED(graphic))
5157 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5162 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5165 Feld[x][y] = EL_EXIT_OPENING;
5167 PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5170 void CheckExitSP(int x, int y)
5172 if (local_player->gems_still_needed > 0)
5174 int element = Feld[x][y];
5175 int graphic = el2img(element);
5177 if (IS_ANIMATED(graphic))
5178 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5183 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5186 Feld[x][y] = EL_SP_EXIT_OPENING;
5188 PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5191 static void CloseAllOpenTimegates()
5195 for (y=0; y<lev_fieldy; y++)
5197 for (x=0; x<lev_fieldx; x++)
5199 int element = Feld[x][y];
5201 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5203 Feld[x][y] = EL_TIMEGATE_CLOSING;
5205 PlaySoundLevelAction(x, y, ACTION_CLOSING);
5207 PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5214 void EdelsteinFunkeln(int x, int y)
5216 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5219 if (Feld[x][y] == EL_BD_DIAMOND)
5222 if (MovDelay[x][y] == 0) /* next animation frame */
5223 MovDelay[x][y] = 11 * !SimpleRND(500);
5225 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5229 if (setup.direct_draw && MovDelay[x][y])
5230 SetDrawtoField(DRAW_BUFFERED);
5232 DrawLevelElementAnimation(x, y, Feld[x][y]);
5234 if (MovDelay[x][y] != 0)
5236 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5237 10 - MovDelay[x][y]);
5239 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5241 if (setup.direct_draw)
5245 dest_x = FX + SCREENX(x) * TILEX;
5246 dest_y = FY + SCREENY(y) * TILEY;
5248 BlitBitmap(drawto_field, window,
5249 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5250 SetDrawtoField(DRAW_DIRECT);
5256 void MauerWaechst(int x, int y)
5260 if (!MovDelay[x][y]) /* next animation frame */
5261 MovDelay[x][y] = 3 * delay;
5263 if (MovDelay[x][y]) /* wait some time before next frame */
5267 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5269 int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
5270 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5272 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5275 if (!MovDelay[x][y])
5277 if (MovDir[x][y] == MV_LEFT)
5279 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5280 DrawLevelField(x - 1, y);
5282 else if (MovDir[x][y] == MV_RIGHT)
5284 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5285 DrawLevelField(x + 1, y);
5287 else if (MovDir[x][y] == MV_UP)
5289 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5290 DrawLevelField(x, y - 1);
5294 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5295 DrawLevelField(x, y + 1);
5298 Feld[x][y] = Store[x][y];
5300 MovDir[x][y] = MV_NO_MOVING;
5301 DrawLevelField(x, y);
5306 void MauerAbleger(int ax, int ay)
5308 int element = Feld[ax][ay];
5309 int graphic = el2img(element);
5310 boolean oben_frei = FALSE, unten_frei = FALSE;
5311 boolean links_frei = FALSE, rechts_frei = FALSE;
5312 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5313 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5314 boolean new_wall = FALSE;
5316 if (IS_ANIMATED(graphic))
5317 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5319 if (!MovDelay[ax][ay]) /* start building new wall */
5320 MovDelay[ax][ay] = 6;
5322 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5325 if (MovDelay[ax][ay])
5329 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5331 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5333 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5335 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5338 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5339 element == EL_EXPANDABLE_WALL_ANY)
5343 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5344 Store[ax][ay-1] = element;
5345 MovDir[ax][ay-1] = MV_UP;
5346 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5347 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5348 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5353 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5354 Store[ax][ay+1] = element;
5355 MovDir[ax][ay+1] = MV_DOWN;
5356 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5357 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5358 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5363 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5364 element == EL_EXPANDABLE_WALL_ANY ||
5365 element == EL_EXPANDABLE_WALL)
5369 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5370 Store[ax-1][ay] = element;
5371 MovDir[ax-1][ay] = MV_LEFT;
5372 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5373 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5374 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5380 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5381 Store[ax+1][ay] = element;
5382 MovDir[ax+1][ay] = MV_RIGHT;
5383 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5384 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5385 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5390 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5391 DrawLevelField(ax, ay);
5393 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5395 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5396 unten_massiv = TRUE;
5397 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5398 links_massiv = TRUE;
5399 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5400 rechts_massiv = TRUE;
5402 if (((oben_massiv && unten_massiv) ||
5403 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5404 element == EL_EXPANDABLE_WALL) &&
5405 ((links_massiv && rechts_massiv) ||
5406 element == EL_EXPANDABLE_WALL_VERTICAL))
5407 Feld[ax][ay] = EL_WALL;
5411 PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5413 PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5417 void CheckForDragon(int x, int y)
5420 boolean dragon_found = FALSE;
5421 static int xy[4][2] =
5433 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5435 if (IN_LEV_FIELD(xx, yy) &&
5436 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5438 if (Feld[xx][yy] == EL_DRAGON)
5439 dragon_found = TRUE;
5452 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5454 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5456 Feld[xx][yy] = EL_EMPTY;
5457 DrawLevelField(xx, yy);
5466 static void InitBuggyBase(int x, int y)
5468 int element = Feld[x][y];
5469 int activating_delay = FRAMES_PER_SECOND / 4;
5472 (element == EL_SP_BUGGY_BASE ?
5473 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5474 element == EL_SP_BUGGY_BASE_ACTIVATING ?
5476 element == EL_SP_BUGGY_BASE_ACTIVE ?
5477 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5480 static void WarnBuggyBase(int x, int y)
5483 static int xy[4][2] =
5493 int xx = x + xy[i][0], yy = y + xy[i][1];
5495 if (IS_PLAYER(xx, yy))
5497 PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5504 static void InitTrap(int x, int y)
5506 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5509 static void ActivateTrap(int x, int y)
5511 PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5514 static void ChangeActiveTrap(int x, int y)
5516 int graphic = IMG_TRAP_ACTIVE;
5518 /* if new animation frame was drawn, correct crumbled sand border */
5519 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5520 DrawLevelFieldCrumbledSand(x, y);
5523 static void ChangeElementNowExt(int x, int y, int target_element)
5525 /* check if element under player changes from accessible to unaccessible
5526 (needed for special case of dropping element which then changes) */
5527 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5528 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5535 Feld[x][y] = target_element;
5537 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5539 ResetGfxAnimation(x, y);
5540 ResetRandomAnimationValue(x, y);
5542 InitField(x, y, FALSE);
5543 if (CAN_MOVE(Feld[x][y]))
5546 DrawLevelField(x, y);
5548 if (GFX_CRUMBLED(Feld[x][y]))
5549 DrawLevelFieldCrumbledSandNeighbours(x, y);
5551 TestIfBadThingTouchesHero(x, y);
5552 TestIfPlayerTouchesCustomElement(x, y);
5553 TestIfElementTouchesCustomElement(x, y);
5555 if (ELEM_IS_PLAYER(target_element))
5556 RelocatePlayer(x, y, target_element);
5559 static boolean ChangeElementNow(int x, int y, int element, int page)
5561 struct ElementChangeInfo *change = &element_info[element].change_page[page];
5563 /* always use default change event to prevent running into a loop */
5564 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5565 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5567 /* do not change already changed elements with same change event */
5569 if (Changed[x][y] & ChangeEvent[x][y])
5576 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
5578 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5580 if (change->explode)
5587 if (change->use_content)
5589 boolean complete_change = TRUE;
5590 boolean can_change[3][3];
5593 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5595 boolean half_destructible;
5596 int ex = x + xx - 1;
5597 int ey = y + yy - 1;
5600 can_change[xx][yy] = TRUE;
5602 if (ex == x && ey == y) /* do not check changing element itself */
5605 if (change->content[xx][yy] == EL_EMPTY_SPACE)
5607 can_change[xx][yy] = FALSE; /* do not change empty borders */
5612 if (!IN_LEV_FIELD(ex, ey))
5614 can_change[xx][yy] = FALSE;
5615 complete_change = FALSE;
5622 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5623 e = MovingOrBlocked2Element(ex, ey);
5625 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5627 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
5628 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5629 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5631 can_change[xx][yy] = FALSE;
5632 complete_change = FALSE;
5636 if (!change->only_complete || complete_change)
5638 boolean something_has_changed = FALSE;
5640 if (change->only_complete && change->use_random_change &&
5641 RND(100) < change->random)
5644 for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5646 int ex = x + xx - 1;
5647 int ey = y + yy - 1;
5649 if (can_change[xx][yy] && (!change->use_random_change ||
5650 RND(100) < change->random))
5652 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5653 RemoveMovingField(ex, ey);
5655 ChangeEvent[ex][ey] = ChangeEvent[x][y];
5657 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5659 something_has_changed = TRUE;
5661 /* for symmetry reasons, freeze newly created border elements */
5662 if (ex != x || ey != y)
5663 Stop[ex][ey] = TRUE; /* no more moving in this frame */
5667 if (something_has_changed)
5668 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5673 ChangeElementNowExt(x, y, change->target_element);
5675 PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5681 static void ChangeElement(int x, int y, int page)
5683 int element = MovingOrBlocked2Element(x, y);
5684 struct ElementInfo *ei = &element_info[element];
5685 struct ElementChangeInfo *change = &ei->change_page[page];
5689 if (!CAN_CHANGE(element))
5692 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5693 x, y, element, element_info[element].token_name);
5694 printf("ChangeElement(): This should never happen!\n");
5700 if (ChangeDelay[x][y] == 0) /* initialize element change */
5702 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
5703 RND(change->delay_random * change->delay_frames)) + 1;
5705 ResetGfxAnimation(x, y);
5706 ResetRandomAnimationValue(x, y);
5708 if (change->pre_change_function)
5709 change->pre_change_function(x, y);
5712 ChangeDelay[x][y]--;
5714 if (ChangeDelay[x][y] != 0) /* continue element change */
5716 int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5718 if (IS_ANIMATED(graphic))
5719 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5721 if (change->change_function)
5722 change->change_function(x, y);
5724 else /* finish element change */
5726 if (ChangePage[x][y] != -1) /* remember page from delayed change */
5728 page = ChangePage[x][y];
5729 ChangePage[x][y] = -1;
5732 if (IS_MOVING(x, y)) /* never change a running system ;-) */
5734 ChangeDelay[x][y] = 1; /* try change after next move step */
5735 ChangePage[x][y] = page; /* remember page to use for change */
5740 if (ChangeElementNow(x, y, element, page))
5742 if (change->post_change_function)
5743 change->post_change_function(x, y);
5748 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5749 int trigger_element,
5755 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5758 for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5760 int element = EL_CUSTOM_START + i;
5762 boolean change_element = FALSE;
5765 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5768 for (j=0; j < element_info[element].num_change_pages; j++)
5770 struct ElementChangeInfo *change = &element_info[element].change_page[j];
5772 if (change->can_change &&
5773 change->sides & trigger_side &&
5774 change->trigger_element == trigger_element)
5776 change_element = TRUE;
5783 if (!change_element)
5786 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5789 if (x == lx && y == ly) /* do not change trigger element itself */
5793 if (Feld[x][y] == element)
5795 ChangeDelay[x][y] = 1;
5796 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5797 ChangeElement(x, y, page);
5805 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5808 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5812 static boolean CheckElementSideChange(int x, int y, int element, int side,
5813 int trigger_event, int page)
5815 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5818 if (Feld[x][y] == EL_BLOCKED)
5820 Blocked2Moving(x, y, &x, &y);
5821 element = Feld[x][y];
5825 page = element_info[element].event_page_nr[trigger_event];
5827 if (!(element_info[element].change_page[page].sides & side))
5830 ChangeDelay[x][y] = 1;
5831 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5832 ChangeElement(x, y, page);
5837 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5839 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5842 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5844 static byte stored_player_action[MAX_PLAYERS];
5845 static int num_stored_actions = 0;
5846 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5847 int left = player_action & JOY_LEFT;
5848 int right = player_action & JOY_RIGHT;
5849 int up = player_action & JOY_UP;
5850 int down = player_action & JOY_DOWN;
5851 int button1 = player_action & JOY_BUTTON_1;
5852 int button2 = player_action & JOY_BUTTON_2;
5853 int dx = (left ? -1 : right ? 1 : 0);
5854 int dy = (up ? -1 : down ? 1 : 0);
5856 stored_player_action[player->index_nr] = 0;
5857 num_stored_actions++;
5859 if (!player->active || tape.pausing)
5865 snapped = SnapField(player, dx, dy);
5869 dropped = DropElement(player);
5871 moved = MovePlayer(player, dx, dy);
5874 if (tape.single_step && tape.recording && !tape.pausing)
5876 if (button1 || (dropped && !moved))
5878 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5879 SnapField(player, 0, 0); /* stop snapping */
5883 stored_player_action[player->index_nr] = player_action;
5887 /* no actions for this player (no input at player's configured device) */
5889 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5890 SnapField(player, 0, 0);
5891 CheckGravityMovement(player);
5893 if (player->MovPos == 0)
5894 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5896 if (player->MovPos == 0) /* needed for tape.playing */
5897 player->is_moving = FALSE;
5900 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5902 TapeRecordAction(stored_player_action);
5903 num_stored_actions = 0;
5909 static unsigned long action_delay = 0;
5910 unsigned long action_delay_value;
5911 int magic_wall_x = 0, magic_wall_y = 0;
5912 int i, x, y, element, graphic;
5913 byte *recorded_player_action;
5914 byte summarized_player_action = 0;
5916 if (game_status != GAME_MODE_PLAYING)
5919 action_delay_value =
5920 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5922 if (tape.playing && tape.index_search && !tape.pausing)
5923 action_delay_value = 0;
5925 /* ---------- main game synchronization point ---------- */
5927 WaitUntilDelayReached(&action_delay, action_delay_value);
5929 if (network_playing && !network_player_action_received)
5933 printf("DEBUG: try to get network player actions in time\n");
5937 #if defined(PLATFORM_UNIX)
5938 /* last chance to get network player actions without main loop delay */
5942 if (game_status != GAME_MODE_PLAYING)
5945 if (!network_player_action_received)
5949 printf("DEBUG: failed to get network player actions in time\n");
5959 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
5961 for (i=0; i<MAX_PLAYERS; i++)
5963 summarized_player_action |= stored_player[i].action;
5965 if (!network_playing)
5966 stored_player[i].effective_action = stored_player[i].action;
5969 #if defined(PLATFORM_UNIX)
5970 if (network_playing)
5971 SendToServer_MovePlayer(summarized_player_action);
5974 if (!options.network && !setup.team_mode)
5975 local_player->effective_action = summarized_player_action;
5977 for (i=0; i<MAX_PLAYERS; i++)
5979 int actual_player_action = stored_player[i].effective_action;
5981 if (stored_player[i].programmed_action)
5982 actual_player_action = stored_player[i].programmed_action;
5984 if (recorded_player_action)
5985 actual_player_action = recorded_player_action[i];
5987 PlayerActions(&stored_player[i], actual_player_action);
5988 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
5991 network_player_action_received = FALSE;
5993 ScrollScreen(NULL, SCROLL_GO_ON);
5999 for (i=0; i<MAX_PLAYERS; i++)
6000 stored_player[i].Frame++;
6004 if (game.engine_version < RELEASE_IDENT(2,2,0,7))
6006 for (i=0; i<MAX_PLAYERS; i++)
6008 struct PlayerInfo *player = &stored_player[i];
6012 if (player->active && player->is_pushing && player->is_moving &&
6015 ContinueMoving(x, y);
6017 /* continue moving after pushing (this is actually a bug) */
6018 if (!IS_MOVING(x, y))
6027 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6029 Changed[x][y] = CE_BITMASK_DEFAULT;
6030 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6033 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6035 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6036 printf("GameActions(): This should never happen!\n");
6038 ChangePage[x][y] = -1;
6043 if (WasJustMoving[x][y] > 0)
6044 WasJustMoving[x][y]--;
6045 if (WasJustFalling[x][y] > 0)
6046 WasJustFalling[x][y]--;
6051 /* reset finished pushing action (not done in ContinueMoving() to allow
6052 continous pushing animation for elements with zero push delay) */
6053 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6055 ResetGfxAnimation(x, y);
6056 DrawLevelField(x, y);
6061 if (IS_BLOCKED(x, y))
6065 Blocked2Moving(x, y, &oldx, &oldy);
6066 if (!IS_MOVING(oldx, oldy))
6068 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6069 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6070 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6071 printf("GameActions(): This should never happen!\n");
6077 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6079 element = Feld[x][y];
6081 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6083 graphic = el2img(element);
6089 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6091 element = graphic = 0;
6095 if (graphic_info[graphic].anim_global_sync)
6096 GfxFrame[x][y] = FrameCounter;
6098 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6099 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6100 ResetRandomAnimationValue(x, y);
6102 SetRandomAnimationValue(x, y);
6105 PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6108 if (IS_INACTIVE(element))
6110 if (IS_ANIMATED(graphic))
6111 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6117 /* this may take place after moving, so 'element' may have changed */
6118 if (IS_CHANGING(x, y))
6121 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6122 element_info[element].event_page_nr[CE_DELAY]);
6124 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6127 element = Feld[x][y];
6128 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6132 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6137 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6139 if (element == EL_MOLE)
6140 printf("::: %d, %d, %d [%d]\n",
6141 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6145 if (element == EL_YAMYAM)
6146 printf("::: %d, %d, %d\n",
6147 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6151 if (IS_ANIMATED(graphic) &&
6155 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6158 if (element == EL_MOLE)
6159 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6163 if (IS_GEM(element) || element == EL_SP_INFOTRON)
6164 EdelsteinFunkeln(x, y);
6166 else if ((element == EL_ACID ||
6167 element == EL_EXIT_OPEN ||
6168 element == EL_SP_EXIT_OPEN ||
6169 element == EL_SP_TERMINAL ||
6170 element == EL_SP_TERMINAL_ACTIVE ||
6171 element == EL_EXTRA_TIME ||
6172 element == EL_SHIELD_NORMAL ||
6173 element == EL_SHIELD_DEADLY) &&
6174 IS_ANIMATED(graphic))
6175 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6176 else if (IS_MOVING(x, y))
6177 ContinueMoving(x, y);
6178 else if (IS_ACTIVE_BOMB(element))
6179 CheckDynamite(x, y);
6181 else if (element == EL_EXPLOSION && !game.explosions_delayed)
6182 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6184 else if (element == EL_AMOEBA_GROWING)
6185 AmoebeWaechst(x, y);
6186 else if (element == EL_AMOEBA_SHRINKING)
6187 AmoebaDisappearing(x, y);
6189 #if !USE_NEW_AMOEBA_CODE
6190 else if (IS_AMOEBALIVE(element))
6191 AmoebeAbleger(x, y);
6194 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6196 else if (element == EL_EXIT_CLOSED)
6198 else if (element == EL_SP_EXIT_CLOSED)
6200 else if (element == EL_EXPANDABLE_WALL_GROWING)
6202 else if (element == EL_EXPANDABLE_WALL ||
6203 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6204 element == EL_EXPANDABLE_WALL_VERTICAL ||
6205 element == EL_EXPANDABLE_WALL_ANY)
6207 else if (element == EL_FLAMES)
6208 CheckForDragon(x, y);
6210 else if (IS_AUTO_CHANGING(element))
6211 ChangeElement(x, y);
6213 else if (element == EL_EXPLOSION)
6214 ; /* drawing of correct explosion animation is handled separately */
6215 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6216 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6219 /* this may take place after moving, so 'element' may have changed */
6220 if (IS_AUTO_CHANGING(Feld[x][y]))
6221 ChangeElement(x, y);
6224 if (IS_BELT_ACTIVE(element))
6225 PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6227 if (game.magic_wall_active)
6229 int jx = local_player->jx, jy = local_player->jy;
6231 /* play the element sound at the position nearest to the player */
6232 if ((element == EL_MAGIC_WALL_FULL ||
6233 element == EL_MAGIC_WALL_ACTIVE ||
6234 element == EL_MAGIC_WALL_EMPTYING ||
6235 element == EL_BD_MAGIC_WALL_FULL ||
6236 element == EL_BD_MAGIC_WALL_ACTIVE ||
6237 element == EL_BD_MAGIC_WALL_EMPTYING) &&
6238 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6246 #if USE_NEW_AMOEBA_CODE
6247 /* new experimental amoeba growth stuff */
6249 if (!(FrameCounter % 8))
6252 static unsigned long random = 1684108901;
6254 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6257 x = (random >> 10) % lev_fieldx;
6258 y = (random >> 20) % lev_fieldy;
6260 x = RND(lev_fieldx);
6261 y = RND(lev_fieldy);
6263 element = Feld[x][y];
6265 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6266 if (!IS_PLAYER(x,y) &&
6267 (element == EL_EMPTY ||
6268 element == EL_SAND ||
6269 element == EL_QUICKSAND_EMPTY ||
6270 element == EL_ACID_SPLASH_LEFT ||
6271 element == EL_ACID_SPLASH_RIGHT))
6273 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6274 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6275 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6276 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6277 Feld[x][y] = EL_AMOEBA_DROP;
6280 random = random * 129 + 1;
6286 if (game.explosions_delayed)
6289 game.explosions_delayed = FALSE;
6291 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6293 element = Feld[x][y];
6295 if (ExplodeField[x][y])
6296 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6297 else if (element == EL_EXPLOSION)
6298 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6300 ExplodeField[x][y] = EX_NO_EXPLOSION;
6303 game.explosions_delayed = TRUE;
6306 if (game.magic_wall_active)
6308 if (!(game.magic_wall_time_left % 4))
6310 int element = Feld[magic_wall_x][magic_wall_y];
6312 if (element == EL_BD_MAGIC_WALL_FULL ||
6313 element == EL_BD_MAGIC_WALL_ACTIVE ||
6314 element == EL_BD_MAGIC_WALL_EMPTYING)
6315 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6317 PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6320 if (game.magic_wall_time_left > 0)
6322 game.magic_wall_time_left--;
6323 if (!game.magic_wall_time_left)
6325 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6327 element = Feld[x][y];
6329 if (element == EL_MAGIC_WALL_ACTIVE ||
6330 element == EL_MAGIC_WALL_FULL)
6332 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6333 DrawLevelField(x, y);
6335 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6336 element == EL_BD_MAGIC_WALL_FULL)
6338 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6339 DrawLevelField(x, y);
6343 game.magic_wall_active = FALSE;
6348 if (game.light_time_left > 0)
6350 game.light_time_left--;
6352 if (game.light_time_left == 0)
6353 RedrawAllLightSwitchesAndInvisibleElements();
6356 if (game.timegate_time_left > 0)
6358 game.timegate_time_left--;
6360 if (game.timegate_time_left == 0)
6361 CloseAllOpenTimegates();
6364 for (i=0; i<MAX_PLAYERS; i++)
6366 struct PlayerInfo *player = &stored_player[i];
6368 if (SHIELD_ON(player))
6370 if (player->shield_deadly_time_left)
6371 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6372 else if (player->shield_normal_time_left)
6373 PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6377 if (TimeFrames >= FRAMES_PER_SECOND)
6382 for (i=0; i<MAX_PLAYERS; i++)
6384 struct PlayerInfo *player = &stored_player[i];
6386 if (SHIELD_ON(player))
6388 player->shield_normal_time_left--;
6390 if (player->shield_deadly_time_left > 0)
6391 player->shield_deadly_time_left--;
6395 if (tape.recording || tape.playing)
6396 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6402 if (TimeLeft <= 10 && setup.time_limit)
6403 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6405 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6407 if (!TimeLeft && setup.time_limit)
6408 for (i=0; i<MAX_PLAYERS; i++)
6409 KillHero(&stored_player[i]);
6411 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6412 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6417 if (options.debug) /* calculate frames per second */
6419 static unsigned long fps_counter = 0;
6420 static int fps_frames = 0;
6421 unsigned long fps_delay_ms = Counter() - fps_counter;
6425 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
6427 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6430 fps_counter = Counter();
6433 redraw_mask |= REDRAW_FPS;
6437 if (stored_player[0].jx != stored_player[0].last_jx ||
6438 stored_player[0].jy != stored_player[0].last_jy)
6439 printf("::: %d, %d, %d, %d, %d\n",
6440 stored_player[0].MovDir,
6441 stored_player[0].MovPos,
6442 stored_player[0].GfxPos,
6443 stored_player[0].Frame,
6444 stored_player[0].StepFrame);
6451 for (i=0; i<MAX_PLAYERS; i++)
6454 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
6456 stored_player[i].Frame += move_frames;
6458 if (stored_player[i].MovPos != 0)
6459 stored_player[i].StepFrame += move_frames;
6464 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6466 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6468 local_player->show_envelope = 0;
6473 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6475 int min_x = x, min_y = y, max_x = x, max_y = y;
6478 for (i=0; i<MAX_PLAYERS; i++)
6480 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6482 if (!stored_player[i].active || &stored_player[i] == player)
6485 min_x = MIN(min_x, jx);
6486 min_y = MIN(min_y, jy);
6487 max_x = MAX(max_x, jx);
6488 max_y = MAX(max_y, jy);
6491 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6494 static boolean AllPlayersInVisibleScreen()
6498 for (i=0; i<MAX_PLAYERS; i++)
6500 int jx = stored_player[i].jx, jy = stored_player[i].jy;
6502 if (!stored_player[i].active)
6505 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6512 void ScrollLevel(int dx, int dy)
6514 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6517 BlitBitmap(drawto_field, drawto_field,
6518 FX + TILEX * (dx == -1) - softscroll_offset,
6519 FY + TILEY * (dy == -1) - softscroll_offset,
6520 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6521 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6522 FX + TILEX * (dx == 1) - softscroll_offset,
6523 FY + TILEY * (dy == 1) - softscroll_offset);
6527 x = (dx == 1 ? BX1 : BX2);
6528 for (y=BY1; y <= BY2; y++)
6529 DrawScreenField(x, y);
6534 y = (dy == 1 ? BY1 : BY2);
6535 for (x=BX1; x <= BX2; x++)
6536 DrawScreenField(x, y);
6539 redraw_mask |= REDRAW_FIELD;
6542 static void CheckGravityMovement(struct PlayerInfo *player)
6544 if (game.gravity && !player->programmed_action)
6546 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6547 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6549 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6550 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6551 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6552 int jx = player->jx, jy = player->jy;
6553 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6554 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6555 int new_jx = jx + dx, new_jy = jy + dy;
6556 boolean field_under_player_is_free =
6557 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6558 boolean player_is_moving_to_valid_field =
6559 (IN_LEV_FIELD(new_jx, new_jy) &&
6560 (Feld[new_jx][new_jy] == EL_SP_BASE ||
6561 Feld[new_jx][new_jy] == EL_SAND));
6562 /* !!! extend EL_SAND to anything diggable !!! */
6564 if (field_under_player_is_free &&
6565 !player_is_moving_to_valid_field &&
6566 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6567 player->programmed_action = MV_DOWN;
6573 -----------------------------------------------------------------------------
6574 dx, dy: direction (non-diagonal) to try to move the player to
6575 real_dx, real_dy: direction as read from input device (can be diagonal)
6578 boolean MovePlayerOneStep(struct PlayerInfo *player,
6579 int dx, int dy, int real_dx, int real_dy)
6582 static int change_sides[4][2] =
6584 /* enter side leave side */
6585 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6586 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6587 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6588 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6590 int move_direction = (dx == -1 ? MV_LEFT :
6591 dx == +1 ? MV_RIGHT :
6593 dy == +1 ? MV_DOWN : MV_NO_MOVING);
6594 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6595 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6597 int jx = player->jx, jy = player->jy;
6598 int new_jx = jx + dx, new_jy = jy + dy;
6602 if (!player->active || (!dx && !dy))
6603 return MF_NO_ACTION;
6605 player->MovDir = (dx < 0 ? MV_LEFT :
6608 dy > 0 ? MV_DOWN : MV_NO_MOVING);
6610 if (!IN_LEV_FIELD(new_jx, new_jy))
6611 return MF_NO_ACTION;
6613 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6614 return MF_NO_ACTION;
6617 element = MovingOrBlocked2Element(new_jx, new_jy);
6619 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6622 if (DONT_RUN_INTO(element))
6624 if (element == EL_ACID && dx == 0 && dy == 1)
6627 Feld[jx][jy] = EL_PLAYER_1;
6628 InitMovingField(jx, jy, MV_DOWN);
6629 Store[jx][jy] = EL_ACID;
6630 ContinueMoving(jx, jy);
6634 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6639 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6640 if (can_move != MF_MOVING)
6643 /* check if DigField() has caused relocation of the player */
6644 if (player->jx != jx || player->jy != jy)
6645 return MF_NO_ACTION;
6647 StorePlayer[jx][jy] = 0;
6648 player->last_jx = jx;
6649 player->last_jy = jy;
6650 player->jx = new_jx;
6651 player->jy = new_jy;
6652 StorePlayer[new_jx][new_jy] = player->element_nr;
6655 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6657 ScrollPlayer(player, SCROLL_INIT);
6660 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6662 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6663 CE_OTHER_GETS_LEFT);
6664 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6665 CE_LEFT_BY_PLAYER, -1);
6668 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6670 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6671 enter_side, CE_OTHER_GETS_ENTERED);
6672 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6673 CE_ENTERED_BY_PLAYER, -1);
6680 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6682 int jx = player->jx, jy = player->jy;
6683 int old_jx = jx, old_jy = jy;
6684 int moved = MF_NO_ACTION;
6686 if (!player->active || (!dx && !dy))
6690 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6694 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6695 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6699 /* remove the last programmed player action */
6700 player->programmed_action = 0;
6704 /* should only happen if pre-1.2 tape recordings are played */
6705 /* this is only for backward compatibility */
6707 int original_move_delay_value = player->move_delay_value;
6710 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6714 /* scroll remaining steps with finest movement resolution */
6715 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6717 while (player->MovPos)
6719 ScrollPlayer(player, SCROLL_GO_ON);
6720 ScrollScreen(NULL, SCROLL_GO_ON);
6726 player->move_delay_value = original_move_delay_value;
6729 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6731 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6732 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6736 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6737 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6743 if (moved & MF_MOVING && !ScreenMovPos &&
6744 (player == local_player || !options.network))
6746 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6747 int offset = (setup.scroll_delay ? 3 : 0);
6749 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6751 /* actual player has left the screen -- scroll in that direction */
6752 if (jx != old_jx) /* player has moved horizontally */
6753 scroll_x += (jx - old_jx);
6754 else /* player has moved vertically */
6755 scroll_y += (jy - old_jy);
6759 if (jx != old_jx) /* player has moved horizontally */
6761 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6762 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6763 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6765 /* don't scroll over playfield boundaries */
6766 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6767 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6769 /* don't scroll more than one field at a time */
6770 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6772 /* don't scroll against the player's moving direction */
6773 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6774 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6775 scroll_x = old_scroll_x;
6777 else /* player has moved vertically */
6779 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6780 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6781 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6783 /* don't scroll over playfield boundaries */
6784 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6785 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6787 /* don't scroll more than one field at a time */
6788 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6790 /* don't scroll against the player's moving direction */
6791 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6792 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6793 scroll_y = old_scroll_y;
6797 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6799 if (!options.network && !AllPlayersInVisibleScreen())
6801 scroll_x = old_scroll_x;
6802 scroll_y = old_scroll_y;
6806 ScrollScreen(player, SCROLL_INIT);
6807 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6814 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6816 if (!(moved & MF_MOVING) && !player->is_pushing)
6821 player->StepFrame = 0;
6823 if (moved & MF_MOVING)
6825 if (old_jx != jx && old_jy == jy)
6826 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6827 else if (old_jx == jx && old_jy != jy)
6828 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6830 DrawLevelField(jx, jy); /* for "crumbled sand" */
6832 player->last_move_dir = player->MovDir;
6833 player->is_moving = TRUE;
6835 player->is_snapping = FALSE;
6839 player->is_switching = FALSE;
6845 static int change_sides[4][2] =
6847 /* enter side leave side */
6848 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
6849 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
6850 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
6851 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
6853 int move_direction = player->MovDir;
6854 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6855 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6858 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6860 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6861 leave_side, CE_OTHER_GETS_LEFT);
6862 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6863 leave_side, CE_LEFT_BY_PLAYER, -1);
6866 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6868 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6869 enter_side, CE_OTHER_GETS_ENTERED);
6870 CheckElementSideChange(jx, jy, Feld[jx][jy],
6871 enter_side, CE_ENTERED_BY_PLAYER, -1);
6882 CheckGravityMovement(player);
6885 player->last_move_dir = MV_NO_MOVING;
6887 player->is_moving = FALSE;
6890 if (game.engine_version < VERSION_IDENT(3,0,7))
6892 TestIfHeroTouchesBadThing(jx, jy);
6893 TestIfPlayerTouchesCustomElement(jx, jy);
6896 if (!player->active)
6902 void ScrollPlayer(struct PlayerInfo *player, int mode)
6904 int jx = player->jx, jy = player->jy;
6905 int last_jx = player->last_jx, last_jy = player->last_jy;
6906 int move_stepsize = TILEX / player->move_delay_value;
6908 if (!player->active || !player->MovPos)
6911 if (mode == SCROLL_INIT)
6913 player->actual_frame_counter = FrameCounter;
6914 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6916 if (Feld[last_jx][last_jy] == EL_EMPTY)
6917 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6924 else if (!FrameReached(&player->actual_frame_counter, 1))
6927 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6928 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6930 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
6931 Feld[last_jx][last_jy] = EL_EMPTY;
6933 /* before DrawPlayer() to draw correct player graphic for this case */
6934 if (player->MovPos == 0)
6935 CheckGravityMovement(player);
6938 DrawPlayer(player); /* needed here only to cleanup last field */
6941 if (player->MovPos == 0) /* player reached destination field */
6943 if (IS_PASSABLE(Feld[last_jx][last_jy]))
6945 /* continue with normal speed after quickly moving through gate */
6946 HALVE_PLAYER_SPEED(player);
6948 /* be able to make the next move without delay */
6949 player->move_delay = 0;
6952 player->last_jx = jx;
6953 player->last_jy = jy;
6955 if (Feld[jx][jy] == EL_EXIT_OPEN ||
6956 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
6957 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
6959 DrawPlayer(player); /* needed here only to cleanup last field */
6962 if (local_player->friends_still_needed == 0 ||
6963 IS_SP_ELEMENT(Feld[jx][jy]))
6964 player->LevelSolved = player->GameOver = TRUE;
6967 if (game.engine_version >= VERSION_IDENT(3,0,7))
6969 TestIfHeroTouchesBadThing(jx, jy);
6970 TestIfPlayerTouchesCustomElement(jx, jy);
6972 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
6975 if (!player->active)
6979 if (tape.single_step && tape.recording && !tape.pausing &&
6980 !player->programmed_action)
6981 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6985 void ScrollScreen(struct PlayerInfo *player, int mode)
6987 static unsigned long screen_frame_counter = 0;
6989 if (mode == SCROLL_INIT)
6991 /* set scrolling step size according to actual player's moving speed */
6992 ScrollStepSize = TILEX / player->move_delay_value;
6994 screen_frame_counter = FrameCounter;
6995 ScreenMovDir = player->MovDir;
6996 ScreenMovPos = player->MovPos;
6997 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7000 else if (!FrameReached(&screen_frame_counter, 1))
7005 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7006 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7007 redraw_mask |= REDRAW_FIELD;
7010 ScreenMovDir = MV_NO_MOVING;
7013 void TestIfPlayerTouchesCustomElement(int x, int y)
7015 static int xy[4][2] =
7022 static int change_sides[4][2] =
7024 /* center side border side */
7025 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7026 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7027 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7028 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7030 static int touch_dir[4] =
7037 int center_element = Feld[x][y]; /* should always be non-moving! */
7042 int xx = x + xy[i][0];
7043 int yy = y + xy[i][1];
7044 int center_side = change_sides[i][0];
7045 int border_side = change_sides[i][1];
7048 if (!IN_LEV_FIELD(xx, yy))
7051 if (IS_PLAYER(x, y))
7053 if (game.engine_version < VERSION_IDENT(3,0,7))
7054 border_element = Feld[xx][yy]; /* may be moving! */
7055 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7056 border_element = Feld[xx][yy];
7057 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7058 border_element = MovingOrBlocked2Element(xx, yy);
7060 continue; /* center and border element do not touch */
7062 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7063 CE_OTHER_GETS_TOUCHED);
7064 CheckElementSideChange(xx, yy, border_element, border_side,
7065 CE_TOUCHED_BY_PLAYER, -1);
7067 else if (IS_PLAYER(xx, yy))
7069 if (game.engine_version >= VERSION_IDENT(3,0,7))
7071 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7073 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7074 continue; /* center and border element do not touch */
7077 CheckTriggeredElementSideChange(x, y, center_element, center_side,
7078 CE_OTHER_GETS_TOUCHED);
7079 CheckElementSideChange(x, y, center_element, center_side,
7080 CE_TOUCHED_BY_PLAYER, -1);
7087 void TestIfElementTouchesCustomElement(int x, int y)
7089 static int xy[4][2] =
7096 static int change_sides[4][2] =
7098 /* center side border side */
7099 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7100 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7101 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7102 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7104 static int touch_dir[4] =
7111 boolean change_center_element = FALSE;
7112 int center_element_change_page = 0;
7113 int center_element = Feld[x][y]; /* should always be non-moving! */
7118 int xx = x + xy[i][0];
7119 int yy = y + xy[i][1];
7120 int center_side = change_sides[i][0];
7121 int border_side = change_sides[i][1];
7124 if (!IN_LEV_FIELD(xx, yy))
7127 if (game.engine_version < VERSION_IDENT(3,0,7))
7128 border_element = Feld[xx][yy]; /* may be moving! */
7129 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7130 border_element = Feld[xx][yy];
7131 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7132 border_element = MovingOrBlocked2Element(xx, yy);
7134 continue; /* center and border element do not touch */
7136 /* check for change of center element (but change it only once) */
7137 if (IS_CUSTOM_ELEMENT(center_element) &&
7138 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7139 !change_center_element)
7141 for (j=0; j < element_info[center_element].num_change_pages; j++)
7143 struct ElementChangeInfo *change =
7144 &element_info[center_element].change_page[j];
7146 if (change->can_change &&
7147 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7148 change->sides & border_side &&
7149 change->trigger_element == border_element)
7151 change_center_element = TRUE;
7152 center_element_change_page = j;
7159 /* check for change of border element */
7160 if (IS_CUSTOM_ELEMENT(border_element) &&
7161 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7163 for (j=0; j < element_info[border_element].num_change_pages; j++)
7165 struct ElementChangeInfo *change =
7166 &element_info[border_element].change_page[j];
7168 if (change->can_change &&
7169 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7170 change->sides & center_side &&
7171 change->trigger_element == center_element)
7173 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7174 CE_OTHER_IS_TOUCHING, j);
7181 if (change_center_element)
7182 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7183 CE_OTHER_IS_TOUCHING, center_element_change_page);
7186 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7188 int i, kill_x = -1, kill_y = -1;
7189 static int test_xy[4][2] =
7196 static int test_dir[4] =
7206 int test_x, test_y, test_move_dir, test_element;
7208 test_x = good_x + test_xy[i][0];
7209 test_y = good_y + test_xy[i][1];
7210 if (!IN_LEV_FIELD(test_x, test_y))
7214 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7217 test_element = Feld[test_x][test_y];
7219 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7222 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7223 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7225 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7226 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
7234 if (kill_x != -1 || kill_y != -1)
7236 if (IS_PLAYER(good_x, good_y))
7238 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7240 if (player->shield_deadly_time_left > 0)
7241 Bang(kill_x, kill_y);
7242 else if (!PLAYER_PROTECTED(good_x, good_y))
7246 Bang(good_x, good_y);
7250 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7252 int i, kill_x = -1, kill_y = -1;
7253 int bad_element = Feld[bad_x][bad_y];
7254 static int test_xy[4][2] =
7261 static int touch_dir[4] =
7268 static int test_dir[4] =
7276 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
7281 int test_x, test_y, test_move_dir, test_element;
7283 test_x = bad_x + test_xy[i][0];
7284 test_y = bad_y + test_xy[i][1];
7285 if (!IN_LEV_FIELD(test_x, test_y))
7289 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7291 test_element = Feld[test_x][test_y];
7293 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7294 2nd case: DONT_TOUCH style bad thing does not move away from good thing
7296 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
7297 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
7299 /* good thing is player or penguin that does not move away */
7300 if (IS_PLAYER(test_x, test_y))
7302 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7304 if (bad_element == EL_ROBOT && player->is_moving)
7305 continue; /* robot does not kill player if he is moving */
7307 if (game.engine_version >= VERSION_IDENT(3,0,7))
7309 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7310 continue; /* center and border element do not touch */
7317 else if (test_element == EL_PENGUIN)
7326 if (kill_x != -1 || kill_y != -1)
7328 if (IS_PLAYER(kill_x, kill_y))
7330 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7332 if (player->shield_deadly_time_left > 0)
7334 else if (!PLAYER_PROTECTED(kill_x, kill_y))
7338 Bang(kill_x, kill_y);
7342 void TestIfHeroTouchesBadThing(int x, int y)
7344 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7347 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7349 TestIfGoodThingHitsBadThing(x, y, move_dir);
7352 void TestIfBadThingTouchesHero(int x, int y)
7354 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7357 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7359 TestIfBadThingHitsGoodThing(x, y, move_dir);
7362 void TestIfFriendTouchesBadThing(int x, int y)
7364 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7367 void TestIfBadThingTouchesFriend(int x, int y)
7369 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7372 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7374 int i, kill_x = bad_x, kill_y = bad_y;
7375 static int xy[4][2] =
7387 x = bad_x + xy[i][0];
7388 y = bad_y + xy[i][1];
7389 if (!IN_LEV_FIELD(x, y))
7392 element = Feld[x][y];
7393 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7394 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7402 if (kill_x != bad_x || kill_y != bad_y)
7406 void KillHero(struct PlayerInfo *player)
7408 int jx = player->jx, jy = player->jy;
7410 if (!player->active)
7413 /* remove accessible field at the player's position */
7414 Feld[jx][jy] = EL_EMPTY;
7416 /* deactivate shield (else Bang()/Explode() would not work right) */
7417 player->shield_normal_time_left = 0;
7418 player->shield_deadly_time_left = 0;
7424 static void KillHeroUnlessProtected(int x, int y)
7426 if (!PLAYER_PROTECTED(x, y))
7427 KillHero(PLAYERINFO(x, y));
7430 void BuryHero(struct PlayerInfo *player)
7432 int jx = player->jx, jy = player->jy;
7434 if (!player->active)
7438 PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7440 PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7442 PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7444 player->GameOver = TRUE;
7448 void RemoveHero(struct PlayerInfo *player)
7450 int jx = player->jx, jy = player->jy;
7451 int i, found = FALSE;
7453 player->present = FALSE;
7454 player->active = FALSE;
7456 if (!ExplodeField[jx][jy])
7457 StorePlayer[jx][jy] = 0;
7459 for (i=0; i<MAX_PLAYERS; i++)
7460 if (stored_player[i].active)
7464 AllPlayersGone = TRUE;
7471 =============================================================================
7472 checkDiagonalPushing()
7473 -----------------------------------------------------------------------------
7474 check if diagonal input device direction results in pushing of object
7475 (by checking if the alternative direction is walkable, diggable, ...)
7476 =============================================================================
7479 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7480 int x, int y, int real_dx, int real_dy)
7482 int jx, jy, dx, dy, xx, yy;
7484 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
7487 /* diagonal direction: check alternative direction */
7492 xx = jx + (dx == 0 ? real_dx : 0);
7493 yy = jy + (dy == 0 ? real_dy : 0);
7495 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7499 =============================================================================
7501 -----------------------------------------------------------------------------
7502 x, y: field next to player (non-diagonal) to try to dig to
7503 real_dx, real_dy: direction as read from input device (can be diagonal)
7504 =============================================================================
7507 int DigField(struct PlayerInfo *player,
7508 int x, int y, int real_dx, int real_dy, int mode)
7510 static int change_sides[4] =
7512 CH_SIDE_RIGHT, /* moving left */
7513 CH_SIDE_LEFT, /* moving right */
7514 CH_SIDE_BOTTOM, /* moving up */
7515 CH_SIDE_TOP, /* moving down */
7517 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
7518 int jx = player->jx, jy = player->jy;
7519 int dx = x - jx, dy = y - jy;
7520 int nextx = x + dx, nexty = y + dy;
7521 int move_direction = (dx == -1 ? MV_LEFT :
7522 dx == +1 ? MV_RIGHT :
7524 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7525 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7528 if (player->MovPos == 0)
7530 player->is_digging = FALSE;
7531 player->is_collecting = FALSE;
7534 if (player->MovPos == 0) /* last pushing move finished */
7535 player->is_pushing = FALSE;
7537 if (mode == DF_NO_PUSH) /* player just stopped pushing */
7539 player->is_switching = FALSE;
7540 player->push_delay = 0;
7542 return MF_NO_ACTION;
7545 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7546 return MF_NO_ACTION;
7549 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7551 if (IS_TUBE(Feld[jx][jy]) ||
7552 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0)))
7556 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7557 int tube_leave_directions[][2] =
7559 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7560 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7561 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7562 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
7563 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
7564 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
7565 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
7566 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
7567 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
7568 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
7569 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
7570 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7573 while (tube_leave_directions[i][0] != tube_element)
7576 if (tube_leave_directions[i][0] == -1) /* should not happen */
7580 if (!(tube_leave_directions[i][1] & move_direction))
7581 return MF_NO_ACTION; /* tube has no opening in this direction */
7584 element = Feld[x][y];
7586 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7587 game.engine_version >= VERSION_IDENT(2,2,0))
7588 return MF_NO_ACTION;
7592 case EL_SP_PORT_LEFT:
7593 case EL_SP_PORT_RIGHT:
7595 case EL_SP_PORT_DOWN:
7596 case EL_SP_PORT_HORIZONTAL:
7597 case EL_SP_PORT_VERTICAL:
7598 case EL_SP_PORT_ANY:
7599 case EL_SP_GRAVITY_PORT_LEFT:
7600 case EL_SP_GRAVITY_PORT_RIGHT:
7601 case EL_SP_GRAVITY_PORT_UP:
7602 case EL_SP_GRAVITY_PORT_DOWN:
7604 element != EL_SP_PORT_LEFT &&
7605 element != EL_SP_GRAVITY_PORT_LEFT &&
7606 element != EL_SP_PORT_HORIZONTAL &&
7607 element != EL_SP_PORT_ANY) ||
7609 element != EL_SP_PORT_RIGHT &&
7610 element != EL_SP_GRAVITY_PORT_RIGHT &&
7611 element != EL_SP_PORT_HORIZONTAL &&
7612 element != EL_SP_PORT_ANY) ||
7614 element != EL_SP_PORT_UP &&
7615 element != EL_SP_GRAVITY_PORT_UP &&
7616 element != EL_SP_PORT_VERTICAL &&
7617 element != EL_SP_PORT_ANY) ||
7619 element != EL_SP_PORT_DOWN &&
7620 element != EL_SP_GRAVITY_PORT_DOWN &&
7621 element != EL_SP_PORT_VERTICAL &&
7622 element != EL_SP_PORT_ANY) ||
7623 !IN_LEV_FIELD(nextx, nexty) ||
7624 !IS_FREE(nextx, nexty))
7625 return MF_NO_ACTION;
7627 if (element == EL_SP_GRAVITY_PORT_LEFT ||
7628 element == EL_SP_GRAVITY_PORT_RIGHT ||
7629 element == EL_SP_GRAVITY_PORT_UP ||
7630 element == EL_SP_GRAVITY_PORT_DOWN)
7631 game.gravity = !game.gravity;
7633 /* automatically move to the next field with double speed */
7634 player->programmed_action = move_direction;
7635 DOUBLE_PLAYER_SPEED(player);
7637 PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7641 case EL_TUBE_VERTICAL:
7642 case EL_TUBE_HORIZONTAL:
7643 case EL_TUBE_VERTICAL_LEFT:
7644 case EL_TUBE_VERTICAL_RIGHT:
7645 case EL_TUBE_HORIZONTAL_UP:
7646 case EL_TUBE_HORIZONTAL_DOWN:
7647 case EL_TUBE_LEFT_UP:
7648 case EL_TUBE_LEFT_DOWN:
7649 case EL_TUBE_RIGHT_UP:
7650 case EL_TUBE_RIGHT_DOWN:
7653 int tube_enter_directions[][2] =
7655 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7656 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
7657 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
7658 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
7659 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
7660 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
7661 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
7662 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
7663 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
7664 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
7665 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
7666 { -1, MV_NO_MOVING }
7669 while (tube_enter_directions[i][0] != element)
7672 if (tube_enter_directions[i][0] == -1) /* should not happen */
7676 if (!(tube_enter_directions[i][1] & move_direction))
7677 return MF_NO_ACTION; /* tube has no opening in this direction */
7679 PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7685 if (IS_WALKABLE(element))
7687 int sound_action = ACTION_WALKING;
7689 if (element >= EL_GATE_1 && element <= EL_GATE_4)
7691 if (!player->key[element - EL_GATE_1])
7692 return MF_NO_ACTION;
7694 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7696 if (!player->key[element - EL_GATE_1_GRAY])
7697 return MF_NO_ACTION;
7699 else if (element == EL_EXIT_OPEN ||
7700 element == EL_SP_EXIT_OPEN ||
7701 element == EL_SP_EXIT_OPENING)
7703 sound_action = ACTION_PASSING; /* player is passing exit */
7705 else if (element == EL_EMPTY)
7707 sound_action = ACTION_MOVING; /* nothing to walk on */
7710 /* play sound from background or player, whatever is available */
7711 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7712 PlaySoundLevelElementAction(x, y, element, sound_action);
7714 PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7718 else if (IS_PASSABLE(element))
7720 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7721 return MF_NO_ACTION;
7724 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
7725 return MF_NO_ACTION;
7728 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7730 if (!player->key[element - EL_EM_GATE_1])
7731 return MF_NO_ACTION;
7733 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7735 if (!player->key[element - EL_EM_GATE_1_GRAY])
7736 return MF_NO_ACTION;
7739 /* automatically move to the next field with double speed */
7740 player->programmed_action = move_direction;
7741 DOUBLE_PLAYER_SPEED(player);
7743 PlaySoundLevelAction(x, y, ACTION_PASSING);
7747 else if (IS_DIGGABLE(element))
7751 if (mode != DF_SNAP)
7754 GfxElement[x][y] = GFX_ELEMENT(element);
7757 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7759 player->is_digging = TRUE;
7762 PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7764 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7767 if (mode == DF_SNAP)
7768 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7773 else if (IS_COLLECTIBLE(element))
7777 if (mode != DF_SNAP)
7779 GfxElement[x][y] = element;
7780 player->is_collecting = TRUE;
7783 if (element == EL_SPEED_PILL)
7784 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7785 else if (element == EL_EXTRA_TIME && level.time > 0)
7788 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7790 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7792 player->shield_normal_time_left += 10;
7793 if (element == EL_SHIELD_DEADLY)
7794 player->shield_deadly_time_left += 10;
7796 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7798 if (player->inventory_size < MAX_INVENTORY_SIZE)
7799 player->inventory_element[player->inventory_size++] = element;
7801 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7802 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7804 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7806 player->dynabomb_count++;
7807 player->dynabombs_left++;
7809 else if (element == EL_DYNABOMB_INCREASE_SIZE)
7811 player->dynabomb_size++;
7813 else if (element == EL_DYNABOMB_INCREASE_POWER)
7815 player->dynabomb_xl = TRUE;
7817 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7818 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7820 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7821 element - EL_KEY_1 : element - EL_EM_KEY_1);
7823 player->key[key_nr] = TRUE;
7825 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7826 el2edimg(EL_KEY_1 + key_nr));
7827 redraw_mask |= REDRAW_DOOR_1;
7829 else if (IS_ENVELOPE(element))
7832 player->show_envelope = element;
7834 ShowEnvelope(element - EL_ENVELOPE_1);
7837 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7841 for (i=0; i < element_info[element].collect_count; i++)
7842 if (player->inventory_size < MAX_INVENTORY_SIZE)
7843 player->inventory_element[player->inventory_size++] = element;
7845 DrawText(DX_DYNAMITE, DY_DYNAMITE,
7846 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7848 else if (element_info[element].collect_count > 0)
7850 local_player->gems_still_needed -=
7851 element_info[element].collect_count;
7852 if (local_player->gems_still_needed < 0)
7853 local_player->gems_still_needed = 0;
7855 DrawText(DX_EMERALDS, DY_EMERALDS,
7856 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7859 RaiseScoreElement(element);
7860 PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7862 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7865 if (mode == DF_SNAP)
7866 TestIfElementTouchesCustomElement(x, y); /* for empty space */
7871 else if (IS_PUSHABLE(element))
7873 if (mode == DF_SNAP && element != EL_BD_ROCK)
7874 return MF_NO_ACTION;
7876 if (CAN_FALL(element) && dy)
7877 return MF_NO_ACTION;
7879 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7880 !(element == EL_SPRING && use_spring_bug))
7881 return MF_NO_ACTION;
7884 /* do not push elements already moving away faster than player */
7885 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7886 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7887 return MF_NO_ACTION;
7889 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7890 return MF_NO_ACTION;
7892 if (!player->is_pushing &&
7893 game.engine_version >= RELEASE_IDENT(2,2,0,7))
7894 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7896 player->is_pushing = TRUE;
7898 if (!(IN_LEV_FIELD(nextx, nexty) &&
7899 (IS_FREE(nextx, nexty) ||
7900 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
7901 IS_SB_ELEMENT(element)))))
7902 return MF_NO_ACTION;
7904 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
7905 return MF_NO_ACTION;
7907 if (player->push_delay == 0) /* new pushing; restart delay */
7908 player->push_delay = FrameCounter;
7910 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
7911 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
7912 element != EL_SPRING && element != EL_BALLOON)
7914 /* make sure that there is no move delay before next try to push */
7915 if (game.engine_version >= VERSION_IDENT(3,0,7))
7916 player->move_delay = INITIAL_MOVE_DELAY_OFF;
7918 return MF_NO_ACTION;
7921 if (IS_SB_ELEMENT(element))
7923 if (element == EL_SOKOBAN_FIELD_FULL)
7925 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
7926 local_player->sokobanfields_still_needed++;
7929 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
7931 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
7932 local_player->sokobanfields_still_needed--;
7935 Feld[x][y] = EL_SOKOBAN_OBJECT;
7937 if (Back[x][y] == Back[nextx][nexty])
7938 PlaySoundLevelAction(x, y, ACTION_PUSHING);
7939 else if (Back[x][y] != 0)
7940 PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
7943 PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
7946 if (local_player->sokobanfields_still_needed == 0 &&
7947 game.emulation == EMU_SOKOBAN)
7949 player->LevelSolved = player->GameOver = TRUE;
7950 PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
7954 PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
7956 InitMovingField(x, y, move_direction);
7957 GfxAction[x][y] = ACTION_PUSHING;
7959 if (mode == DF_SNAP)
7960 ContinueMoving(x, y);
7962 MovPos[x][y] = (dx != 0 ? dx : dy);
7964 Pushed[x][y] = TRUE;
7965 Pushed[nextx][nexty] = TRUE;
7967 if (game.engine_version < RELEASE_IDENT(2,2,0,7))
7968 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7970 CheckTriggeredElementSideChange(x, y, element, dig_side,
7971 CE_OTHER_GETS_PUSHED);
7972 CheckElementSideChange(x, y, element, dig_side,
7973 CE_PUSHED_BY_PLAYER, -1);
7977 else if (IS_SWITCHABLE(element))
7979 if (PLAYER_SWITCHING(player, x, y))
7982 player->is_switching = TRUE;
7983 player->switch_x = x;
7984 player->switch_y = y;
7986 PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
7988 if (element == EL_ROBOT_WHEEL)
7990 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
7994 DrawLevelField(x, y);
7996 else if (element == EL_SP_TERMINAL)
8000 for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8002 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8004 else if (Feld[xx][yy] == EL_SP_TERMINAL)
8005 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8008 else if (IS_BELT_SWITCH(element))
8010 ToggleBeltSwitch(x, y);
8012 else if (element == EL_SWITCHGATE_SWITCH_UP ||
8013 element == EL_SWITCHGATE_SWITCH_DOWN)
8015 ToggleSwitchgateSwitch(x, y);
8017 else if (element == EL_LIGHT_SWITCH ||
8018 element == EL_LIGHT_SWITCH_ACTIVE)
8020 ToggleLightSwitch(x, y);
8023 PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8024 SND_LIGHT_SWITCH_ACTIVATING :
8025 SND_LIGHT_SWITCH_DEACTIVATING);
8028 else if (element == EL_TIMEGATE_SWITCH)
8030 ActivateTimegateSwitch(x, y);
8032 else if (element == EL_BALLOON_SWITCH_LEFT ||
8033 element == EL_BALLOON_SWITCH_RIGHT ||
8034 element == EL_BALLOON_SWITCH_UP ||
8035 element == EL_BALLOON_SWITCH_DOWN ||
8036 element == EL_BALLOON_SWITCH_ANY)
8038 if (element == EL_BALLOON_SWITCH_ANY)
8039 game.balloon_dir = move_direction;
8041 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
8042 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8043 element == EL_BALLOON_SWITCH_UP ? MV_UP :
8044 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
8047 else if (element == EL_LAMP)
8049 Feld[x][y] = EL_LAMP_ACTIVE;
8050 local_player->lights_still_needed--;
8052 DrawLevelField(x, y);
8054 else if (element == EL_TIME_ORB_FULL)
8056 Feld[x][y] = EL_TIME_ORB_EMPTY;
8058 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8060 DrawLevelField(x, y);
8063 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8071 if (!PLAYER_SWITCHING(player, x, y))
8073 player->is_switching = TRUE;
8074 player->switch_x = x;
8075 player->switch_y = y;
8077 CheckTriggeredElementSideChange(x, y, element, dig_side,
8078 CE_OTHER_IS_SWITCHING);
8079 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8082 CheckTriggeredElementSideChange(x, y, element, dig_side,
8083 CE_OTHER_GETS_PRESSED);
8084 CheckElementSideChange(x, y, element, dig_side,
8085 CE_PRESSED_BY_PLAYER, -1);
8088 return MF_NO_ACTION;
8091 player->push_delay = 0;
8093 if (Feld[x][y] != element) /* really digged/collected something */
8094 player->is_collecting = !player->is_digging;
8099 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8101 int jx = player->jx, jy = player->jy;
8102 int x = jx + dx, y = jy + dy;
8103 int snap_direction = (dx == -1 ? MV_LEFT :
8104 dx == +1 ? MV_RIGHT :
8106 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8108 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
8111 if (!player->active || !IN_LEV_FIELD(x, y))
8119 if (player->MovPos == 0)
8120 player->is_pushing = FALSE;
8122 player->is_snapping = FALSE;
8124 if (player->MovPos == 0)
8126 player->is_moving = FALSE;
8127 player->is_digging = FALSE;
8128 player->is_collecting = FALSE;
8134 if (player->is_snapping)
8137 player->MovDir = snap_direction;
8139 player->is_moving = FALSE;
8140 player->is_digging = FALSE;
8141 player->is_collecting = FALSE;
8143 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8146 player->is_snapping = TRUE;
8148 player->is_moving = FALSE;
8149 player->is_digging = FALSE;
8150 player->is_collecting = FALSE;
8152 DrawLevelField(x, y);
8158 boolean DropElement(struct PlayerInfo *player)
8160 int jx = player->jx, jy = player->jy;
8163 if (!player->active || player->MovPos)
8166 old_element = Feld[jx][jy];
8168 /* check if player has anything that can be dropped */
8169 if (player->inventory_size == 0 && player->dynabombs_left == 0)
8172 /* check if anything can be dropped at the current position */
8173 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8176 /* collected custom elements can only be dropped on empty fields */
8177 if (player->inventory_size > 0 &&
8178 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8179 && old_element != EL_EMPTY)
8182 if (old_element != EL_EMPTY)
8183 Back[jx][jy] = old_element; /* store old element on this field */
8185 MovDelay[jx][jy] = 96;
8187 ResetGfxAnimation(jx, jy);
8188 ResetRandomAnimationValue(jx, jy);
8190 if (player->inventory_size > 0)
8192 int new_element = player->inventory_element[--player->inventory_size];
8194 Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8195 new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8198 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8199 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8201 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8202 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8204 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8206 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8207 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8209 TestIfElementTouchesCustomElement(jx, jy);
8211 else /* player is dropping a dyna bomb */
8213 player->dynabombs_left--;
8216 EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8218 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8219 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8221 PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8227 /* ------------------------------------------------------------------------- */
8228 /* game sound playing functions */
8229 /* ------------------------------------------------------------------------- */
8231 static int *loop_sound_frame = NULL;
8232 static int *loop_sound_volume = NULL;
8234 void InitPlaySoundLevel()
8236 int num_sounds = getSoundListSize();
8238 if (loop_sound_frame != NULL)
8239 free(loop_sound_frame);
8241 if (loop_sound_volume != NULL)
8242 free(loop_sound_volume);
8244 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8245 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8248 static void PlaySoundLevel(int x, int y, int nr)
8250 int sx = SCREENX(x), sy = SCREENY(y);
8251 int volume, stereo_position;
8252 int max_distance = 8;
8253 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8255 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8256 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8259 if (!IN_LEV_FIELD(x, y) ||
8260 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8261 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8264 volume = SOUND_MAX_VOLUME;
8266 if (!IN_SCR_FIELD(sx, sy))
8268 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8269 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8271 volume -= volume * (dx > dy ? dx : dy) / max_distance;
8274 stereo_position = (SOUND_MAX_LEFT +
8275 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8276 (SCR_FIELDX + 2 * max_distance));
8278 if (IS_LOOP_SOUND(nr))
8280 /* This assures that quieter loop sounds do not overwrite louder ones,
8281 while restarting sound volume comparison with each new game frame. */
8283 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8286 loop_sound_volume[nr] = volume;
8287 loop_sound_frame[nr] = FrameCounter;
8290 PlaySoundExt(nr, volume, stereo_position, type);
8293 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8295 PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8296 x > LEVELX(BX2) ? LEVELX(BX2) : x,
8297 y < LEVELY(BY1) ? LEVELY(BY1) :
8298 y > LEVELY(BY2) ? LEVELY(BY2) : y,
8302 static void PlaySoundLevelAction(int x, int y, int action)
8304 PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8307 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8309 int sound_effect = element_info[element].sound[action];
8311 if (sound_effect != SND_UNDEFINED)
8312 PlaySoundLevel(x, y, sound_effect);
8315 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8317 int sound_effect = element_info[Feld[x][y]].sound[action];
8319 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8320 PlaySoundLevel(x, y, sound_effect);
8323 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8325 int sound_effect = element_info[Feld[x][y]].sound[action];
8327 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8328 StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8331 void RaiseScore(int value)
8333 local_player->score += value;
8334 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8337 void RaiseScoreElement(int element)
8343 case EL_EMERALD_YELLOW:
8344 case EL_EMERALD_RED:
8345 case EL_EMERALD_PURPLE:
8346 case EL_SP_INFOTRON:
8347 RaiseScore(level.score[SC_EMERALD]);
8350 RaiseScore(level.score[SC_DIAMOND]);
8353 RaiseScore(level.score[SC_CRYSTAL]);
8356 RaiseScore(level.score[SC_PEARL]);
8359 case EL_BD_BUTTERFLY:
8360 case EL_SP_ELECTRON:
8361 RaiseScore(level.score[SC_BUG]);
8365 case EL_SP_SNIKSNAK:
8366 RaiseScore(level.score[SC_SPACESHIP]);
8369 case EL_DARK_YAMYAM:
8370 RaiseScore(level.score[SC_YAMYAM]);
8373 RaiseScore(level.score[SC_ROBOT]);
8376 RaiseScore(level.score[SC_PACMAN]);
8379 RaiseScore(level.score[SC_NUT]);
8382 case EL_SP_DISK_RED:
8383 case EL_DYNABOMB_INCREASE_NUMBER:
8384 case EL_DYNABOMB_INCREASE_SIZE:
8385 case EL_DYNABOMB_INCREASE_POWER:
8386 RaiseScore(level.score[SC_DYNAMITE]);
8388 case EL_SHIELD_NORMAL:
8389 case EL_SHIELD_DEADLY:
8390 RaiseScore(level.score[SC_SHIELD]);
8393 RaiseScore(level.score[SC_TIME_BONUS]);
8399 RaiseScore(level.score[SC_KEY]);
8402 RaiseScore(element_info[element].collect_score);
8407 void RequestQuitGame(boolean ask_if_really_quit)
8409 if (AllPlayersGone ||
8410 !ask_if_really_quit ||
8411 level_editor_test_game ||
8412 Request("Do you really want to quit the game ?",
8413 REQ_ASK | REQ_STAY_CLOSED))
8415 #if defined(PLATFORM_UNIX)
8416 if (options.network)
8417 SendToServer_StopPlaying();
8421 game_status = GAME_MODE_MAIN;
8427 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8432 /* ---------- new game button stuff ---------------------------------------- */
8434 /* graphic position values for game buttons */
8435 #define GAME_BUTTON_XSIZE 30
8436 #define GAME_BUTTON_YSIZE 30
8437 #define GAME_BUTTON_XPOS 5
8438 #define GAME_BUTTON_YPOS 215
8439 #define SOUND_BUTTON_XPOS 5
8440 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8442 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8443 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8444 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8445 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8446 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8447 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8454 } gamebutton_info[NUM_GAME_BUTTONS] =
8457 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
8462 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
8467 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
8472 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
8473 SOUND_CTRL_ID_MUSIC,
8474 "background music on/off"
8477 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
8478 SOUND_CTRL_ID_LOOPS,
8479 "sound loops on/off"
8482 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
8483 SOUND_CTRL_ID_SIMPLE,
8484 "normal sounds on/off"
8488 void CreateGameButtons()
8492 for (i=0; i<NUM_GAME_BUTTONS; i++)
8494 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8495 struct GadgetInfo *gi;
8498 unsigned long event_mask;
8499 int gd_xoffset, gd_yoffset;
8500 int gd_x1, gd_x2, gd_y1, gd_y2;
8503 gd_xoffset = gamebutton_info[i].x;
8504 gd_yoffset = gamebutton_info[i].y;
8505 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8506 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8508 if (id == GAME_CTRL_ID_STOP ||
8509 id == GAME_CTRL_ID_PAUSE ||
8510 id == GAME_CTRL_ID_PLAY)
8512 button_type = GD_TYPE_NORMAL_BUTTON;
8514 event_mask = GD_EVENT_RELEASED;
8515 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8516 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8520 button_type = GD_TYPE_CHECK_BUTTON;
8522 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8523 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8524 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8525 event_mask = GD_EVENT_PRESSED;
8526 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
8527 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8530 gi = CreateGadget(GDI_CUSTOM_ID, id,
8531 GDI_INFO_TEXT, gamebutton_info[i].infotext,
8532 GDI_X, DX + gd_xoffset,
8533 GDI_Y, DY + gd_yoffset,
8534 GDI_WIDTH, GAME_BUTTON_XSIZE,
8535 GDI_HEIGHT, GAME_BUTTON_YSIZE,
8536 GDI_TYPE, button_type,
8537 GDI_STATE, GD_BUTTON_UNPRESSED,
8538 GDI_CHECKED, checked,
8539 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8540 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8541 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8542 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8543 GDI_EVENT_MASK, event_mask,
8544 GDI_CALLBACK_ACTION, HandleGameButtons,
8548 Error(ERR_EXIT, "cannot create gadget");
8550 game_gadget[id] = gi;
8554 void FreeGameButtons()
8558 for (i=0; i<NUM_GAME_BUTTONS; i++)
8559 FreeGadget(game_gadget[i]);
8562 static void MapGameButtons()
8566 for (i=0; i<NUM_GAME_BUTTONS; i++)
8567 MapGadget(game_gadget[i]);
8570 void UnmapGameButtons()
8574 for (i=0; i<NUM_GAME_BUTTONS; i++)
8575 UnmapGadget(game_gadget[i]);
8578 static void HandleGameButtons(struct GadgetInfo *gi)
8580 int id = gi->custom_id;
8582 if (game_status != GAME_MODE_PLAYING)
8587 case GAME_CTRL_ID_STOP:
8588 RequestQuitGame(TRUE);
8591 case GAME_CTRL_ID_PAUSE:
8592 if (options.network)
8594 #if defined(PLATFORM_UNIX)
8596 SendToServer_ContinuePlaying();
8598 SendToServer_PausePlaying();
8602 TapeTogglePause(TAPE_TOGGLE_MANUAL);
8605 case GAME_CTRL_ID_PLAY:
8608 #if defined(PLATFORM_UNIX)
8609 if (options.network)
8610 SendToServer_ContinuePlaying();
8614 tape.pausing = FALSE;
8615 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8620 case SOUND_CTRL_ID_MUSIC:
8621 if (setup.sound_music)
8623 setup.sound_music = FALSE;
8626 else if (audio.music_available)
8628 setup.sound = setup.sound_music = TRUE;
8630 SetAudioMode(setup.sound);
8631 PlayMusic(level_nr);
8635 case SOUND_CTRL_ID_LOOPS:
8636 if (setup.sound_loops)
8637 setup.sound_loops = FALSE;
8638 else if (audio.loops_available)
8640 setup.sound = setup.sound_loops = TRUE;
8641 SetAudioMode(setup.sound);
8645 case SOUND_CTRL_ID_SIMPLE:
8646 if (setup.sound_simple)
8647 setup.sound_simple = FALSE;
8648 else if (audio.sound_available)
8650 setup.sound = setup.sound_simple = TRUE;
8651 SetAudioMode(setup.sound);