1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL (DX + XX_LEVEL)
67 #define DY_LEVEL (DY + YY_LEVEL)
68 #define DX_EMERALDS (DX + XX_EMERALDS)
69 #define DY_EMERALDS (DY + YY_EMERALDS)
70 #define DX_DYNAMITE (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE (DY + YY_DYNAMITE)
72 #define DX_KEYS (DX + XX_KEYS)
73 #define DY_KEYS (DY + YY_KEYS)
74 #define DX_SCORE (DX + XX_SCORE)
75 #define DY_SCORE (DY + YY_SCORE)
76 #define DX_TIME (DX + XX_TIME)
77 #define DY_TIME (DY + YY_TIME)
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF -1
81 #define INITIAL_MOVE_DELAY_ON 0
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED 4
87 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
104 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
105 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
107 (DONT_COLLIDE_WITH(e) && \
108 IS_FREE_OR_PLAYER(x, y))))
110 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
111 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
114 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
115 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
117 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
118 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
120 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
121 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
123 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
125 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
126 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
127 Feld[x][y] == EL_DIAMOND))
129 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
130 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
131 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
133 #define PACMAN_CAN_ENTER_FIELD(x, y) \
134 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
135 IS_AMOEBOID(Feld[x][y])))
137 #define PIG_CAN_ENTER_FIELD(x, y) \
138 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 IS_FOOD_PIG(Feld[x][y])))
141 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
142 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
143 IS_FOOD_PENGUIN(Feld[x][y]) || \
144 Feld[x][y] == EL_EXIT_OPEN || \
145 Feld[x][y] == EL_ACID))
149 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
150 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
152 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
153 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
154 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
158 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
159 #define IS_IN_GROUP(e, g) (element_info[e].in_group[g] == TRUE)
160 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
162 #define CE_ENTER_FIELD_COND(e, x, y) \
163 (!IS_PLAYER(x, y) && \
164 (Feld[x][y] == EL_ACID || \
165 Feld[x][y] == MOVE_ENTER_EL(e) || \
166 (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) && \
167 IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
169 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
172 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
173 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
175 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
176 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
178 /* game button identifiers */
179 #define GAME_CTRL_ID_STOP 0
180 #define GAME_CTRL_ID_PAUSE 1
181 #define GAME_CTRL_ID_PLAY 2
182 #define SOUND_CTRL_ID_MUSIC 3
183 #define SOUND_CTRL_ID_LOOPS 4
184 #define SOUND_CTRL_ID_SIMPLE 5
186 #define NUM_GAME_BUTTONS 6
189 /* forward declaration for internal use */
191 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
192 static boolean MovePlayer(struct PlayerInfo *, int, int);
193 static void ScrollPlayer(struct PlayerInfo *, int);
194 static void ScrollScreen(struct PlayerInfo *, int);
196 static void InitBeltMovement(void);
197 static void CloseAllOpenTimegates(void);
198 static void CheckGravityMovement(struct PlayerInfo *);
199 static void KillHeroUnlessProtected(int, int);
201 static void TestIfPlayerTouchesCustomElement(int, int);
202 static void TestIfElementTouchesCustomElement(int, int);
203 static void TestIfElementHitsCustomElement(int, int, int);
205 static void ChangeElement(int, int, int);
206 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
207 static boolean CheckTriggeredElementChange(int, int, int, int);
208 static boolean CheckElementSideChange(int, int, int, int, int, int);
209 static boolean CheckElementChange(int, int, int, int);
211 static void PlayLevelSound(int, int, int);
212 static void PlayLevelSoundNearest(int, int, int);
213 static void PlayLevelSoundAction(int, int, int);
214 static void PlayLevelSoundElementAction(int, int, int, int);
215 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
216 static void PlayLevelSoundActionIfLoop(int, int, int);
217 static void StopLevelSoundActionIfLoop(int, int, int);
218 static void PlayLevelMusic();
220 static void MapGameButtons();
221 static void HandleGameButtons(struct GadgetInfo *);
223 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
226 /* ------------------------------------------------------------------------- */
227 /* definition of elements that automatically change to other elements after */
228 /* a specified time, eventually calling a function when changing */
229 /* ------------------------------------------------------------------------- */
231 /* forward declaration for changer functions */
232 static void InitBuggyBase(int x, int y);
233 static void WarnBuggyBase(int x, int y);
235 static void InitTrap(int x, int y);
236 static void ActivateTrap(int x, int y);
237 static void ChangeActiveTrap(int x, int y);
239 static void InitRobotWheel(int x, int y);
240 static void RunRobotWheel(int x, int y);
241 static void StopRobotWheel(int x, int y);
243 static void InitTimegateWheel(int x, int y);
244 static void RunTimegateWheel(int x, int y);
246 struct ChangingElementInfo
251 void (*pre_change_function)(int x, int y);
252 void (*change_function)(int x, int y);
253 void (*post_change_function)(int x, int y);
256 static struct ChangingElementInfo change_delay_list[] =
307 EL_SWITCHGATE_OPENING,
315 EL_SWITCHGATE_CLOSING,
316 EL_SWITCHGATE_CLOSED,
348 EL_ACID_SPLASH_RIGHT,
357 EL_SP_BUGGY_BASE_ACTIVATING,
364 EL_SP_BUGGY_BASE_ACTIVATING,
365 EL_SP_BUGGY_BASE_ACTIVE,
372 EL_SP_BUGGY_BASE_ACTIVE,
396 EL_ROBOT_WHEEL_ACTIVE,
404 EL_TIMEGATE_SWITCH_ACTIVE,
425 int push_delay_fixed, push_delay_random;
430 { EL_BALLOON, 0, 0 },
432 { EL_SOKOBAN_OBJECT, 2, 0 },
433 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
434 { EL_SATELLITE, 2, 0 },
435 { EL_SP_DISK_YELLOW, 2, 0 },
437 { EL_UNDEFINED, 0, 0 },
445 move_stepsize_list[] =
447 { EL_AMOEBA_DROP, 2 },
448 { EL_AMOEBA_DROPPING, 2 },
449 { EL_QUICKSAND_FILLING, 1 },
450 { EL_QUICKSAND_EMPTYING, 1 },
451 { EL_MAGIC_WALL_FILLING, 2 },
452 { EL_BD_MAGIC_WALL_FILLING, 2 },
453 { EL_MAGIC_WALL_EMPTYING, 2 },
454 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
464 collect_count_list[] =
467 { EL_BD_DIAMOND, 1 },
468 { EL_EMERALD_YELLOW, 1 },
469 { EL_EMERALD_RED, 1 },
470 { EL_EMERALD_PURPLE, 1 },
472 { EL_SP_INFOTRON, 1 },
479 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
481 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
482 CH_EVENT_BIT(CE_DELAY))
483 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
484 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
485 IS_JUST_CHANGING(x, y))
487 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
490 void GetPlayerConfig()
492 if (!audio.sound_available)
493 setup.sound_simple = FALSE;
495 if (!audio.loops_available)
496 setup.sound_loops = FALSE;
498 if (!audio.music_available)
499 setup.sound_music = FALSE;
501 if (!video.fullscreen_available)
502 setup.fullscreen = FALSE;
504 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
506 SetAudioMode(setup.sound);
510 static int getBeltNrFromBeltElement(int element)
512 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
513 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
514 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
517 static int getBeltNrFromBeltActiveElement(int element)
519 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
520 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
521 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
524 static int getBeltNrFromBeltSwitchElement(int element)
526 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
527 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
528 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
531 static int getBeltDirNrFromBeltSwitchElement(int element)
533 static int belt_base_element[4] =
535 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
536 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
537 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
538 EL_CONVEYOR_BELT_4_SWITCH_LEFT
541 int belt_nr = getBeltNrFromBeltSwitchElement(element);
542 int belt_dir_nr = element - belt_base_element[belt_nr];
544 return (belt_dir_nr % 3);
547 static int getBeltDirFromBeltSwitchElement(int element)
549 static int belt_move_dir[3] =
556 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
558 return belt_move_dir[belt_dir_nr];
561 static void InitPlayerField(int x, int y, int element, boolean init_game)
563 if (element == EL_SP_MURPHY)
567 if (stored_player[0].present)
569 Feld[x][y] = EL_SP_MURPHY_CLONE;
575 stored_player[0].use_murphy_graphic = TRUE;
578 Feld[x][y] = EL_PLAYER_1;
584 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
585 int jx = player->jx, jy = player->jy;
587 player->present = TRUE;
589 if (!options.network || player->connected)
591 player->active = TRUE;
593 /* remove potentially duplicate players */
594 if (StorePlayer[jx][jy] == Feld[x][y])
595 StorePlayer[jx][jy] = 0;
597 StorePlayer[x][y] = Feld[x][y];
601 printf("Player %d activated.\n", player->element_nr);
602 printf("[Local player is %d and currently %s.]\n",
603 local_player->element_nr,
604 local_player->active ? "active" : "not active");
608 Feld[x][y] = EL_EMPTY;
609 player->jx = player->last_jx = x;
610 player->jy = player->last_jy = y;
614 static void InitField(int x, int y, boolean init_game)
616 int element = Feld[x][y];
625 InitPlayerField(x, y, element, init_game);
629 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
630 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
631 else if (x > 0 && Feld[x-1][y] == EL_ACID)
632 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
633 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
634 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
635 else if (y > 0 && Feld[x][y-1] == EL_ACID)
636 Feld[x][y] = EL_ACID_POOL_BOTTOM;
637 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
638 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
646 case EL_SPACESHIP_RIGHT:
647 case EL_SPACESHIP_UP:
648 case EL_SPACESHIP_LEFT:
649 case EL_SPACESHIP_DOWN:
651 case EL_BD_BUTTERFLY_RIGHT:
652 case EL_BD_BUTTERFLY_UP:
653 case EL_BD_BUTTERFLY_LEFT:
654 case EL_BD_BUTTERFLY_DOWN:
655 case EL_BD_BUTTERFLY:
656 case EL_BD_FIREFLY_RIGHT:
657 case EL_BD_FIREFLY_UP:
658 case EL_BD_FIREFLY_LEFT:
659 case EL_BD_FIREFLY_DOWN:
661 case EL_PACMAN_RIGHT:
685 if (y == lev_fieldy - 1)
687 Feld[x][y] = EL_AMOEBA_GROWING;
688 Store[x][y] = EL_AMOEBA_WET;
692 case EL_DYNAMITE_ACTIVE:
693 case EL_SP_DISK_RED_ACTIVE:
694 case EL_DYNABOMB_PLAYER_1_ACTIVE:
695 case EL_DYNABOMB_PLAYER_2_ACTIVE:
696 case EL_DYNABOMB_PLAYER_3_ACTIVE:
697 case EL_DYNABOMB_PLAYER_4_ACTIVE:
702 local_player->lights_still_needed++;
705 case EL_SOKOBAN_FIELD_EMPTY:
706 local_player->sokobanfields_still_needed++;
710 local_player->friends_still_needed++;
715 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
720 Feld[x][y] = EL_EMPTY;
725 case EL_EM_KEY_1_FILE:
726 Feld[x][y] = EL_EM_KEY_1;
728 case EL_EM_KEY_2_FILE:
729 Feld[x][y] = EL_EM_KEY_2;
731 case EL_EM_KEY_3_FILE:
732 Feld[x][y] = EL_EM_KEY_3;
734 case EL_EM_KEY_4_FILE:
735 Feld[x][y] = EL_EM_KEY_4;
739 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
740 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
741 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
742 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
743 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
744 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
745 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
746 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
747 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
748 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
749 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
750 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
753 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
754 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
755 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
757 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
759 game.belt_dir[belt_nr] = belt_dir;
760 game.belt_dir_nr[belt_nr] = belt_dir_nr;
762 else /* more than one switch -- set it like the first switch */
764 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
769 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
771 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
774 case EL_LIGHT_SWITCH_ACTIVE:
776 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
780 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
782 else if (IS_GROUP_ELEMENT(element))
784 struct ElementGroupInfo *group = element_info[element].group;
785 int random_pos = RND(group->num_elements_resolved);
787 Feld[x][y] = group->element_resolved[random_pos];
789 InitField(x, y, init_game);
795 void DrawGameDoorValues()
799 for (i = 0; i < MAX_PLAYERS; i++)
800 for (j = 0; j < 4; j++)
801 if (stored_player[i].key[j])
802 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
803 el2edimg(EL_KEY_1 + j));
805 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
806 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
807 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
808 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
809 DrawText(DX + XX_SCORE, DY + YY_SCORE,
810 int2str(local_player->score, 5), FONT_TEXT_2);
811 DrawText(DX + XX_TIME, DY + YY_TIME,
812 int2str(TimeLeft, 3), FONT_TEXT_2);
815 static void resolve_group_element(int group_element, int recursion_depth)
818 static struct ElementGroupInfo *group;
819 struct ElementGroupInfo *actual_group = element_info[group_element].group;
822 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
824 Error(ERR_WARN, "recursion too deep when resolving group element %d",
825 group_element - EL_GROUP_START + 1);
827 /* replace element which caused too deep recursion by question mark */
828 group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
833 if (recursion_depth == 0) /* initialization */
835 group = element_info[group_element].group;
836 group->num_elements_resolved = 0;
837 group_nr = group_element - EL_GROUP_START;
840 for (i = 0; i < actual_group->num_elements; i++)
842 int element = actual_group->element[i];
844 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
847 if (IS_GROUP_ELEMENT(element))
848 resolve_group_element(element, recursion_depth + 1);
851 group->element_resolved[group->num_elements_resolved++] = element;
852 element_info[element].in_group[group_nr] = TRUE;
857 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
859 printf("::: group %d: %d resolved elements\n",
860 group_element - EL_GROUP_START, group->num_elements_resolved);
861 for (i = 0; i < group->num_elements_resolved; i++)
862 printf("::: - %d ['%s']\n", group->element_resolved[i],
863 element_info[group->element_resolved[i]].token_name);
870 =============================================================================
872 -----------------------------------------------------------------------------
873 initialize game engine due to level / tape version number
874 =============================================================================
877 static void InitGameEngine()
881 /* set game engine from tape file when re-playing, else from level file */
882 game.engine_version = (tape.playing ? tape.engine_version :
885 /* dynamically adjust element properties according to game engine version */
886 InitElementPropertiesEngine(game.engine_version);
889 printf("level %d: level version == %06d\n", level_nr, level.game_version);
890 printf(" tape version == %06d [%s] [file: %06d]\n",
891 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
893 printf(" => game.engine_version == %06d\n", game.engine_version);
896 /* ---------- recursively resolve group elements ------------------------- */
898 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
899 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
900 element_info[i].in_group[j] = FALSE;
902 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
903 resolve_group_element(EL_GROUP_START + i, 0);
905 /* ---------- initialize player's initial move delay --------------------- */
907 /* dynamically adjust player properties according to game engine version */
908 game.initial_move_delay =
909 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
910 INITIAL_MOVE_DELAY_OFF);
912 /* dynamically adjust player properties according to level information */
913 game.initial_move_delay_value =
914 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
916 /* ---------- initialize player's initial push delay --------------------- */
918 /* dynamically adjust player properties according to game engine version */
919 game.initial_push_delay_value =
920 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
922 /* ---------- initialize changing elements ------------------------------- */
924 /* initialize changing elements information */
925 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
927 struct ElementInfo *ei = &element_info[i];
929 /* this pointer might have been changed in the level editor */
930 ei->change = &ei->change_page[0];
932 if (!IS_CUSTOM_ELEMENT(i))
934 ei->change->target_element = EL_EMPTY_SPACE;
935 ei->change->delay_fixed = 0;
936 ei->change->delay_random = 0;
937 ei->change->delay_frames = 1;
940 ei->change_events = CE_BITMASK_DEFAULT;
941 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
943 ei->event_page_nr[j] = 0;
944 ei->event_page[j] = &ei->change_page[0];
948 /* add changing elements from pre-defined list */
949 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
951 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
952 struct ElementInfo *ei = &element_info[ch_delay->element];
954 ei->change->target_element = ch_delay->target_element;
955 ei->change->delay_fixed = ch_delay->change_delay;
957 ei->change->pre_change_function = ch_delay->pre_change_function;
958 ei->change->change_function = ch_delay->change_function;
959 ei->change->post_change_function = ch_delay->post_change_function;
961 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
965 /* add change events from custom element configuration */
966 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
968 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
970 for (j = 0; j < ei->num_change_pages; j++)
972 if (!ei->change_page[j].can_change)
975 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
977 /* only add event page for the first page found with this event */
978 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
979 !(ei->change_events & CH_EVENT_BIT(k)))
981 ei->change_events |= CH_EVENT_BIT(k);
982 ei->event_page_nr[k] = j;
983 ei->event_page[k] = &ei->change_page[j];
991 /* add change events from custom element configuration */
992 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
994 int element = EL_CUSTOM_START + i;
996 /* only add custom elements that change after fixed/random frame delay */
997 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
998 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1002 /* ---------- initialize trigger events ---------------------------------- */
1004 /* initialize trigger events information */
1005 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1006 trigger_events[i] = EP_BITMASK_DEFAULT;
1009 /* add trigger events from element change event properties */
1010 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1012 struct ElementInfo *ei = &element_info[i];
1014 for (j = 0; j < ei->num_change_pages; j++)
1016 if (!ei->change_page[j].can_change)
1019 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1021 int trigger_element = ei->change_page[j].trigger_element;
1023 trigger_events[trigger_element] |= ei->change_page[j].events;
1028 /* add trigger events from element change event properties */
1029 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1030 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1031 trigger_events[element_info[i].change->trigger_element] |=
1032 element_info[i].change->events;
1035 /* ---------- initialize push delay -------------------------------------- */
1037 /* initialize push delay values to default */
1038 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1040 if (!IS_CUSTOM_ELEMENT(i))
1042 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1043 element_info[i].push_delay_random = game.default_push_delay_random;
1047 /* set push delay value for certain elements from pre-defined list */
1048 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1050 int e = push_delay_list[i].element;
1052 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1053 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1056 /* ---------- initialize move stepsize ----------------------------------- */
1058 /* initialize move stepsize values to default */
1059 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1060 if (!IS_CUSTOM_ELEMENT(i))
1061 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1063 /* set move stepsize value for certain elements from pre-defined list */
1064 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1066 int e = move_stepsize_list[i].element;
1068 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1071 /* ---------- initialize gem count --------------------------------------- */
1073 /* initialize gem count values for each element */
1074 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1075 if (!IS_CUSTOM_ELEMENT(i))
1076 element_info[i].collect_count = 0;
1078 /* add gem count values for all elements from pre-defined list */
1079 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1080 element_info[collect_count_list[i].element].collect_count =
1081 collect_count_list[i].count;
1086 =============================================================================
1088 -----------------------------------------------------------------------------
1089 initialize and start new game
1090 =============================================================================
1095 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1096 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1097 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1104 #if USE_NEW_AMOEBA_CODE
1105 printf("Using new amoeba code.\n");
1107 printf("Using old amoeba code.\n");
1112 /* don't play tapes over network */
1113 network_playing = (options.network && !tape.playing);
1115 for (i = 0; i < MAX_PLAYERS; i++)
1117 struct PlayerInfo *player = &stored_player[i];
1119 player->index_nr = i;
1120 player->element_nr = EL_PLAYER_1 + i;
1122 player->present = FALSE;
1123 player->active = FALSE;
1126 player->effective_action = 0;
1127 player->programmed_action = 0;
1130 player->gems_still_needed = level.gems_needed;
1131 player->sokobanfields_still_needed = 0;
1132 player->lights_still_needed = 0;
1133 player->friends_still_needed = 0;
1135 for (j = 0; j < 4; j++)
1136 player->key[j] = FALSE;
1138 player->dynabomb_count = 0;
1139 player->dynabomb_size = 1;
1140 player->dynabombs_left = 0;
1141 player->dynabomb_xl = FALSE;
1143 player->MovDir = MV_NO_MOVING;
1146 player->GfxDir = MV_NO_MOVING;
1147 player->GfxAction = ACTION_DEFAULT;
1149 player->StepFrame = 0;
1151 player->use_murphy_graphic = FALSE;
1153 player->actual_frame_counter = 0;
1155 player->step_counter = 0;
1157 player->last_move_dir = MV_NO_MOVING;
1159 player->is_waiting = FALSE;
1160 player->is_moving = FALSE;
1161 player->is_digging = FALSE;
1162 player->is_snapping = FALSE;
1163 player->is_collecting = FALSE;
1164 player->is_pushing = FALSE;
1165 player->is_switching = FALSE;
1166 player->is_dropping = FALSE;
1168 player->is_bored = FALSE;
1169 player->is_sleeping = FALSE;
1171 player->frame_counter_bored = -1;
1172 player->frame_counter_sleeping = -1;
1174 player->anim_delay_counter = 0;
1175 player->post_delay_counter = 0;
1177 player->action_waiting = ACTION_DEFAULT;
1178 player->last_action_waiting = ACTION_DEFAULT;
1179 player->special_action_bored = ACTION_DEFAULT;
1180 player->special_action_sleeping = ACTION_DEFAULT;
1182 player->num_special_action_bored = 0;
1183 player->num_special_action_sleeping = 0;
1185 /* determine number of special actions for bored and sleeping animation */
1186 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1188 boolean found = FALSE;
1190 for (k = 0; k < NUM_DIRECTIONS; k++)
1191 if (el_act_dir2img(player->element_nr, j, k) !=
1192 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1196 player->num_special_action_bored++;
1200 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1202 boolean found = FALSE;
1204 for (k = 0; k < NUM_DIRECTIONS; k++)
1205 if (el_act_dir2img(player->element_nr, j, k) !=
1206 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1210 player->num_special_action_sleeping++;
1215 player->switch_x = -1;
1216 player->switch_y = -1;
1218 player->show_envelope = 0;
1220 player->move_delay = game.initial_move_delay;
1221 player->move_delay_value = game.initial_move_delay_value;
1223 player->move_delay_reset_counter = 0;
1225 player->push_delay = 0;
1226 player->push_delay_value = game.initial_push_delay_value;
1228 player->drop_delay = 0;
1230 player->last_jx = player->last_jy = 0;
1231 player->jx = player->jy = 0;
1233 player->shield_normal_time_left = 0;
1234 player->shield_deadly_time_left = 0;
1236 player->inventory_size = 0;
1238 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1239 SnapField(player, 0, 0);
1241 player->LevelSolved = FALSE;
1242 player->GameOver = FALSE;
1245 network_player_action_received = FALSE;
1247 #if defined(PLATFORM_UNIX)
1248 /* initial null action */
1249 if (network_playing)
1250 SendToServer_MovePlayer(MV_NO_MOVING);
1258 TimeLeft = level.time;
1260 ScreenMovDir = MV_NO_MOVING;
1264 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1266 AllPlayersGone = FALSE;
1268 game.yamyam_content_nr = 0;
1269 game.magic_wall_active = FALSE;
1270 game.magic_wall_time_left = 0;
1271 game.light_time_left = 0;
1272 game.timegate_time_left = 0;
1273 game.switchgate_pos = 0;
1274 game.balloon_dir = MV_NO_MOVING;
1275 game.gravity = level.initial_gravity;
1276 game.explosions_delayed = TRUE;
1278 game.envelope_active = FALSE;
1280 for (i = 0; i < 4; i++)
1282 game.belt_dir[i] = MV_NO_MOVING;
1283 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1286 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1287 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1289 for (x = 0; x < lev_fieldx; x++)
1291 for (y = 0; y < lev_fieldy; y++)
1293 Feld[x][y] = level.field[x][y];
1294 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1295 ChangeDelay[x][y] = 0;
1296 ChangePage[x][y] = -1;
1297 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1299 WasJustMoving[x][y] = 0;
1300 WasJustFalling[x][y] = 0;
1302 Pushed[x][y] = FALSE;
1304 Changed[x][y] = CE_BITMASK_DEFAULT;
1305 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1307 ExplodePhase[x][y] = 0;
1308 ExplodeField[x][y] = EX_NO_EXPLOSION;
1310 RunnerVisit[x][y] = 0;
1311 PlayerVisit[x][y] = 0;
1314 GfxRandom[x][y] = INIT_GFX_RANDOM();
1315 GfxElement[x][y] = EL_UNDEFINED;
1316 GfxAction[x][y] = ACTION_DEFAULT;
1317 GfxDir[x][y] = MV_NO_MOVING;
1321 for (y = 0; y < lev_fieldy; y++)
1323 for (x = 0; x < lev_fieldx; x++)
1325 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1327 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1329 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1332 InitField(x, y, TRUE);
1338 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1339 emulate_sb ? EMU_SOKOBAN :
1340 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1342 /* correct non-moving belts to start moving left */
1343 for (i = 0; i < 4; i++)
1344 if (game.belt_dir[i] == MV_NO_MOVING)
1345 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1347 /* check if any connected player was not found in playfield */
1348 for (i = 0; i < MAX_PLAYERS; i++)
1350 struct PlayerInfo *player = &stored_player[i];
1352 if (player->connected && !player->present)
1354 for (j = 0; j < MAX_PLAYERS; j++)
1356 struct PlayerInfo *some_player = &stored_player[j];
1357 int jx = some_player->jx, jy = some_player->jy;
1359 /* assign first free player found that is present in the playfield */
1360 if (some_player->present && !some_player->connected)
1362 player->present = TRUE;
1363 player->active = TRUE;
1364 some_player->present = FALSE;
1366 StorePlayer[jx][jy] = player->element_nr;
1367 player->jx = player->last_jx = jx;
1368 player->jy = player->last_jy = jy;
1378 /* when playing a tape, eliminate all players who do not participate */
1380 for (i = 0; i < MAX_PLAYERS; i++)
1382 if (stored_player[i].active && !tape.player_participates[i])
1384 struct PlayerInfo *player = &stored_player[i];
1385 int jx = player->jx, jy = player->jy;
1387 player->active = FALSE;
1388 StorePlayer[jx][jy] = 0;
1389 Feld[jx][jy] = EL_EMPTY;
1393 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1395 /* when in single player mode, eliminate all but the first active player */
1397 for (i = 0; i < MAX_PLAYERS; i++)
1399 if (stored_player[i].active)
1401 for (j = i + 1; j < MAX_PLAYERS; j++)
1403 if (stored_player[j].active)
1405 struct PlayerInfo *player = &stored_player[j];
1406 int jx = player->jx, jy = player->jy;
1408 player->active = FALSE;
1409 StorePlayer[jx][jy] = 0;
1410 Feld[jx][jy] = EL_EMPTY;
1417 /* when recording the game, store which players take part in the game */
1420 for (i = 0; i < MAX_PLAYERS; i++)
1421 if (stored_player[i].active)
1422 tape.player_participates[i] = TRUE;
1427 for (i = 0; i < MAX_PLAYERS; i++)
1429 struct PlayerInfo *player = &stored_player[i];
1431 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1436 if (local_player == player)
1437 printf("Player %d is local player.\n", i+1);
1441 if (BorderElement == EL_EMPTY)
1444 SBX_Right = lev_fieldx - SCR_FIELDX;
1446 SBY_Lower = lev_fieldy - SCR_FIELDY;
1451 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1453 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1456 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1457 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1459 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1460 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1462 /* if local player not found, look for custom element that might create
1463 the player (make some assumptions about the right custom element) */
1464 if (!local_player->present)
1466 int start_x = 0, start_y = 0;
1467 int found_rating = 0;
1468 int found_element = EL_UNDEFINED;
1470 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1472 int element = Feld[x][y];
1477 if (!IS_CUSTOM_ELEMENT(element))
1480 if (CAN_CHANGE(element))
1482 for (i = 0; i < element_info[element].num_change_pages; i++)
1484 content = element_info[element].change_page[i].target_element;
1485 is_player = ELEM_IS_PLAYER(content);
1487 if (is_player && (found_rating < 3 || element < found_element))
1493 found_element = element;
1498 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1500 content = element_info[element].content[xx][yy];
1501 is_player = ELEM_IS_PLAYER(content);
1503 if (is_player && (found_rating < 2 || element < found_element))
1505 start_x = x + xx - 1;
1506 start_y = y + yy - 1;
1509 found_element = element;
1512 if (!CAN_CHANGE(element))
1515 for (i = 0; i < element_info[element].num_change_pages; i++)
1517 content = element_info[element].change_page[i].content[xx][yy];
1518 is_player = ELEM_IS_PLAYER(content);
1520 if (is_player && (found_rating < 1 || element < found_element))
1522 start_x = x + xx - 1;
1523 start_y = y + yy - 1;
1526 found_element = element;
1532 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1533 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1536 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1537 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1543 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1544 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1545 local_player->jx - MIDPOSX);
1547 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1548 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1549 local_player->jy - MIDPOSY);
1551 scroll_x = SBX_Left;
1552 scroll_y = SBY_Upper;
1553 if (local_player->jx >= SBX_Left + MIDPOSX)
1554 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1555 local_player->jx - MIDPOSX :
1557 if (local_player->jy >= SBY_Upper + MIDPOSY)
1558 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1559 local_player->jy - MIDPOSY :
1564 CloseDoor(DOOR_CLOSE_1);
1569 /* after drawing the level, correct some elements */
1570 if (game.timegate_time_left == 0)
1571 CloseAllOpenTimegates();
1573 if (setup.soft_scrolling)
1574 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1576 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1579 /* copy default game door content to main double buffer */
1580 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1581 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1584 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1587 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1588 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1589 BlitBitmap(drawto, drawto,
1590 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1591 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1592 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1593 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1596 DrawGameDoorValues();
1600 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1601 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1602 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1606 /* copy actual game door content to door double buffer for OpenDoor() */
1607 BlitBitmap(drawto, bitmap_db_door,
1608 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1610 OpenDoor(DOOR_OPEN_ALL);
1612 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1614 if (setup.sound_music)
1617 KeyboardAutoRepeatOffUnlessAutoplay();
1621 for (i = 0; i < 4; i++)
1622 printf("Player %d %sactive.\n",
1623 i + 1, (stored_player[i].active ? "" : "not "));
1627 printf("::: starting game [%d]\n", FrameCounter);
1631 void InitMovDir(int x, int y)
1633 int i, element = Feld[x][y];
1634 static int xy[4][2] =
1641 static int direction[3][4] =
1643 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1644 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1645 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1654 Feld[x][y] = EL_BUG;
1655 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1658 case EL_SPACESHIP_RIGHT:
1659 case EL_SPACESHIP_UP:
1660 case EL_SPACESHIP_LEFT:
1661 case EL_SPACESHIP_DOWN:
1662 Feld[x][y] = EL_SPACESHIP;
1663 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1666 case EL_BD_BUTTERFLY_RIGHT:
1667 case EL_BD_BUTTERFLY_UP:
1668 case EL_BD_BUTTERFLY_LEFT:
1669 case EL_BD_BUTTERFLY_DOWN:
1670 Feld[x][y] = EL_BD_BUTTERFLY;
1671 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1674 case EL_BD_FIREFLY_RIGHT:
1675 case EL_BD_FIREFLY_UP:
1676 case EL_BD_FIREFLY_LEFT:
1677 case EL_BD_FIREFLY_DOWN:
1678 Feld[x][y] = EL_BD_FIREFLY;
1679 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1682 case EL_PACMAN_RIGHT:
1684 case EL_PACMAN_LEFT:
1685 case EL_PACMAN_DOWN:
1686 Feld[x][y] = EL_PACMAN;
1687 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1690 case EL_SP_SNIKSNAK:
1691 MovDir[x][y] = MV_UP;
1694 case EL_SP_ELECTRON:
1695 MovDir[x][y] = MV_LEFT;
1702 Feld[x][y] = EL_MOLE;
1703 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1707 if (IS_CUSTOM_ELEMENT(element))
1709 if (element_info[element].move_direction_initial != MV_NO_MOVING)
1710 MovDir[x][y] = element_info[element].move_direction_initial;
1711 else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1712 element_info[element].move_pattern == MV_TURNING_LEFT ||
1713 element_info[element].move_pattern == MV_TURNING_RIGHT ||
1714 element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
1715 element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
1716 element_info[element].move_pattern == MV_TURNING_RANDOM)
1717 MovDir[x][y] = 1 << RND(4);
1718 else if (element_info[element].move_pattern == MV_HORIZONTAL)
1719 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1720 else if (element_info[element].move_pattern == MV_VERTICAL)
1721 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1722 else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1723 MovDir[x][y] = element_info[element].move_pattern;
1724 else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1725 element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1727 for (i = 0; i < 4; i++)
1729 int x1 = x + xy[i][0];
1730 int y1 = y + xy[i][1];
1732 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1734 if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1735 MovDir[x][y] = direction[0][i];
1737 MovDir[x][y] = direction[1][i];
1746 MovDir[x][y] = 1 << RND(4);
1748 if (element != EL_BUG &&
1749 element != EL_SPACESHIP &&
1750 element != EL_BD_BUTTERFLY &&
1751 element != EL_BD_FIREFLY)
1754 for (i = 0; i < 4; i++)
1756 int x1 = x + xy[i][0];
1757 int y1 = y + xy[i][1];
1759 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1761 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1763 MovDir[x][y] = direction[0][i];
1766 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1767 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1769 MovDir[x][y] = direction[1][i];
1778 GfxDir[x][y] = MovDir[x][y];
1781 void InitAmoebaNr(int x, int y)
1784 int group_nr = AmoebeNachbarNr(x, y);
1788 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1790 if (AmoebaCnt[i] == 0)
1798 AmoebaNr[x][y] = group_nr;
1799 AmoebaCnt[group_nr]++;
1800 AmoebaCnt2[group_nr]++;
1806 boolean raise_level = FALSE;
1808 if (local_player->MovPos)
1812 if (tape.auto_play) /* tape might already be stopped here */
1813 tape.auto_play_level_solved = TRUE;
1815 if (tape.playing && tape.auto_play)
1816 tape.auto_play_level_solved = TRUE;
1819 local_player->LevelSolved = FALSE;
1821 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1825 if (!tape.playing && setup.sound_loops)
1826 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1827 SND_CTRL_PLAY_LOOP);
1829 while (TimeLeft > 0)
1831 if (!tape.playing && !setup.sound_loops)
1832 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1833 if (TimeLeft > 0 && !(TimeLeft % 10))
1834 RaiseScore(level.score[SC_TIME_BONUS]);
1835 if (TimeLeft > 100 && !(TimeLeft % 10))
1839 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1846 if (!tape.playing && setup.sound_loops)
1847 StopSound(SND_GAME_LEVELTIME_BONUS);
1849 else if (level.time == 0) /* level without time limit */
1851 if (!tape.playing && setup.sound_loops)
1852 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1853 SND_CTRL_PLAY_LOOP);
1855 while (TimePlayed < 999)
1857 if (!tape.playing && !setup.sound_loops)
1858 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1859 if (TimePlayed < 999 && !(TimePlayed % 10))
1860 RaiseScore(level.score[SC_TIME_BONUS]);
1861 if (TimePlayed < 900 && !(TimePlayed % 10))
1865 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1872 if (!tape.playing && setup.sound_loops)
1873 StopSound(SND_GAME_LEVELTIME_BONUS);
1876 /* close exit door after last player */
1877 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1878 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1880 int element = Feld[ExitX][ExitY];
1882 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1883 EL_SP_EXIT_CLOSING);
1885 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1888 /* Hero disappears */
1889 DrawLevelField(ExitX, ExitY);
1895 CloseDoor(DOOR_CLOSE_1);
1900 SaveTape(tape.level_nr); /* Ask to save tape */
1903 if (level_nr == leveldir_current->handicap_level)
1905 leveldir_current->handicap_level++;
1906 SaveLevelSetup_SeriesInfo();
1909 if (level_editor_test_game)
1910 local_player->score = -1; /* no highscore when playing from editor */
1911 else if (level_nr < leveldir_current->last_level)
1912 raise_level = TRUE; /* advance to next level */
1914 if ((hi_pos = NewHiScore()) >= 0)
1916 game_status = GAME_MODE_SCORES;
1917 DrawHallOfFame(hi_pos);
1926 game_status = GAME_MODE_MAIN;
1943 LoadScore(level_nr);
1945 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1946 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
1949 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
1951 if (local_player->score > highscore[k].Score)
1953 /* player has made it to the hall of fame */
1955 if (k < MAX_SCORE_ENTRIES - 1)
1957 int m = MAX_SCORE_ENTRIES - 1;
1960 for (l = k; l < MAX_SCORE_ENTRIES; l++)
1961 if (!strcmp(setup.player_name, highscore[l].Name))
1963 if (m == k) /* player's new highscore overwrites his old one */
1967 for (l = m; l > k; l--)
1969 strcpy(highscore[l].Name, highscore[l - 1].Name);
1970 highscore[l].Score = highscore[l - 1].Score;
1977 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1978 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1979 highscore[k].Score = local_player->score;
1985 else if (!strncmp(setup.player_name, highscore[k].Name,
1986 MAX_PLAYER_NAME_LEN))
1987 break; /* player already there with a higher score */
1993 SaveScore(level_nr);
1998 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2000 if (player->GfxAction != action || player->GfxDir != dir)
2003 printf("Player frame reset! (%d => %d, %d => %d)\n",
2004 player->GfxAction, action, player->GfxDir, dir);
2007 player->GfxAction = action;
2008 player->GfxDir = dir;
2010 player->StepFrame = 0;
2014 static void ResetRandomAnimationValue(int x, int y)
2016 GfxRandom[x][y] = INIT_GFX_RANDOM();
2019 static void ResetGfxAnimation(int x, int y)
2022 GfxAction[x][y] = ACTION_DEFAULT;
2023 GfxDir[x][y] = MovDir[x][y];
2026 void InitMovingField(int x, int y, int direction)
2028 int element = Feld[x][y];
2029 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2030 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2034 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2035 ResetGfxAnimation(x, y);
2037 MovDir[newx][newy] = MovDir[x][y] = direction;
2038 GfxDir[x][y] = direction;
2040 if (Feld[newx][newy] == EL_EMPTY)
2041 Feld[newx][newy] = EL_BLOCKED;
2043 if (direction == MV_DOWN && CAN_FALL(element))
2044 GfxAction[x][y] = ACTION_FALLING;
2046 GfxAction[x][y] = ACTION_MOVING;
2048 GfxFrame[newx][newy] = GfxFrame[x][y];
2049 GfxRandom[newx][newy] = GfxRandom[x][y];
2050 GfxAction[newx][newy] = GfxAction[x][y];
2051 GfxDir[newx][newy] = GfxDir[x][y];
2054 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2056 int direction = MovDir[x][y];
2057 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2058 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2064 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2066 int oldx = x, oldy = y;
2067 int direction = MovDir[x][y];
2069 if (direction == MV_LEFT)
2071 else if (direction == MV_RIGHT)
2073 else if (direction == MV_UP)
2075 else if (direction == MV_DOWN)
2078 *comes_from_x = oldx;
2079 *comes_from_y = oldy;
2082 int MovingOrBlocked2Element(int x, int y)
2084 int element = Feld[x][y];
2086 if (element == EL_BLOCKED)
2090 Blocked2Moving(x, y, &oldx, &oldy);
2091 return Feld[oldx][oldy];
2097 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2099 /* like MovingOrBlocked2Element(), but if element is moving
2100 and (x,y) is the field the moving element is just leaving,
2101 return EL_BLOCKED instead of the element value */
2102 int element = Feld[x][y];
2104 if (IS_MOVING(x, y))
2106 if (element == EL_BLOCKED)
2110 Blocked2Moving(x, y, &oldx, &oldy);
2111 return Feld[oldx][oldy];
2120 static void RemoveField(int x, int y)
2122 Feld[x][y] = EL_EMPTY;
2129 ChangeDelay[x][y] = 0;
2130 ChangePage[x][y] = -1;
2131 Pushed[x][y] = FALSE;
2133 GfxElement[x][y] = EL_UNDEFINED;
2134 GfxAction[x][y] = ACTION_DEFAULT;
2135 GfxDir[x][y] = MV_NO_MOVING;
2138 void RemoveMovingField(int x, int y)
2140 int oldx = x, oldy = y, newx = x, newy = y;
2141 int element = Feld[x][y];
2142 int next_element = EL_UNDEFINED;
2144 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2147 if (IS_MOVING(x, y))
2149 Moving2Blocked(x, y, &newx, &newy);
2150 if (Feld[newx][newy] != EL_BLOCKED)
2153 else if (element == EL_BLOCKED)
2155 Blocked2Moving(x, y, &oldx, &oldy);
2156 if (!IS_MOVING(oldx, oldy))
2160 if (element == EL_BLOCKED &&
2161 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2162 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2163 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2164 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2165 next_element = get_next_element(Feld[oldx][oldy]);
2167 RemoveField(oldx, oldy);
2168 RemoveField(newx, newy);
2170 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2172 if (next_element != EL_UNDEFINED)
2173 Feld[oldx][oldy] = next_element;
2175 DrawLevelField(oldx, oldy);
2176 DrawLevelField(newx, newy);
2179 void DrawDynamite(int x, int y)
2181 int sx = SCREENX(x), sy = SCREENY(y);
2182 int graphic = el2img(Feld[x][y]);
2185 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2188 if (IS_WALKABLE_INSIDE(Back[x][y]))
2192 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2193 else if (Store[x][y])
2194 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2196 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2199 if (Back[x][y] || Store[x][y])
2200 DrawGraphicThruMask(sx, sy, graphic, frame);
2202 DrawGraphic(sx, sy, graphic, frame);
2204 if (game.emulation == EMU_SUPAPLEX)
2205 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2206 else if (Store[x][y])
2207 DrawGraphicThruMask(sx, sy, graphic, frame);
2209 DrawGraphic(sx, sy, graphic, frame);
2213 void CheckDynamite(int x, int y)
2215 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2219 if (MovDelay[x][y] != 0)
2222 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2229 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2231 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2232 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2233 StopSound(SND_DYNAMITE_ACTIVE);
2235 StopSound(SND_DYNABOMB_ACTIVE);
2241 void RelocatePlayer(int x, int y, int element)
2243 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2245 if (player->GameOver) /* do not reanimate dead player */
2249 RemoveField(x, y); /* temporarily remove newly placed player */
2250 DrawLevelField(x, y);
2253 if (player->present)
2255 while (player->MovPos)
2257 ScrollPlayer(player, SCROLL_GO_ON);
2258 ScrollScreen(NULL, SCROLL_GO_ON);
2264 Delay(GAME_FRAME_DELAY);
2267 DrawPlayer(player); /* needed here only to cleanup last field */
2268 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2270 player->is_moving = FALSE;
2273 Feld[x][y] = element;
2274 InitPlayerField(x, y, element, TRUE);
2276 if (player == local_player)
2278 int scroll_xx = -999, scroll_yy = -999;
2280 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2283 int fx = FX, fy = FY;
2285 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2286 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2287 local_player->jx - MIDPOSX);
2289 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2290 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2291 local_player->jy - MIDPOSY);
2293 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2294 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2299 fx += dx * TILEX / 2;
2300 fy += dy * TILEY / 2;
2302 ScrollLevel(dx, dy);
2305 /* scroll in two steps of half tile size to make things smoother */
2306 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2308 Delay(GAME_FRAME_DELAY);
2310 /* scroll second step to align at full tile size */
2312 Delay(GAME_FRAME_DELAY);
2317 void Explode(int ex, int ey, int phase, int mode)
2321 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2322 int last_phase = num_phase * delay;
2323 int half_phase = (num_phase / 2) * delay;
2324 int first_phase_after_start = EX_PHASE_START + 1;
2326 if (game.explosions_delayed)
2328 ExplodeField[ex][ey] = mode;
2332 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2334 int center_element = Feld[ex][ey];
2337 /* --- This is only really needed (and now handled) in "Impact()". --- */
2338 /* do not explode moving elements that left the explode field in time */
2339 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2340 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2344 if (mode == EX_NORMAL || mode == EX_CENTER)
2345 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2347 /* remove things displayed in background while burning dynamite */
2348 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2351 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2353 /* put moving element to center field (and let it explode there) */
2354 center_element = MovingOrBlocked2Element(ex, ey);
2355 RemoveMovingField(ex, ey);
2356 Feld[ex][ey] = center_element;
2359 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2361 int xx = x - ex + 1;
2362 int yy = y - ey + 1;
2365 if (!IN_LEV_FIELD(x, y) ||
2366 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2367 (x != ex || y != ey)))
2370 element = Feld[x][y];
2372 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2374 element = MovingOrBlocked2Element(x, y);
2376 if (!IS_EXPLOSION_PROOF(element))
2377 RemoveMovingField(x, y);
2383 if (IS_EXPLOSION_PROOF(element))
2386 /* indestructible elements can only explode in center (but not flames) */
2387 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2388 element == EL_FLAMES)
2393 if ((IS_INDESTRUCTIBLE(element) &&
2394 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2395 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2396 element == EL_FLAMES)
2400 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2402 if (IS_ACTIVE_BOMB(element))
2404 /* re-activate things under the bomb like gate or penguin */
2405 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2412 /* save walkable background elements while explosion on same tile */
2414 if (IS_INDESTRUCTIBLE(element))
2415 Back[x][y] = element;
2417 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2418 Back[x][y] = element;
2421 /* ignite explodable elements reached by other explosion */
2422 if (element == EL_EXPLOSION)
2423 element = Store2[x][y];
2426 if (AmoebaNr[x][y] &&
2427 (element == EL_AMOEBA_FULL ||
2428 element == EL_BD_AMOEBA ||
2429 element == EL_AMOEBA_GROWING))
2431 AmoebaCnt[AmoebaNr[x][y]]--;
2432 AmoebaCnt2[AmoebaNr[x][y]]--;
2438 if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2440 switch(StorePlayer[ex][ey])
2443 Store[x][y] = EL_EMERALD_RED;
2446 Store[x][y] = EL_EMERALD;
2449 Store[x][y] = EL_EMERALD_PURPLE;
2453 Store[x][y] = EL_EMERALD_YELLOW;
2457 if (game.emulation == EMU_SUPAPLEX)
2458 Store[x][y] = EL_EMPTY;
2460 else if (center_element == EL_MOLE)
2461 Store[x][y] = EL_EMERALD_RED;
2462 else if (center_element == EL_PENGUIN)
2463 Store[x][y] = EL_EMERALD_PURPLE;
2464 else if (center_element == EL_BUG)
2465 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2466 else if (center_element == EL_BD_BUTTERFLY)
2467 Store[x][y] = EL_BD_DIAMOND;
2468 else if (center_element == EL_SP_ELECTRON)
2469 Store[x][y] = EL_SP_INFOTRON;
2470 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2471 Store[x][y] = level.amoeba_content;
2472 else if (center_element == EL_YAMYAM)
2473 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2474 else if (IS_CUSTOM_ELEMENT(center_element) &&
2475 element_info[center_element].content[xx][yy] != EL_EMPTY)
2476 Store[x][y] = element_info[center_element].content[xx][yy];
2477 else if (element == EL_WALL_EMERALD)
2478 Store[x][y] = EL_EMERALD;
2479 else if (element == EL_WALL_DIAMOND)
2480 Store[x][y] = EL_DIAMOND;
2481 else if (element == EL_WALL_BD_DIAMOND)
2482 Store[x][y] = EL_BD_DIAMOND;
2483 else if (element == EL_WALL_EMERALD_YELLOW)
2484 Store[x][y] = EL_EMERALD_YELLOW;
2485 else if (element == EL_WALL_EMERALD_RED)
2486 Store[x][y] = EL_EMERALD_RED;
2487 else if (element == EL_WALL_EMERALD_PURPLE)
2488 Store[x][y] = EL_EMERALD_PURPLE;
2489 else if (element == EL_WALL_PEARL)
2490 Store[x][y] = EL_PEARL;
2491 else if (element == EL_WALL_CRYSTAL)
2492 Store[x][y] = EL_CRYSTAL;
2493 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2494 Store[x][y] = element_info[element].content[1][1];
2496 Store[x][y] = EL_EMPTY;
2498 if (x != ex || y != ey ||
2499 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2500 Store2[x][y] = element;
2503 if (AmoebaNr[x][y] &&
2504 (element == EL_AMOEBA_FULL ||
2505 element == EL_BD_AMOEBA ||
2506 element == EL_AMOEBA_GROWING))
2508 AmoebaCnt[AmoebaNr[x][y]]--;
2509 AmoebaCnt2[AmoebaNr[x][y]]--;
2515 MovDir[x][y] = MovPos[x][y] = 0;
2516 GfxDir[x][y] = MovDir[x][y];
2521 Feld[x][y] = EL_EXPLOSION;
2523 GfxElement[x][y] = center_element;
2525 GfxElement[x][y] = EL_UNDEFINED;
2528 ExplodePhase[x][y] = 1;
2532 if (center_element == EL_YAMYAM)
2533 game.yamyam_content_nr =
2534 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2545 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2549 /* activate this even in non-DEBUG version until cause for crash in
2550 getGraphicAnimationFrame() (see below) is found and eliminated */
2554 if (GfxElement[x][y] == EL_UNDEFINED)
2557 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2558 printf("Explode(): This should never happen!\n");
2561 GfxElement[x][y] = EL_EMPTY;
2565 if (phase == first_phase_after_start)
2567 int element = Store2[x][y];
2569 if (element == EL_BLACK_ORB)
2571 Feld[x][y] = Store2[x][y];
2576 else if (phase == half_phase)
2578 int element = Store2[x][y];
2580 if (IS_PLAYER(x, y))
2581 KillHeroUnlessProtected(x, y);
2582 else if (CAN_EXPLODE_BY_FIRE(element))
2584 Feld[x][y] = Store2[x][y];
2588 else if (element == EL_AMOEBA_TO_DIAMOND)
2589 AmoebeUmwandeln(x, y);
2592 if (phase == last_phase)
2596 element = Feld[x][y] = Store[x][y];
2597 Store[x][y] = Store2[x][y] = 0;
2598 GfxElement[x][y] = EL_UNDEFINED;
2600 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2601 element = Feld[x][y] = Back[x][y];
2604 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2605 GfxDir[x][y] = MV_NO_MOVING;
2606 ChangeDelay[x][y] = 0;
2607 ChangePage[x][y] = -1;
2609 InitField(x, y, FALSE);
2610 if (CAN_MOVE(element))
2612 DrawLevelField(x, y);
2614 TestIfElementTouchesCustomElement(x, y);
2616 if (GFX_CRUMBLED(element))
2617 DrawLevelFieldCrumbledSandNeighbours(x, y);
2619 if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2620 StorePlayer[x][y] = 0;
2622 if (ELEM_IS_PLAYER(element))
2623 RelocatePlayer(x, y, element);
2625 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2628 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2630 int stored = Store[x][y];
2631 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2632 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2635 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2638 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2639 element_info[GfxElement[x][y]].token_name,
2644 DrawLevelFieldCrumbledSand(x, y);
2646 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2648 DrawLevelElement(x, y, Back[x][y]);
2649 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2651 else if (IS_WALKABLE_UNDER(Back[x][y]))
2653 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2654 DrawLevelElementThruMask(x, y, Back[x][y]);
2656 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2657 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2661 void DynaExplode(int ex, int ey)
2664 int dynabomb_element = Feld[ex][ey];
2665 int dynabomb_size = 1;
2666 boolean dynabomb_xl = FALSE;
2667 struct PlayerInfo *player;
2668 static int xy[4][2] =
2676 if (IS_ACTIVE_BOMB(dynabomb_element))
2678 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2679 dynabomb_size = player->dynabomb_size;
2680 dynabomb_xl = player->dynabomb_xl;
2681 player->dynabombs_left++;
2684 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2686 for (i = 0; i < 4; i++)
2688 for (j = 1; j <= dynabomb_size; j++)
2690 int x = ex + j * xy[i % 4][0];
2691 int y = ey + j * xy[i % 4][1];
2694 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2697 element = Feld[x][y];
2699 /* do not restart explosions of fields with active bombs */
2700 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2703 Explode(x, y, EX_PHASE_START, EX_BORDER);
2705 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2706 if (element != EL_EMPTY &&
2707 element != EL_SAND &&
2708 element != EL_EXPLOSION &&
2715 void Bang(int x, int y)
2718 int element = MovingOrBlocked2Element(x, y);
2720 int element = Feld[x][y];
2724 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2726 if (IS_PLAYER(x, y))
2729 struct PlayerInfo *player = PLAYERINFO(x, y);
2731 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2732 player->element_nr);
2737 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2739 if (game.emulation == EMU_SUPAPLEX)
2740 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2742 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2747 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
2755 case EL_BD_BUTTERFLY:
2758 case EL_DARK_YAMYAM:
2762 RaiseScoreElement(element);
2763 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2765 case EL_DYNABOMB_PLAYER_1_ACTIVE:
2766 case EL_DYNABOMB_PLAYER_2_ACTIVE:
2767 case EL_DYNABOMB_PLAYER_3_ACTIVE:
2768 case EL_DYNABOMB_PLAYER_4_ACTIVE:
2769 case EL_DYNABOMB_INCREASE_NUMBER:
2770 case EL_DYNABOMB_INCREASE_SIZE:
2771 case EL_DYNABOMB_INCREASE_POWER:
2776 case EL_LAMP_ACTIVE:
2777 if (IS_PLAYER(x, y))
2778 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2780 Explode(x, y, EX_PHASE_START, EX_CENTER);
2783 if (CAN_EXPLODE_DYNA(element))
2785 else if (CAN_EXPLODE_1X1(element))
2786 Explode(x, y, EX_PHASE_START, EX_CENTER);
2788 Explode(x, y, EX_PHASE_START, EX_NORMAL);
2792 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2795 void SplashAcid(int x, int y)
2797 int element = Feld[x][y];
2799 if (element != EL_ACID_SPLASH_LEFT &&
2800 element != EL_ACID_SPLASH_RIGHT)
2802 PlayLevelSound(x, y, SND_ACID_SPLASHING);
2804 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2805 (!IN_LEV_FIELD(x-1, y-1) ||
2806 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2807 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2809 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2810 (!IN_LEV_FIELD(x+1, y-1) ||
2811 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2812 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2816 static void InitBeltMovement()
2818 static int belt_base_element[4] =
2820 EL_CONVEYOR_BELT_1_LEFT,
2821 EL_CONVEYOR_BELT_2_LEFT,
2822 EL_CONVEYOR_BELT_3_LEFT,
2823 EL_CONVEYOR_BELT_4_LEFT
2825 static int belt_base_active_element[4] =
2827 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2828 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2829 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2830 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2835 /* set frame order for belt animation graphic according to belt direction */
2836 for (i = 0; i < 4; i++)
2840 for (j = 0; j < 3; j++)
2842 int element = belt_base_active_element[belt_nr] + j;
2843 int graphic = el2img(element);
2845 if (game.belt_dir[i] == MV_LEFT)
2846 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2848 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2852 for (y = 0; y < lev_fieldy; y++)
2854 for (x = 0; x < lev_fieldx; x++)
2856 int element = Feld[x][y];
2858 for (i = 0; i < 4; i++)
2860 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2862 int e_belt_nr = getBeltNrFromBeltElement(element);
2865 if (e_belt_nr == belt_nr)
2867 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2869 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2877 static void ToggleBeltSwitch(int x, int y)
2879 static int belt_base_element[4] =
2881 EL_CONVEYOR_BELT_1_LEFT,
2882 EL_CONVEYOR_BELT_2_LEFT,
2883 EL_CONVEYOR_BELT_3_LEFT,
2884 EL_CONVEYOR_BELT_4_LEFT
2886 static int belt_base_active_element[4] =
2888 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2889 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2890 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2891 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2893 static int belt_base_switch_element[4] =
2895 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2896 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2897 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2898 EL_CONVEYOR_BELT_4_SWITCH_LEFT
2900 static int belt_move_dir[4] =
2908 int element = Feld[x][y];
2909 int belt_nr = getBeltNrFromBeltSwitchElement(element);
2910 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2911 int belt_dir = belt_move_dir[belt_dir_nr];
2914 if (!IS_BELT_SWITCH(element))
2917 game.belt_dir_nr[belt_nr] = belt_dir_nr;
2918 game.belt_dir[belt_nr] = belt_dir;
2920 if (belt_dir_nr == 3)
2923 /* set frame order for belt animation graphic according to belt direction */
2924 for (i = 0; i < 3; i++)
2926 int element = belt_base_active_element[belt_nr] + i;
2927 int graphic = el2img(element);
2929 if (belt_dir == MV_LEFT)
2930 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2932 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
2935 for (yy = 0; yy < lev_fieldy; yy++)
2937 for (xx = 0; xx < lev_fieldx; xx++)
2939 int element = Feld[xx][yy];
2941 if (IS_BELT_SWITCH(element))
2943 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2945 if (e_belt_nr == belt_nr)
2947 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2948 DrawLevelField(xx, yy);
2951 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2953 int e_belt_nr = getBeltNrFromBeltElement(element);
2955 if (e_belt_nr == belt_nr)
2957 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2959 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2960 DrawLevelField(xx, yy);
2963 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2965 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2967 if (e_belt_nr == belt_nr)
2969 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2971 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2972 DrawLevelField(xx, yy);
2979 static void ToggleSwitchgateSwitch(int x, int y)
2983 game.switchgate_pos = !game.switchgate_pos;
2985 for (yy = 0; yy < lev_fieldy; yy++)
2987 for (xx = 0; xx < lev_fieldx; xx++)
2989 int element = Feld[xx][yy];
2991 if (element == EL_SWITCHGATE_SWITCH_UP ||
2992 element == EL_SWITCHGATE_SWITCH_DOWN)
2994 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2995 DrawLevelField(xx, yy);
2997 else if (element == EL_SWITCHGATE_OPEN ||
2998 element == EL_SWITCHGATE_OPENING)
3000 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3002 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3004 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3007 else if (element == EL_SWITCHGATE_CLOSED ||
3008 element == EL_SWITCHGATE_CLOSING)
3010 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3012 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3014 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3021 static int getInvisibleActiveFromInvisibleElement(int element)
3023 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3024 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3025 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3029 static int getInvisibleFromInvisibleActiveElement(int element)
3031 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3032 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3033 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3037 static void RedrawAllLightSwitchesAndInvisibleElements()
3041 for (y = 0; y < lev_fieldy; y++)
3043 for (x = 0; x < lev_fieldx; x++)
3045 int element = Feld[x][y];
3047 if (element == EL_LIGHT_SWITCH &&
3048 game.light_time_left > 0)
3050 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3051 DrawLevelField(x, y);
3053 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3054 game.light_time_left == 0)
3056 Feld[x][y] = EL_LIGHT_SWITCH;
3057 DrawLevelField(x, y);
3059 else if (element == EL_INVISIBLE_STEELWALL ||
3060 element == EL_INVISIBLE_WALL ||
3061 element == EL_INVISIBLE_SAND)
3063 if (game.light_time_left > 0)
3064 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3066 DrawLevelField(x, y);
3068 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3069 element == EL_INVISIBLE_WALL_ACTIVE ||
3070 element == EL_INVISIBLE_SAND_ACTIVE)
3072 if (game.light_time_left == 0)
3073 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3075 DrawLevelField(x, y);
3081 static void ToggleLightSwitch(int x, int y)
3083 int element = Feld[x][y];
3085 game.light_time_left =
3086 (element == EL_LIGHT_SWITCH ?
3087 level.time_light * FRAMES_PER_SECOND : 0);
3089 RedrawAllLightSwitchesAndInvisibleElements();
3092 static void ActivateTimegateSwitch(int x, int y)
3096 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3098 for (yy = 0; yy < lev_fieldy; yy++)
3100 for (xx = 0; xx < lev_fieldx; xx++)
3102 int element = Feld[xx][yy];
3104 if (element == EL_TIMEGATE_CLOSED ||
3105 element == EL_TIMEGATE_CLOSING)
3107 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3108 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3112 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3114 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3115 DrawLevelField(xx, yy);
3122 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3125 inline static int getElementMoveStepsize(int x, int y)
3127 int element = Feld[x][y];
3128 int direction = MovDir[x][y];
3129 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3130 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3131 int horiz_move = (dx != 0);
3132 int sign = (horiz_move ? dx : dy);
3133 int step = sign * element_info[element].move_stepsize;
3135 /* special values for move stepsize for spring and things on conveyor belt */
3138 if (CAN_FALL(element) &&
3139 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3140 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3141 else if (element == EL_SPRING)
3142 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3148 void Impact(int x, int y)
3150 boolean lastline = (y == lev_fieldy-1);
3151 boolean object_hit = FALSE;
3152 boolean impact = (lastline || object_hit);
3153 int element = Feld[x][y];
3154 int smashed = EL_UNDEFINED;
3156 if (!lastline) /* check if element below was hit */
3158 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3161 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3162 MovDir[x][y + 1] != MV_DOWN ||
3163 MovPos[x][y + 1] <= TILEY / 2));
3166 object_hit = !IS_FREE(x, y + 1);
3169 /* do not smash moving elements that left the smashed field in time */
3170 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3171 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3175 smashed = MovingOrBlocked2Element(x, y + 1);
3177 impact = (lastline || object_hit);
3180 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3186 /* only reset graphic animation if graphic really changes after impact */
3188 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3190 ResetGfxAnimation(x, y);
3191 DrawLevelField(x, y);
3194 if (impact && CAN_EXPLODE_IMPACT(element))
3199 else if (impact && element == EL_PEARL)
3201 Feld[x][y] = EL_PEARL_BREAKING;
3202 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3205 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3207 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3212 if (impact && element == EL_AMOEBA_DROP)
3214 if (object_hit && IS_PLAYER(x, y + 1))
3215 KillHeroUnlessProtected(x, y + 1);
3216 else if (object_hit && smashed == EL_PENGUIN)
3220 Feld[x][y] = EL_AMOEBA_GROWING;
3221 Store[x][y] = EL_AMOEBA_WET;
3223 ResetRandomAnimationValue(x, y);
3228 if (object_hit) /* check which object was hit */
3230 if (CAN_PASS_MAGIC_WALL(element) &&
3231 (smashed == EL_MAGIC_WALL ||
3232 smashed == EL_BD_MAGIC_WALL))
3235 int activated_magic_wall =
3236 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3237 EL_BD_MAGIC_WALL_ACTIVE);
3239 /* activate magic wall / mill */
3240 for (yy = 0; yy < lev_fieldy; yy++)
3241 for (xx = 0; xx < lev_fieldx; xx++)
3242 if (Feld[xx][yy] == smashed)
3243 Feld[xx][yy] = activated_magic_wall;
3245 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3246 game.magic_wall_active = TRUE;
3248 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3249 SND_MAGIC_WALL_ACTIVATING :
3250 SND_BD_MAGIC_WALL_ACTIVATING));
3253 if (IS_PLAYER(x, y + 1))
3255 if (CAN_SMASH_PLAYER(element))
3257 KillHeroUnlessProtected(x, y + 1);
3261 else if (smashed == EL_PENGUIN)
3263 if (CAN_SMASH_PLAYER(element))
3269 else if (element == EL_BD_DIAMOND)
3271 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3277 else if ((element == EL_SP_INFOTRON ||
3278 element == EL_SP_ZONK) &&
3279 (smashed == EL_SP_SNIKSNAK ||
3280 smashed == EL_SP_ELECTRON ||
3281 smashed == EL_SP_DISK_ORANGE))
3287 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3293 else if (CAN_SMASH_EVERYTHING(element))
3295 if (IS_CLASSIC_ENEMY(smashed) ||
3296 CAN_EXPLODE_SMASHED(smashed))
3301 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3303 if (smashed == EL_LAMP ||
3304 smashed == EL_LAMP_ACTIVE)
3309 else if (smashed == EL_NUT)
3311 Feld[x][y + 1] = EL_NUT_BREAKING;
3312 PlayLevelSound(x, y, SND_NUT_BREAKING);
3313 RaiseScoreElement(EL_NUT);
3316 else if (smashed == EL_PEARL)
3318 Feld[x][y + 1] = EL_PEARL_BREAKING;
3319 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3322 else if (smashed == EL_DIAMOND)
3324 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3325 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3328 else if (IS_BELT_SWITCH(smashed))
3330 ToggleBeltSwitch(x, y + 1);
3332 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3333 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3335 ToggleSwitchgateSwitch(x, y + 1);
3337 else if (smashed == EL_LIGHT_SWITCH ||
3338 smashed == EL_LIGHT_SWITCH_ACTIVE)
3340 ToggleLightSwitch(x, y + 1);
3344 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3346 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3347 CE_OTHER_IS_SWITCHING);
3348 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3354 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3359 /* play sound of magic wall / mill */
3361 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3362 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3364 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3365 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3366 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3367 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3372 /* play sound of object that hits the ground */
3373 if (lastline || object_hit)
3374 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3377 inline static void TurnRoundExt(int x, int y)
3389 { 0, 0 }, { 0, 0 }, { 0, 0 },
3394 int left, right, back;
3398 { MV_DOWN, MV_UP, MV_RIGHT },
3399 { MV_UP, MV_DOWN, MV_LEFT },
3401 { MV_LEFT, MV_RIGHT, MV_DOWN },
3405 { MV_RIGHT, MV_LEFT, MV_UP }
3408 int element = Feld[x][y];
3409 int move_pattern = element_info[element].move_pattern;
3411 int old_move_dir = MovDir[x][y];
3412 int left_dir = turn[old_move_dir].left;
3413 int right_dir = turn[old_move_dir].right;
3414 int back_dir = turn[old_move_dir].back;
3416 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3417 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3418 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3419 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3421 int left_x = x + left_dx, left_y = y + left_dy;
3422 int right_x = x + right_dx, right_y = y + right_dy;
3423 int move_x = x + move_dx, move_y = y + move_dy;
3427 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3429 TestIfBadThingTouchesOtherBadThing(x, y);
3431 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3432 MovDir[x][y] = right_dir;
3433 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3434 MovDir[x][y] = left_dir;
3436 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3438 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3441 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3442 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3444 TestIfBadThingTouchesOtherBadThing(x, y);
3446 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3447 MovDir[x][y] = left_dir;
3448 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3449 MovDir[x][y] = right_dir;
3451 if ((element == EL_SPACESHIP ||
3452 element == EL_SP_SNIKSNAK ||
3453 element == EL_SP_ELECTRON)
3454 && MovDir[x][y] != old_move_dir)
3456 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3459 else if (element == EL_YAMYAM)
3461 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3462 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3464 if (can_turn_left && can_turn_right)
3465 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3466 else if (can_turn_left)
3467 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3468 else if (can_turn_right)
3469 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3471 MovDir[x][y] = back_dir;
3473 MovDelay[x][y] = 16 + 16 * RND(3);
3475 else if (element == EL_DARK_YAMYAM)
3477 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3478 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3480 if (can_turn_left && can_turn_right)
3481 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3482 else if (can_turn_left)
3483 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3484 else if (can_turn_right)
3485 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3487 MovDir[x][y] = back_dir;
3489 MovDelay[x][y] = 16 + 16 * RND(3);
3491 else if (element == EL_PACMAN)
3493 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3494 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3496 if (can_turn_left && can_turn_right)
3497 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3498 else if (can_turn_left)
3499 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3500 else if (can_turn_right)
3501 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3503 MovDir[x][y] = back_dir;
3505 MovDelay[x][y] = 6 + RND(40);
3507 else if (element == EL_PIG)
3509 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3510 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3511 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3512 boolean should_turn_left, should_turn_right, should_move_on;
3514 int rnd = RND(rnd_value);
3516 should_turn_left = (can_turn_left &&
3518 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3519 y + back_dy + left_dy)));
3520 should_turn_right = (can_turn_right &&
3522 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3523 y + back_dy + right_dy)));
3524 should_move_on = (can_move_on &&
3527 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3528 y + move_dy + left_dy) ||
3529 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3530 y + move_dy + right_dy)));
3532 if (should_turn_left || should_turn_right || should_move_on)
3534 if (should_turn_left && should_turn_right && should_move_on)
3535 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3536 rnd < 2 * rnd_value / 3 ? right_dir :
3538 else if (should_turn_left && should_turn_right)
3539 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3540 else if (should_turn_left && should_move_on)
3541 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3542 else if (should_turn_right && should_move_on)
3543 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3544 else if (should_turn_left)
3545 MovDir[x][y] = left_dir;
3546 else if (should_turn_right)
3547 MovDir[x][y] = right_dir;
3548 else if (should_move_on)
3549 MovDir[x][y] = old_move_dir;
3551 else if (can_move_on && rnd > rnd_value / 8)
3552 MovDir[x][y] = old_move_dir;
3553 else if (can_turn_left && can_turn_right)
3554 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3555 else if (can_turn_left && rnd > rnd_value / 8)
3556 MovDir[x][y] = left_dir;
3557 else if (can_turn_right && rnd > rnd_value/8)
3558 MovDir[x][y] = right_dir;
3560 MovDir[x][y] = back_dir;
3562 xx = x + move_xy[MovDir[x][y]].x;
3563 yy = y + move_xy[MovDir[x][y]].y;
3565 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3566 MovDir[x][y] = old_move_dir;
3570 else if (element == EL_DRAGON)
3572 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3573 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3574 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3576 int rnd = RND(rnd_value);
3579 if (FrameCounter < 1 && x == 0 && y == 29)
3580 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3583 if (can_move_on && rnd > rnd_value / 8)
3584 MovDir[x][y] = old_move_dir;
3585 else if (can_turn_left && can_turn_right)
3586 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3587 else if (can_turn_left && rnd > rnd_value / 8)
3588 MovDir[x][y] = left_dir;
3589 else if (can_turn_right && rnd > rnd_value / 8)
3590 MovDir[x][y] = right_dir;
3592 MovDir[x][y] = back_dir;
3594 xx = x + move_xy[MovDir[x][y]].x;
3595 yy = y + move_xy[MovDir[x][y]].y;
3598 if (FrameCounter < 1 && x == 0 && y == 29)
3599 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3600 xx, yy, Feld[xx][yy],
3605 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3606 MovDir[x][y] = old_move_dir;
3608 if (!IS_FREE(xx, yy))
3609 MovDir[x][y] = old_move_dir;
3613 if (FrameCounter < 1 && x == 0 && y == 29)
3614 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3619 else if (element == EL_MOLE)
3621 boolean can_move_on =
3622 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3623 IS_AMOEBOID(Feld[move_x][move_y]) ||
3624 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3627 boolean can_turn_left =
3628 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3629 IS_AMOEBOID(Feld[left_x][left_y])));
3631 boolean can_turn_right =
3632 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3633 IS_AMOEBOID(Feld[right_x][right_y])));
3635 if (can_turn_left && can_turn_right)
3636 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3637 else if (can_turn_left)
3638 MovDir[x][y] = left_dir;
3640 MovDir[x][y] = right_dir;
3643 if (MovDir[x][y] != old_move_dir)
3646 else if (element == EL_BALLOON)
3648 MovDir[x][y] = game.balloon_dir;
3651 else if (element == EL_SPRING)
3653 if (MovDir[x][y] & MV_HORIZONTAL &&
3654 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3655 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3656 MovDir[x][y] = MV_NO_MOVING;
3660 else if (element == EL_ROBOT ||
3661 element == EL_SATELLITE ||
3662 element == EL_PENGUIN)
3664 int attr_x = -1, attr_y = -1;
3675 for (i = 0; i < MAX_PLAYERS; i++)
3677 struct PlayerInfo *player = &stored_player[i];
3678 int jx = player->jx, jy = player->jy;
3680 if (!player->active)
3684 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3692 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3698 if (element == EL_PENGUIN)
3701 static int xy[4][2] =
3709 for (i = 0; i < 4; i++)
3711 int ex = x + xy[i % 4][0];
3712 int ey = y + xy[i % 4][1];
3714 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3723 MovDir[x][y] = MV_NO_MOVING;
3725 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3726 else if (attr_x > x)
3727 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3729 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3730 else if (attr_y > y)
3731 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3733 if (element == EL_ROBOT)
3737 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3738 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3739 Moving2Blocked(x, y, &newx, &newy);
3741 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3742 MovDelay[x][y] = 8 + 8 * !RND(3);
3744 MovDelay[x][y] = 16;
3746 else if (element == EL_PENGUIN)
3752 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3754 boolean first_horiz = RND(2);
3755 int new_move_dir = MovDir[x][y];
3758 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3759 Moving2Blocked(x, y, &newx, &newy);
3761 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3765 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3766 Moving2Blocked(x, y, &newx, &newy);
3768 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3771 MovDir[x][y] = old_move_dir;
3775 else /* (element == EL_SATELLITE) */
3781 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3783 boolean first_horiz = RND(2);
3784 int new_move_dir = MovDir[x][y];
3787 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3788 Moving2Blocked(x, y, &newx, &newy);
3790 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3794 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3795 Moving2Blocked(x, y, &newx, &newy);
3797 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3800 MovDir[x][y] = old_move_dir;
3805 else if (move_pattern == MV_TURNING_LEFT ||
3806 move_pattern == MV_TURNING_RIGHT ||
3807 move_pattern == MV_TURNING_LEFT_RIGHT ||
3808 move_pattern == MV_TURNING_RIGHT_LEFT ||
3809 move_pattern == MV_TURNING_RANDOM ||
3810 move_pattern == MV_ALL_DIRECTIONS)
3812 boolean can_turn_left =
3813 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3814 boolean can_turn_right =
3815 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3817 if (move_pattern == MV_TURNING_LEFT)
3818 MovDir[x][y] = left_dir;
3819 else if (move_pattern == MV_TURNING_RIGHT)
3820 MovDir[x][y] = right_dir;
3821 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
3822 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
3823 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
3824 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
3825 else if (move_pattern == MV_TURNING_RANDOM)
3826 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
3827 can_turn_right && !can_turn_left ? right_dir :
3828 RND(2) ? left_dir : right_dir);
3829 else if (can_turn_left && can_turn_right)
3830 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3831 else if (can_turn_left)
3832 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3833 else if (can_turn_right)
3834 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3836 MovDir[x][y] = back_dir;
3838 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3840 else if (move_pattern == MV_HORIZONTAL ||
3841 move_pattern == MV_VERTICAL)
3843 if (move_pattern & old_move_dir)
3844 MovDir[x][y] = back_dir;
3845 else if (move_pattern == MV_HORIZONTAL)
3846 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3847 else if (move_pattern == MV_VERTICAL)
3848 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3850 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3852 else if (move_pattern & MV_ANY_DIRECTION)
3854 MovDir[x][y] = move_pattern;
3855 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3857 else if (move_pattern == MV_ALONG_LEFT_SIDE)
3859 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3860 MovDir[x][y] = left_dir;
3861 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3862 MovDir[x][y] = right_dir;
3864 if (MovDir[x][y] != old_move_dir)
3865 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3867 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
3869 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3870 MovDir[x][y] = right_dir;
3871 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3872 MovDir[x][y] = left_dir;
3874 if (MovDir[x][y] != old_move_dir)
3875 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3877 else if (move_pattern == MV_TOWARDS_PLAYER ||
3878 move_pattern == MV_AWAY_FROM_PLAYER)
3880 int attr_x = -1, attr_y = -1;
3882 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
3893 for (i = 0; i < MAX_PLAYERS; i++)
3895 struct PlayerInfo *player = &stored_player[i];
3896 int jx = player->jx, jy = player->jy;
3898 if (!player->active)
3902 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3910 MovDir[x][y] = MV_NO_MOVING;
3912 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3913 else if (attr_x > x)
3914 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3916 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3917 else if (attr_y > y)
3918 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3920 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3922 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3924 boolean first_horiz = RND(2);
3925 int new_move_dir = MovDir[x][y];
3928 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3929 Moving2Blocked(x, y, &newx, &newy);
3931 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
3935 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3936 Moving2Blocked(x, y, &newx, &newy);
3938 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
3941 MovDir[x][y] = old_move_dir;
3944 else if (move_pattern == MV_WHEN_PUSHED ||
3945 move_pattern == MV_WHEN_DROPPED)
3947 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3948 MovDir[x][y] = MV_NO_MOVING;
3952 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
3954 static int test_xy[7][2] =
3964 static int test_dir[7] =
3974 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
3975 int move_preference = -1000000; /* start with very low preference */
3976 int new_move_dir = MV_NO_MOVING;
3977 int start_test = RND(4);
3980 for (i = 0; i < 4; i++)
3982 int move_dir = test_dir[start_test + i];
3983 int move_dir_preference;
3985 xx = x + test_xy[start_test + i][0];
3986 yy = y + test_xy[start_test + i][1];
3988 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
3989 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
3991 new_move_dir = move_dir;
3996 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
3999 move_dir_preference = -1 * RunnerVisit[xx][yy];
4000 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4001 move_dir_preference = PlayerVisit[xx][yy];
4003 if (move_dir_preference > move_preference)
4005 /* prefer field that has not been visited for the longest time */
4006 move_preference = move_dir_preference;
4007 new_move_dir = move_dir;
4009 else if (move_dir_preference == move_preference &&
4010 move_dir == old_move_dir)
4012 /* prefer last direction when all directions are preferred equally */
4013 move_preference = move_dir_preference;
4014 new_move_dir = move_dir;
4018 MovDir[x][y] = new_move_dir;
4019 if (old_move_dir != new_move_dir)
4024 static void TurnRound(int x, int y)
4026 int direction = MovDir[x][y];
4029 GfxDir[x][y] = MovDir[x][y];
4035 GfxDir[x][y] = MovDir[x][y];
4038 if (direction != MovDir[x][y])
4043 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4046 GfxAction[x][y] = ACTION_WAITING;
4050 static boolean JustBeingPushed(int x, int y)
4054 for (i = 0; i < MAX_PLAYERS; i++)
4056 struct PlayerInfo *player = &stored_player[i];
4058 if (player->active && player->is_pushing && player->MovPos)
4060 int next_jx = player->jx + (player->jx - player->last_jx);
4061 int next_jy = player->jy + (player->jy - player->last_jy);
4063 if (x == next_jx && y == next_jy)
4071 void StartMoving(int x, int y)
4073 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4074 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4075 int element = Feld[x][y];
4081 if (MovDelay[x][y] == 0)
4082 GfxAction[x][y] = ACTION_DEFAULT;
4084 /* !!! this should be handled more generic (not only for mole) !!! */
4085 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4086 GfxAction[x][y] = ACTION_DEFAULT;
4089 if (CAN_FALL(element) && y < lev_fieldy - 1)
4091 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4092 (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4093 if (JustBeingPushed(x, y))
4096 if (element == EL_QUICKSAND_FULL)
4098 if (IS_FREE(x, y + 1))
4100 InitMovingField(x, y, MV_DOWN);
4101 started_moving = TRUE;
4103 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4104 Store[x][y] = EL_ROCK;
4106 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4108 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4111 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4113 if (!MovDelay[x][y])
4114 MovDelay[x][y] = TILEY + 1;
4123 Feld[x][y] = EL_QUICKSAND_EMPTY;
4124 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4125 Store[x][y + 1] = Store[x][y];
4128 PlayLevelSoundAction(x, y, ACTION_FILLING);
4130 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4134 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4135 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4137 InitMovingField(x, y, MV_DOWN);
4138 started_moving = TRUE;
4140 Feld[x][y] = EL_QUICKSAND_FILLING;
4141 Store[x][y] = element;
4143 PlayLevelSoundAction(x, y, ACTION_FILLING);
4145 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4148 else if (element == EL_MAGIC_WALL_FULL)
4150 if (IS_FREE(x, y + 1))
4152 InitMovingField(x, y, MV_DOWN);
4153 started_moving = TRUE;
4155 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4156 Store[x][y] = EL_CHANGED(Store[x][y]);
4158 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4160 if (!MovDelay[x][y])
4161 MovDelay[x][y] = TILEY/4 + 1;
4170 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4171 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4172 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4176 else if (element == EL_BD_MAGIC_WALL_FULL)
4178 if (IS_FREE(x, y + 1))
4180 InitMovingField(x, y, MV_DOWN);
4181 started_moving = TRUE;
4183 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4184 Store[x][y] = EL_CHANGED2(Store[x][y]);
4186 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4188 if (!MovDelay[x][y])
4189 MovDelay[x][y] = TILEY/4 + 1;
4198 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4199 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4200 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4204 else if (CAN_PASS_MAGIC_WALL(element) &&
4205 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4206 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4208 InitMovingField(x, y, MV_DOWN);
4209 started_moving = TRUE;
4212 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4213 EL_BD_MAGIC_WALL_FILLING);
4214 Store[x][y] = element;
4217 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4219 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4224 InitMovingField(x, y, MV_DOWN);
4225 started_moving = TRUE;
4227 Store[x][y] = EL_ACID;
4229 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4230 GfxAction[x][y + 1] = ACTION_ACTIVE;
4234 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4235 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4236 (Feld[x][y + 1] == EL_BLOCKED)) ||
4237 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4238 CAN_SMASH(element) && WasJustFalling[x][y] &&
4239 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4243 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4244 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4245 WasJustMoving[x][y] && !Pushed[x][y + 1])
4247 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4248 WasJustMoving[x][y])
4253 /* this is needed for a special case not covered by calling "Impact()"
4254 from "ContinueMoving()": if an element moves to a tile directly below
4255 another element which was just falling on that tile (which was empty
4256 in the previous frame), the falling element above would just stop
4257 instead of smashing the element below (in previous version, the above
4258 element was just checked for "moving" instead of "falling", resulting
4259 in incorrect smashes caused by horizontal movement of the above
4260 element; also, the case of the player being the element to smash was
4261 simply not covered here... :-/ ) */
4264 WasJustMoving[x][y] = 0;
4265 WasJustFalling[x][y] = 0;
4270 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4272 if (MovDir[x][y] == MV_NO_MOVING)
4274 InitMovingField(x, y, MV_DOWN);
4275 started_moving = TRUE;
4278 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4280 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4281 MovDir[x][y] = MV_DOWN;
4283 InitMovingField(x, y, MV_DOWN);
4284 started_moving = TRUE;
4286 else if (element == EL_AMOEBA_DROP)
4288 Feld[x][y] = EL_AMOEBA_GROWING;
4289 Store[x][y] = EL_AMOEBA_WET;
4291 /* Store[x][y + 1] must be zero, because:
4292 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4295 #if OLD_GAME_BEHAVIOUR
4296 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4298 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4299 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4300 element != EL_DX_SUPABOMB)
4303 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4304 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4305 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4306 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4309 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4310 (IS_FREE(x - 1, y + 1) ||
4311 Feld[x - 1][y + 1] == EL_ACID));
4312 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4313 (IS_FREE(x + 1, y + 1) ||
4314 Feld[x + 1][y + 1] == EL_ACID));
4315 boolean can_fall_any = (can_fall_left || can_fall_right);
4316 boolean can_fall_both = (can_fall_left && can_fall_right);
4318 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4320 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4322 if (slippery_type == SLIPPERY_ONLY_LEFT)
4323 can_fall_right = FALSE;
4324 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4325 can_fall_left = FALSE;
4326 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4327 can_fall_right = FALSE;
4328 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4329 can_fall_left = FALSE;
4331 can_fall_any = (can_fall_left || can_fall_right);
4332 can_fall_both = (can_fall_left && can_fall_right);
4337 if (can_fall_both &&
4338 (game.emulation != EMU_BOULDERDASH &&
4339 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4340 can_fall_left = !(can_fall_right = RND(2));
4342 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4343 started_moving = TRUE;
4346 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4348 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4349 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4350 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4351 int belt_dir = game.belt_dir[belt_nr];
4353 if ((belt_dir == MV_LEFT && left_is_free) ||
4354 (belt_dir == MV_RIGHT && right_is_free))
4356 InitMovingField(x, y, belt_dir);
4357 started_moving = TRUE;
4359 GfxAction[x][y] = ACTION_DEFAULT;
4364 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4365 if (CAN_MOVE(element) && !started_moving)
4367 int move_pattern = element_info[element].move_pattern;
4370 Moving2Blocked(x, y, &newx, &newy);
4373 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4376 if ((element == EL_SATELLITE ||
4377 element == EL_BALLOON ||
4378 element == EL_SPRING)
4379 && JustBeingPushed(x, y))
4384 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4385 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4386 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4389 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4390 element, element_info[element].token_name,
4391 WasJustMoving[x][y],
4392 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4393 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4394 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4395 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4399 WasJustMoving[x][y] = 0;
4402 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4405 if (Feld[x][y] != element) /* element has changed */
4407 element = Feld[x][y];
4408 move_pattern = element_info[element].move_pattern;
4410 if (!CAN_MOVE(element))
4414 if (Feld[x][y] != element) /* element has changed */
4422 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4423 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4425 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4427 Moving2Blocked(x, y, &newx, &newy);
4428 if (Feld[newx][newy] == EL_BLOCKED)
4429 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4435 if (FrameCounter < 1 && x == 0 && y == 29)
4436 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4439 if (!MovDelay[x][y]) /* start new movement phase */
4441 /* all objects that can change their move direction after each step
4442 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4444 if (element != EL_YAMYAM &&
4445 element != EL_DARK_YAMYAM &&
4446 element != EL_PACMAN &&
4447 !(move_pattern & MV_ANY_DIRECTION) &&
4448 move_pattern != MV_TURNING_LEFT &&
4449 move_pattern != MV_TURNING_RIGHT &&
4450 move_pattern != MV_TURNING_LEFT_RIGHT &&
4451 move_pattern != MV_TURNING_RIGHT_LEFT &&
4452 move_pattern != MV_TURNING_RANDOM)
4457 if (FrameCounter < 1 && x == 0 && y == 29)
4458 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4461 if (MovDelay[x][y] && (element == EL_BUG ||
4462 element == EL_SPACESHIP ||
4463 element == EL_SP_SNIKSNAK ||
4464 element == EL_SP_ELECTRON ||
4465 element == EL_MOLE))
4466 DrawLevelField(x, y);
4470 if (MovDelay[x][y]) /* wait some time before next movement */
4475 if (element == EL_YAMYAM)
4478 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4479 DrawLevelElementAnimation(x, y, element);
4483 if (MovDelay[x][y]) /* element still has to wait some time */
4486 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4487 ResetGfxAnimation(x, y);
4491 if (GfxAction[x][y] != ACTION_WAITING)
4492 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4494 GfxAction[x][y] = ACTION_WAITING;
4498 if (element == EL_ROBOT ||
4500 element == EL_PACMAN ||
4502 element == EL_YAMYAM ||
4503 element == EL_DARK_YAMYAM)
4506 DrawLevelElementAnimation(x, y, element);
4508 DrawLevelElementAnimationIfNeeded(x, y, element);
4510 PlayLevelSoundAction(x, y, ACTION_WAITING);
4512 else if (element == EL_SP_ELECTRON)
4513 DrawLevelElementAnimationIfNeeded(x, y, element);
4514 else if (element == EL_DRAGON)
4517 int dir = MovDir[x][y];
4518 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4519 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4520 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4521 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4522 dir == MV_UP ? IMG_FLAMES_1_UP :
4523 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4524 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4527 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4530 GfxAction[x][y] = ACTION_ATTACKING;
4532 if (IS_PLAYER(x, y))
4533 DrawPlayerField(x, y);
4535 DrawLevelField(x, y);
4537 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4539 for (i = 1; i <= 3; i++)
4541 int xx = x + i * dx;
4542 int yy = y + i * dy;
4543 int sx = SCREENX(xx);
4544 int sy = SCREENY(yy);
4545 int flame_graphic = graphic + (i - 1);
4547 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4552 int flamed = MovingOrBlocked2Element(xx, yy);
4554 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4557 RemoveMovingField(xx, yy);
4559 Feld[xx][yy] = EL_FLAMES;
4560 if (IN_SCR_FIELD(sx, sy))
4562 DrawLevelFieldCrumbledSand(xx, yy);
4563 DrawGraphic(sx, sy, flame_graphic, frame);
4568 if (Feld[xx][yy] == EL_FLAMES)
4569 Feld[xx][yy] = EL_EMPTY;
4570 DrawLevelField(xx, yy);
4575 if (MovDelay[x][y]) /* element still has to wait some time */
4577 PlayLevelSoundAction(x, y, ACTION_WAITING);
4583 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4584 for all other elements GfxAction will be set by InitMovingField() */
4585 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4586 GfxAction[x][y] = ACTION_MOVING;
4590 /* now make next step */
4592 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4594 if (DONT_COLLIDE_WITH(element) &&
4595 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4596 !PLAYER_PROTECTED(newx, newy))
4599 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4603 /* player killed by element which is deadly when colliding with */
4605 KillHero(PLAYERINFO(newx, newy));
4610 else if ((element == EL_PENGUIN ||
4611 element == EL_ROBOT ||
4612 element == EL_SATELLITE ||
4613 element == EL_BALLOON ||
4614 IS_CUSTOM_ELEMENT(element)) &&
4615 IN_LEV_FIELD(newx, newy) &&
4616 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4619 Store[x][y] = EL_ACID;
4621 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4623 if (Feld[newx][newy] == EL_EXIT_OPEN)
4627 DrawLevelField(x, y);
4629 Feld[x][y] = EL_EMPTY;
4630 DrawLevelField(x, y);
4633 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4634 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4635 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4637 local_player->friends_still_needed--;
4638 if (!local_player->friends_still_needed &&
4639 !local_player->GameOver && AllPlayersGone)
4640 local_player->LevelSolved = local_player->GameOver = TRUE;
4644 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4646 if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4647 DrawLevelField(newx, newy);
4649 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4651 else if (!IS_FREE(newx, newy))
4653 GfxAction[x][y] = ACTION_WAITING;
4655 if (IS_PLAYER(x, y))
4656 DrawPlayerField(x, y);
4658 DrawLevelField(x, y);
4663 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4665 if (IS_FOOD_PIG(Feld[newx][newy]))
4667 if (IS_MOVING(newx, newy))
4668 RemoveMovingField(newx, newy);
4671 Feld[newx][newy] = EL_EMPTY;
4672 DrawLevelField(newx, newy);
4675 PlayLevelSound(x, y, SND_PIG_DIGGING);
4677 else if (!IS_FREE(newx, newy))
4679 if (IS_PLAYER(x, y))
4680 DrawPlayerField(x, y);
4682 DrawLevelField(x, y);
4689 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4692 else if (IS_CUSTOM_ELEMENT(element) &&
4693 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4695 int new_element = Feld[newx][newy];
4698 /* no element can dig solid indestructible elements */
4699 if (IS_INDESTRUCTIBLE(new_element) &&
4700 !IS_DIGGABLE(new_element) &&
4701 !IS_COLLECTIBLE(new_element))
4704 if (AmoebaNr[newx][newy] &&
4705 (new_element == EL_AMOEBA_FULL ||
4706 new_element == EL_BD_AMOEBA ||
4707 new_element == EL_AMOEBA_GROWING))
4709 AmoebaCnt[AmoebaNr[newx][newy]]--;
4710 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4713 if (IS_MOVING(newx, newy))
4714 RemoveMovingField(newx, newy);
4717 RemoveField(newx, newy);
4718 DrawLevelField(newx, newy);
4721 sound = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4722 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4725 PlayLevelSoundAction(x, y, sound);
4727 if (move_pattern & MV_MAZE_RUNNER_STYLE)
4729 RunnerVisit[x][y] = FrameCounter;
4730 PlayerVisit[x][y] /= 8; /* expire player visit path */
4733 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4735 if (!IS_FREE(newx, newy))
4737 if (IS_PLAYER(x, y))
4738 DrawPlayerField(x, y);
4740 DrawLevelField(x, y);
4746 boolean wanna_flame = !RND(10);
4747 int dx = newx - x, dy = newy - y;
4748 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4749 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4750 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4751 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4752 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4753 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4756 IS_CLASSIC_ENEMY(element1) ||
4757 IS_CLASSIC_ENEMY(element2)) &&
4758 element1 != EL_DRAGON && element2 != EL_DRAGON &&
4759 element1 != EL_FLAMES && element2 != EL_FLAMES)
4762 ResetGfxAnimation(x, y);
4763 GfxAction[x][y] = ACTION_ATTACKING;
4766 if (IS_PLAYER(x, y))
4767 DrawPlayerField(x, y);
4769 DrawLevelField(x, y);
4771 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4773 MovDelay[x][y] = 50;
4775 Feld[newx][newy] = EL_FLAMES;
4776 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4777 Feld[newx1][newy1] = EL_FLAMES;
4778 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4779 Feld[newx2][newy2] = EL_FLAMES;
4785 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4786 Feld[newx][newy] == EL_DIAMOND)
4788 if (IS_MOVING(newx, newy))
4789 RemoveMovingField(newx, newy);
4792 Feld[newx][newy] = EL_EMPTY;
4793 DrawLevelField(newx, newy);
4796 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4798 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4799 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4801 if (AmoebaNr[newx][newy])
4803 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4804 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4805 Feld[newx][newy] == EL_BD_AMOEBA)
4806 AmoebaCnt[AmoebaNr[newx][newy]]--;
4809 if (IS_MOVING(newx, newy))
4810 RemoveMovingField(newx, newy);
4813 Feld[newx][newy] = EL_EMPTY;
4814 DrawLevelField(newx, newy);
4817 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4819 else if ((element == EL_PACMAN || element == EL_MOLE)
4820 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4822 if (AmoebaNr[newx][newy])
4824 AmoebaCnt2[AmoebaNr[newx][newy]]--;
4825 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4826 Feld[newx][newy] == EL_BD_AMOEBA)
4827 AmoebaCnt[AmoebaNr[newx][newy]]--;
4830 if (element == EL_MOLE)
4832 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4833 PlayLevelSound(x, y, SND_MOLE_DIGGING);
4835 ResetGfxAnimation(x, y);
4836 GfxAction[x][y] = ACTION_DIGGING;
4837 DrawLevelField(x, y);
4839 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
4841 return; /* wait for shrinking amoeba */
4843 else /* element == EL_PACMAN */
4845 Feld[newx][newy] = EL_EMPTY;
4846 DrawLevelField(newx, newy);
4847 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4850 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4851 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4852 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4854 /* wait for shrinking amoeba to completely disappear */
4857 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4859 /* object was running against a wall */
4864 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
4865 DrawLevelElementAnimation(x, y, element);
4867 if (element == EL_BUG ||
4868 element == EL_SPACESHIP ||
4869 element == EL_SP_SNIKSNAK)
4870 DrawLevelField(x, y);
4871 else if (element == EL_MOLE)
4872 DrawLevelField(x, y);
4873 else if (element == EL_BD_BUTTERFLY ||
4874 element == EL_BD_FIREFLY)
4875 DrawLevelElementAnimationIfNeeded(x, y, element);
4876 else if (element == EL_SATELLITE)
4877 DrawLevelElementAnimationIfNeeded(x, y, element);
4878 else if (element == EL_SP_ELECTRON)
4879 DrawLevelElementAnimationIfNeeded(x, y, element);
4882 if (DONT_TOUCH(element))
4883 TestIfBadThingTouchesHero(x, y);
4886 PlayLevelSoundAction(x, y, ACTION_WAITING);
4892 InitMovingField(x, y, MovDir[x][y]);
4894 PlayLevelSoundAction(x, y, ACTION_MOVING);
4898 ContinueMoving(x, y);
4901 void ContinueMoving(int x, int y)
4903 int element = Feld[x][y];
4904 int direction = MovDir[x][y];
4905 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4906 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4907 int newx = x + dx, newy = y + dy;
4909 int nextx = newx + dx, nexty = newy + dy;
4911 boolean pushed = Pushed[x][y];
4913 MovPos[x][y] += getElementMoveStepsize(x, y);
4915 if (pushed) /* special case: moving object pushed by player */
4916 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4918 if (ABS(MovPos[x][y]) < TILEX)
4920 DrawLevelField(x, y);
4922 return; /* element is still moving */
4925 /* element reached destination field */
4927 Feld[x][y] = EL_EMPTY;
4928 Feld[newx][newy] = element;
4929 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
4931 if (element == EL_MOLE)
4933 Feld[x][y] = EL_SAND;
4935 DrawLevelFieldCrumbledSandNeighbours(x, y);
4937 else if (element == EL_QUICKSAND_FILLING)
4939 element = Feld[newx][newy] = get_next_element(element);
4940 Store[newx][newy] = Store[x][y];
4942 else if (element == EL_QUICKSAND_EMPTYING)
4944 Feld[x][y] = get_next_element(element);
4945 element = Feld[newx][newy] = Store[x][y];
4947 else if (element == EL_MAGIC_WALL_FILLING)
4949 element = Feld[newx][newy] = get_next_element(element);
4950 if (!game.magic_wall_active)
4951 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4952 Store[newx][newy] = Store[x][y];
4954 else if (element == EL_MAGIC_WALL_EMPTYING)
4956 Feld[x][y] = get_next_element(element);
4957 if (!game.magic_wall_active)
4958 Feld[x][y] = EL_MAGIC_WALL_DEAD;
4959 element = Feld[newx][newy] = Store[x][y];
4961 else if (element == EL_BD_MAGIC_WALL_FILLING)
4963 element = Feld[newx][newy] = get_next_element(element);
4964 if (!game.magic_wall_active)
4965 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4966 Store[newx][newy] = Store[x][y];
4968 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4970 Feld[x][y] = get_next_element(element);
4971 if (!game.magic_wall_active)
4972 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4973 element = Feld[newx][newy] = Store[x][y];
4975 else if (element == EL_AMOEBA_DROPPING)
4977 Feld[x][y] = get_next_element(element);
4978 element = Feld[newx][newy] = Store[x][y];
4980 else if (element == EL_SOKOBAN_OBJECT)
4983 Feld[x][y] = Back[x][y];
4985 if (Back[newx][newy])
4986 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4988 Back[x][y] = Back[newx][newy] = 0;
4990 else if (Store[x][y] == EL_ACID)
4992 element = Feld[newx][newy] = EL_ACID;
4996 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4997 MovDelay[newx][newy] = 0;
4999 /* copy element change control values to new field */
5000 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5001 ChangePage[newx][newy] = ChangePage[x][y];
5002 Changed[newx][newy] = Changed[x][y];
5003 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5005 ChangeDelay[x][y] = 0;
5006 ChangePage[x][y] = -1;
5007 Changed[x][y] = CE_BITMASK_DEFAULT;
5008 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5010 /* copy animation control values to new field */
5011 GfxFrame[newx][newy] = GfxFrame[x][y];
5012 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5013 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5014 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5016 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5018 ResetGfxAnimation(x, y); /* reset animation values for old field */
5020 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y))
5022 int new_element = element_info[element].move_leave_element;
5024 Feld[x][y] = new_element;
5026 if (new_element != EL_EMPTY)
5028 InitField(x, y, FALSE);
5030 TestIfElementTouchesCustomElement(x, y);
5032 if (GFX_CRUMBLED(new_element))
5033 DrawLevelFieldCrumbledSandNeighbours(x, y);
5038 /* 2.1.1 (does not work correctly for spring) */
5039 if (!CAN_MOVE(element))
5040 MovDir[newx][newy] = 0;
5044 /* (does not work for falling objects that slide horizontally) */
5045 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5046 MovDir[newx][newy] = 0;
5049 if (!CAN_MOVE(element) ||
5050 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5051 MovDir[newx][newy] = 0;
5054 if (!CAN_MOVE(element) ||
5055 (CAN_FALL(element) && direction == MV_DOWN))
5056 GfxDir[x][y] = MovDir[newx][newy] = 0;
5061 DrawLevelField(x, y);
5062 DrawLevelField(newx, newy);
5064 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5066 /* prevent pushed element from moving on in pushed direction */
5067 if (pushed && CAN_MOVE(element) &&
5068 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5069 !(element_info[element].move_pattern & direction))
5070 TurnRound(newx, newy);
5072 if (!pushed) /* special case: moving object pushed by player */
5074 WasJustMoving[newx][newy] = 3;
5076 if (CAN_FALL(element) && direction == MV_DOWN)
5077 WasJustFalling[newx][newy] = 3;
5080 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5082 TestIfBadThingTouchesHero(newx, newy);
5083 TestIfBadThingTouchesFriend(newx, newy);
5085 if (!IS_CUSTOM_ELEMENT(element))
5086 TestIfBadThingTouchesOtherBadThing(newx, newy);
5088 else if (element == EL_PENGUIN)
5089 TestIfFriendTouchesBadThing(newx, newy);
5091 if (CAN_FALL(element) && direction == MV_DOWN &&
5092 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5096 TestIfElementTouchesCustomElement(x, y); /* for empty space */
5100 if (ChangePage[newx][newy] != -1) /* delayed change */
5101 ChangeElement(newx, newy, ChangePage[newx][newy]);
5106 TestIfElementHitsCustomElement(newx, newy, direction);
5110 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5112 int hitting_element = Feld[newx][newy];
5114 /* !!! fix side (direction) orientation here and elsewhere !!! */
5115 CheckElementSideChange(newx, newy, hitting_element,
5116 direction, CE_HITTING_SOMETHING, -1);
5119 if (IN_LEV_FIELD(nextx, nexty))
5121 static int opposite_directions[] =
5128 int move_dir_bit = MV_DIR_BIT(direction);
5129 int opposite_direction = opposite_directions[move_dir_bit];
5130 int hitting_side = direction;
5131 int touched_side = opposite_direction;
5132 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5133 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5134 MovDir[nextx][nexty] != direction ||
5135 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5141 CheckElementSideChange(nextx, nexty, touched_element,
5142 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5144 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5145 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5147 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5149 struct ElementChangeInfo *change =
5150 &element_info[hitting_element].change_page[i];
5152 if (change->can_change &&
5153 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5154 change->sides & touched_side &&
5155 change->trigger_element == touched_element)
5157 CheckElementSideChange(newx, newy, hitting_element,
5158 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5164 if (IS_CUSTOM_ELEMENT(touched_element) &&
5165 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5167 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5169 struct ElementChangeInfo *change =
5170 &element_info[touched_element].change_page[i];
5172 if (change->can_change &&
5173 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5174 change->sides & hitting_side &&
5175 change->trigger_element == hitting_element)
5177 CheckElementSideChange(nextx, nexty, touched_element,
5178 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5189 TestIfPlayerTouchesCustomElement(newx, newy);
5190 TestIfElementTouchesCustomElement(newx, newy);
5193 int AmoebeNachbarNr(int ax, int ay)
5196 int element = Feld[ax][ay];
5198 static int xy[4][2] =
5206 for (i = 0; i < 4; i++)
5208 int x = ax + xy[i][0];
5209 int y = ay + xy[i][1];
5211 if (!IN_LEV_FIELD(x, y))
5214 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5215 group_nr = AmoebaNr[x][y];
5221 void AmoebenVereinigen(int ax, int ay)
5223 int i, x, y, xx, yy;
5224 int new_group_nr = AmoebaNr[ax][ay];
5225 static int xy[4][2] =
5233 if (new_group_nr == 0)
5236 for (i = 0; i < 4; i++)
5241 if (!IN_LEV_FIELD(x, y))
5244 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5245 Feld[x][y] == EL_BD_AMOEBA ||
5246 Feld[x][y] == EL_AMOEBA_DEAD) &&
5247 AmoebaNr[x][y] != new_group_nr)
5249 int old_group_nr = AmoebaNr[x][y];
5251 if (old_group_nr == 0)
5254 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5255 AmoebaCnt[old_group_nr] = 0;
5256 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5257 AmoebaCnt2[old_group_nr] = 0;
5259 for (yy = 0; yy < lev_fieldy; yy++)
5261 for (xx = 0; xx < lev_fieldx; xx++)
5263 if (AmoebaNr[xx][yy] == old_group_nr)
5264 AmoebaNr[xx][yy] = new_group_nr;
5271 void AmoebeUmwandeln(int ax, int ay)
5275 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5277 int group_nr = AmoebaNr[ax][ay];
5282 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5283 printf("AmoebeUmwandeln(): This should never happen!\n");
5288 for (y = 0; y < lev_fieldy; y++)
5290 for (x = 0; x < lev_fieldx; x++)
5292 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5295 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5299 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5300 SND_AMOEBA_TURNING_TO_GEM :
5301 SND_AMOEBA_TURNING_TO_ROCK));
5306 static int xy[4][2] =
5314 for (i = 0; i < 4; i++)
5319 if (!IN_LEV_FIELD(x, y))
5322 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5324 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5325 SND_AMOEBA_TURNING_TO_GEM :
5326 SND_AMOEBA_TURNING_TO_ROCK));
5333 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5336 int group_nr = AmoebaNr[ax][ay];
5337 boolean done = FALSE;
5342 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5343 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5348 for (y = 0; y < lev_fieldy; y++)
5350 for (x = 0; x < lev_fieldx; x++)
5352 if (AmoebaNr[x][y] == group_nr &&
5353 (Feld[x][y] == EL_AMOEBA_DEAD ||
5354 Feld[x][y] == EL_BD_AMOEBA ||
5355 Feld[x][y] == EL_AMOEBA_GROWING))
5358 Feld[x][y] = new_element;
5359 InitField(x, y, FALSE);
5360 DrawLevelField(x, y);
5367 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5368 SND_BD_AMOEBA_TURNING_TO_ROCK :
5369 SND_BD_AMOEBA_TURNING_TO_GEM));
5372 void AmoebeWaechst(int x, int y)
5374 static unsigned long sound_delay = 0;
5375 static unsigned long sound_delay_value = 0;
5377 if (!MovDelay[x][y]) /* start new growing cycle */
5381 if (DelayReached(&sound_delay, sound_delay_value))
5384 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5386 if (Store[x][y] == EL_BD_AMOEBA)
5387 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5389 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5391 sound_delay_value = 30;
5395 if (MovDelay[x][y]) /* wait some time before growing bigger */
5398 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5400 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5401 6 - MovDelay[x][y]);
5403 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5406 if (!MovDelay[x][y])
5408 Feld[x][y] = Store[x][y];
5410 DrawLevelField(x, y);
5415 void AmoebaDisappearing(int x, int y)
5417 static unsigned long sound_delay = 0;
5418 static unsigned long sound_delay_value = 0;
5420 if (!MovDelay[x][y]) /* start new shrinking cycle */
5424 if (DelayReached(&sound_delay, sound_delay_value))
5425 sound_delay_value = 30;
5428 if (MovDelay[x][y]) /* wait some time before shrinking */
5431 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5433 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5434 6 - MovDelay[x][y]);
5436 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5439 if (!MovDelay[x][y])
5441 Feld[x][y] = EL_EMPTY;
5442 DrawLevelField(x, y);
5444 /* don't let mole enter this field in this cycle;
5445 (give priority to objects falling to this field from above) */
5451 void AmoebeAbleger(int ax, int ay)
5454 int element = Feld[ax][ay];
5455 int graphic = el2img(element);
5456 int newax = ax, neway = ay;
5457 static int xy[4][2] =
5465 if (!level.amoeba_speed)
5467 Feld[ax][ay] = EL_AMOEBA_DEAD;
5468 DrawLevelField(ax, ay);
5472 if (IS_ANIMATED(graphic))
5473 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5475 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5476 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5478 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5481 if (MovDelay[ax][ay])
5485 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5488 int x = ax + xy[start][0];
5489 int y = ay + xy[start][1];
5491 if (!IN_LEV_FIELD(x, y))
5494 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5495 if (IS_FREE(x, y) ||
5496 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5502 if (newax == ax && neway == ay)
5505 else /* normal or "filled" (BD style) amoeba */
5508 boolean waiting_for_player = FALSE;
5510 for (i = 0; i < 4; i++)
5512 int j = (start + i) % 4;
5513 int x = ax + xy[j][0];
5514 int y = ay + xy[j][1];
5516 if (!IN_LEV_FIELD(x, y))
5519 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5520 if (IS_FREE(x, y) ||
5521 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5527 else if (IS_PLAYER(x, y))
5528 waiting_for_player = TRUE;
5531 if (newax == ax && neway == ay) /* amoeba cannot grow */
5533 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5535 Feld[ax][ay] = EL_AMOEBA_DEAD;
5536 DrawLevelField(ax, ay);
5537 AmoebaCnt[AmoebaNr[ax][ay]]--;
5539 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5541 if (element == EL_AMOEBA_FULL)
5542 AmoebeUmwandeln(ax, ay);
5543 else if (element == EL_BD_AMOEBA)
5544 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5549 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5551 /* amoeba gets larger by growing in some direction */
5553 int new_group_nr = AmoebaNr[ax][ay];
5556 if (new_group_nr == 0)
5558 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5559 printf("AmoebeAbleger(): This should never happen!\n");
5564 AmoebaNr[newax][neway] = new_group_nr;
5565 AmoebaCnt[new_group_nr]++;
5566 AmoebaCnt2[new_group_nr]++;
5568 /* if amoeba touches other amoeba(s) after growing, unify them */
5569 AmoebenVereinigen(newax, neway);
5571 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5573 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5579 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5580 (neway == lev_fieldy - 1 && newax != ax))
5582 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5583 Store[newax][neway] = element;
5585 else if (neway == ay)
5587 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5589 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5591 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5596 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5597 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5598 Store[ax][ay] = EL_AMOEBA_DROP;
5599 ContinueMoving(ax, ay);
5603 DrawLevelField(newax, neway);
5606 void Life(int ax, int ay)
5609 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5611 int element = Feld[ax][ay];
5612 int graphic = el2img(element);
5613 boolean changed = FALSE;
5615 if (IS_ANIMATED(graphic))
5616 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5621 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5622 MovDelay[ax][ay] = life_time;
5624 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5627 if (MovDelay[ax][ay])
5631 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5633 int xx = ax+x1, yy = ay+y1;
5636 if (!IN_LEV_FIELD(xx, yy))
5639 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5641 int x = xx+x2, y = yy+y2;
5643 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5646 if (((Feld[x][y] == element ||
5647 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5649 (IS_FREE(x, y) && Stop[x][y]))
5653 if (xx == ax && yy == ay) /* field in the middle */
5655 if (nachbarn < life[0] || nachbarn > life[1])
5657 Feld[xx][yy] = EL_EMPTY;
5659 DrawLevelField(xx, yy);
5660 Stop[xx][yy] = TRUE;
5664 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5665 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5666 { /* free border field */
5667 if (nachbarn >= life[2] && nachbarn <= life[3])
5669 Feld[xx][yy] = element;
5670 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5672 DrawLevelField(xx, yy);
5673 Stop[xx][yy] = TRUE;
5680 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5681 SND_GAME_OF_LIFE_GROWING);
5684 static void InitRobotWheel(int x, int y)
5686 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5689 static void RunRobotWheel(int x, int y)
5691 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5694 static void StopRobotWheel(int x, int y)
5696 if (ZX == x && ZY == y)
5700 static void InitTimegateWheel(int x, int y)
5702 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5705 static void RunTimegateWheel(int x, int y)
5707 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5710 void CheckExit(int x, int y)
5712 if (local_player->gems_still_needed > 0 ||
5713 local_player->sokobanfields_still_needed > 0 ||
5714 local_player->lights_still_needed > 0)
5716 int element = Feld[x][y];
5717 int graphic = el2img(element);
5719 if (IS_ANIMATED(graphic))
5720 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5725 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5728 Feld[x][y] = EL_EXIT_OPENING;
5730 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5733 void CheckExitSP(int x, int y)
5735 if (local_player->gems_still_needed > 0)
5737 int element = Feld[x][y];
5738 int graphic = el2img(element);
5740 if (IS_ANIMATED(graphic))
5741 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5746 if (AllPlayersGone) /* do not re-open exit door closed after last player */
5749 Feld[x][y] = EL_SP_EXIT_OPENING;
5751 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5754 static void CloseAllOpenTimegates()
5758 for (y = 0; y < lev_fieldy; y++)
5760 for (x = 0; x < lev_fieldx; x++)
5762 int element = Feld[x][y];
5764 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5766 Feld[x][y] = EL_TIMEGATE_CLOSING;
5768 PlayLevelSoundAction(x, y, ACTION_CLOSING);
5770 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5777 void EdelsteinFunkeln(int x, int y)
5779 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5782 if (Feld[x][y] == EL_BD_DIAMOND)
5785 if (MovDelay[x][y] == 0) /* next animation frame */
5786 MovDelay[x][y] = 11 * !SimpleRND(500);
5788 if (MovDelay[x][y] != 0) /* wait some time before next frame */
5792 if (setup.direct_draw && MovDelay[x][y])
5793 SetDrawtoField(DRAW_BUFFERED);
5795 DrawLevelElementAnimation(x, y, Feld[x][y]);
5797 if (MovDelay[x][y] != 0)
5799 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5800 10 - MovDelay[x][y]);
5802 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5804 if (setup.direct_draw)
5808 dest_x = FX + SCREENX(x) * TILEX;
5809 dest_y = FY + SCREENY(y) * TILEY;
5811 BlitBitmap(drawto_field, window,
5812 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5813 SetDrawtoField(DRAW_DIRECT);
5819 void MauerWaechst(int x, int y)
5823 if (!MovDelay[x][y]) /* next animation frame */
5824 MovDelay[x][y] = 3 * delay;
5826 if (MovDelay[x][y]) /* wait some time before next frame */
5830 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5832 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5833 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5835 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5838 if (!MovDelay[x][y])
5840 if (MovDir[x][y] == MV_LEFT)
5842 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5843 DrawLevelField(x - 1, y);
5845 else if (MovDir[x][y] == MV_RIGHT)
5847 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5848 DrawLevelField(x + 1, y);
5850 else if (MovDir[x][y] == MV_UP)
5852 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5853 DrawLevelField(x, y - 1);
5857 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5858 DrawLevelField(x, y + 1);
5861 Feld[x][y] = Store[x][y];
5863 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5864 DrawLevelField(x, y);
5869 void MauerAbleger(int ax, int ay)
5871 int element = Feld[ax][ay];
5872 int graphic = el2img(element);
5873 boolean oben_frei = FALSE, unten_frei = FALSE;
5874 boolean links_frei = FALSE, rechts_frei = FALSE;
5875 boolean oben_massiv = FALSE, unten_massiv = FALSE;
5876 boolean links_massiv = FALSE, rechts_massiv = FALSE;
5877 boolean new_wall = FALSE;
5879 if (IS_ANIMATED(graphic))
5880 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5882 if (!MovDelay[ax][ay]) /* start building new wall */
5883 MovDelay[ax][ay] = 6;
5885 if (MovDelay[ax][ay]) /* wait some time before building new wall */
5888 if (MovDelay[ax][ay])
5892 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5894 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5896 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5898 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5901 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5902 element == EL_EXPANDABLE_WALL_ANY)
5906 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5907 Store[ax][ay-1] = element;
5908 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5909 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5910 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5911 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5916 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5917 Store[ax][ay+1] = element;
5918 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5919 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5920 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5921 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5926 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5927 element == EL_EXPANDABLE_WALL_ANY ||
5928 element == EL_EXPANDABLE_WALL)
5932 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5933 Store[ax-1][ay] = element;
5934 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5935 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5936 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5937 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5943 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5944 Store[ax+1][ay] = element;
5945 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5946 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5947 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5948 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5953 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5954 DrawLevelField(ax, ay);
5956 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5958 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5959 unten_massiv = TRUE;
5960 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5961 links_massiv = TRUE;
5962 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5963 rechts_massiv = TRUE;
5965 if (((oben_massiv && unten_massiv) ||
5966 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5967 element == EL_EXPANDABLE_WALL) &&
5968 ((links_massiv && rechts_massiv) ||
5969 element == EL_EXPANDABLE_WALL_VERTICAL))
5970 Feld[ax][ay] = EL_WALL;
5974 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5976 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5980 void CheckForDragon(int x, int y)
5983 boolean dragon_found = FALSE;
5984 static int xy[4][2] =
5992 for (i = 0; i < 4; i++)
5994 for (j = 0; j < 4; j++)
5996 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5998 if (IN_LEV_FIELD(xx, yy) &&
5999 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6001 if (Feld[xx][yy] == EL_DRAGON)
6002 dragon_found = TRUE;
6011 for (i = 0; i < 4; i++)
6013 for (j = 0; j < 3; j++)
6015 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6017 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6019 Feld[xx][yy] = EL_EMPTY;
6020 DrawLevelField(xx, yy);
6029 static void InitBuggyBase(int x, int y)
6031 int element = Feld[x][y];
6032 int activating_delay = FRAMES_PER_SECOND / 4;
6035 (element == EL_SP_BUGGY_BASE ?
6036 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6037 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6039 element == EL_SP_BUGGY_BASE_ACTIVE ?
6040 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6043 static void WarnBuggyBase(int x, int y)
6046 static int xy[4][2] =
6054 for (i = 0; i < 4; i++)
6056 int xx = x + xy[i][0], yy = y + xy[i][1];
6058 if (IS_PLAYER(xx, yy))
6060 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6067 static void InitTrap(int x, int y)
6069 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6072 static void ActivateTrap(int x, int y)
6074 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6077 static void ChangeActiveTrap(int x, int y)
6079 int graphic = IMG_TRAP_ACTIVE;
6081 /* if new animation frame was drawn, correct crumbled sand border */
6082 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6083 DrawLevelFieldCrumbledSand(x, y);
6086 static void ChangeElementNowExt(int x, int y, int target_element)
6088 /* check if element under player changes from accessible to unaccessible
6089 (needed for special case of dropping element which then changes) */
6090 if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
6091 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6098 Feld[x][y] = target_element;
6100 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6102 ResetGfxAnimation(x, y);
6103 ResetRandomAnimationValue(x, y);
6105 InitField(x, y, FALSE);
6106 if (CAN_MOVE(Feld[x][y]))
6109 DrawLevelField(x, y);
6111 if (GFX_CRUMBLED(Feld[x][y]))
6112 DrawLevelFieldCrumbledSandNeighbours(x, y);
6114 TestIfBadThingTouchesHero(x, y);
6115 TestIfPlayerTouchesCustomElement(x, y);
6116 TestIfElementTouchesCustomElement(x, y);
6118 if (ELEM_IS_PLAYER(target_element))
6119 RelocatePlayer(x, y, target_element);
6122 static boolean ChangeElementNow(int x, int y, int element, int page)
6124 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6126 /* always use default change event to prevent running into a loop */
6127 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6128 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6130 /* do not change already changed elements with same change event */
6132 if (Changed[x][y] & ChangeEvent[x][y])
6139 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6141 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6143 if (change->explode)
6150 if (change->use_content)
6152 boolean complete_change = TRUE;
6153 boolean can_change[3][3];
6156 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6158 boolean half_destructible;
6159 int ex = x + xx - 1;
6160 int ey = y + yy - 1;
6163 can_change[xx][yy] = TRUE;
6165 if (ex == x && ey == y) /* do not check changing element itself */
6168 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6170 can_change[xx][yy] = FALSE; /* do not change empty borders */
6175 if (!IN_LEV_FIELD(ex, ey))
6177 can_change[xx][yy] = FALSE;
6178 complete_change = FALSE;
6185 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6186 e = MovingOrBlocked2Element(ex, ey);
6188 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6190 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6191 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6192 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6194 can_change[xx][yy] = FALSE;
6195 complete_change = FALSE;
6199 if (!change->only_complete || complete_change)
6201 boolean something_has_changed = FALSE;
6203 if (change->only_complete && change->use_random_change &&
6204 RND(100) < change->random)
6207 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6209 int ex = x + xx - 1;
6210 int ey = y + yy - 1;
6212 if (can_change[xx][yy] && (!change->use_random_change ||
6213 RND(100) < change->random))
6215 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6216 RemoveMovingField(ex, ey);
6218 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6220 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6222 something_has_changed = TRUE;
6224 /* for symmetry reasons, freeze newly created border elements */
6225 if (ex != x || ey != y)
6226 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6230 if (something_has_changed)
6231 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6236 ChangeElementNowExt(x, y, change->target_element);
6238 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6244 static void ChangeElement(int x, int y, int page)
6246 int element = MovingOrBlocked2Element(x, y);
6247 struct ElementInfo *ei = &element_info[element];
6248 struct ElementChangeInfo *change = &ei->change_page[page];
6252 if (!CAN_CHANGE(element))
6255 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6256 x, y, element, element_info[element].token_name);
6257 printf("ChangeElement(): This should never happen!\n");
6263 if (ChangeDelay[x][y] == 0) /* initialize element change */
6265 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6266 RND(change->delay_random * change->delay_frames)) + 1;
6268 ResetGfxAnimation(x, y);
6269 ResetRandomAnimationValue(x, y);
6271 if (change->pre_change_function)
6272 change->pre_change_function(x, y);
6275 ChangeDelay[x][y]--;
6277 if (ChangeDelay[x][y] != 0) /* continue element change */
6279 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6281 if (IS_ANIMATED(graphic))
6282 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6284 if (change->change_function)
6285 change->change_function(x, y);
6287 else /* finish element change */
6289 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6291 page = ChangePage[x][y];
6292 ChangePage[x][y] = -1;
6296 if (IS_MOVING(x, y) && !change->explode)
6298 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6301 ChangeDelay[x][y] = 1; /* try change after next move step */
6302 ChangePage[x][y] = page; /* remember page to use for change */
6307 if (ChangeElementNow(x, y, element, page))
6309 if (change->post_change_function)
6310 change->post_change_function(x, y);
6315 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6316 int trigger_element,
6322 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6325 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6327 int element = EL_CUSTOM_START + i;
6329 boolean change_element = FALSE;
6332 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6335 for (j = 0; j < element_info[element].num_change_pages; j++)
6337 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6339 if (change->can_change &&
6341 change->events & CH_EVENT_BIT(trigger_event) &&
6343 change->sides & trigger_side &&
6344 change->trigger_element == trigger_element)
6347 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6348 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6349 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6352 change_element = TRUE;
6359 if (!change_element)
6362 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6365 if (x == lx && y == ly) /* do not change trigger element itself */
6369 if (Feld[x][y] == element)
6371 ChangeDelay[x][y] = 1;
6372 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6373 ChangeElement(x, y, page);
6381 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6384 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6388 static boolean CheckElementSideChange(int x, int y, int element, int side,
6389 int trigger_event, int page)
6391 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6394 if (Feld[x][y] == EL_BLOCKED)
6396 Blocked2Moving(x, y, &x, &y);
6397 element = Feld[x][y];
6401 page = element_info[element].event_page_nr[trigger_event];
6403 if (!(element_info[element].change_page[page].sides & side))
6406 ChangeDelay[x][y] = 1;
6407 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6408 ChangeElement(x, y, page);
6413 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6415 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6418 static void PlayPlayerSound(struct PlayerInfo *player)
6420 int jx = player->jx, jy = player->jy;
6421 int element = player->element_nr;
6422 int last_action = player->last_action_waiting;
6423 int action = player->action_waiting;
6425 if (player->is_waiting)
6427 if (action != last_action)
6428 PlayLevelSoundElementAction(jx, jy, element, action);
6430 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6434 if (action != last_action)
6435 StopSound(element_info[element].sound[last_action]);
6437 if (last_action == ACTION_SLEEPING)
6438 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6442 static void PlayAllPlayersSound()
6446 for (i = 0; i < MAX_PLAYERS; i++)
6447 if (stored_player[i].active)
6448 PlayPlayerSound(&stored_player[i]);
6451 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6453 boolean last_waiting = player->is_waiting;
6454 int move_dir = player->MovDir;
6456 player->last_action_waiting = player->action_waiting;
6460 if (!last_waiting) /* not waiting -> waiting */
6462 player->is_waiting = TRUE;
6464 player->frame_counter_bored =
6466 game.player_boring_delay_fixed +
6467 SimpleRND(game.player_boring_delay_random);
6468 player->frame_counter_sleeping =
6470 game.player_sleeping_delay_fixed +
6471 SimpleRND(game.player_sleeping_delay_random);
6473 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6476 if (game.player_sleeping_delay_fixed +
6477 game.player_sleeping_delay_random > 0 &&
6478 player->anim_delay_counter == 0 &&
6479 player->post_delay_counter == 0 &&
6480 FrameCounter >= player->frame_counter_sleeping)
6481 player->is_sleeping = TRUE;
6482 else if (game.player_boring_delay_fixed +
6483 game.player_boring_delay_random > 0 &&
6484 FrameCounter >= player->frame_counter_bored)
6485 player->is_bored = TRUE;
6487 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6488 player->is_bored ? ACTION_BORING :
6491 if (player->is_sleeping)
6493 if (player->num_special_action_sleeping > 0)
6495 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6497 int last_special_action = player->special_action_sleeping;
6498 int num_special_action = player->num_special_action_sleeping;
6499 int special_action =
6500 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6501 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6502 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6503 last_special_action + 1 : ACTION_SLEEPING);
6504 int special_graphic =
6505 el_act_dir2img(player->element_nr, special_action, move_dir);
6507 player->anim_delay_counter =
6508 graphic_info[special_graphic].anim_delay_fixed +
6509 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6510 player->post_delay_counter =
6511 graphic_info[special_graphic].post_delay_fixed +
6512 SimpleRND(graphic_info[special_graphic].post_delay_random);
6514 player->special_action_sleeping = special_action;
6517 if (player->anim_delay_counter > 0)
6519 player->action_waiting = player->special_action_sleeping;
6520 player->anim_delay_counter--;
6522 else if (player->post_delay_counter > 0)
6524 player->post_delay_counter--;
6528 else if (player->is_bored)
6530 if (player->num_special_action_bored > 0)
6532 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6534 int special_action =
6535 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6536 int special_graphic =
6537 el_act_dir2img(player->element_nr, special_action, move_dir);
6539 player->anim_delay_counter =
6540 graphic_info[special_graphic].anim_delay_fixed +
6541 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6542 player->post_delay_counter =
6543 graphic_info[special_graphic].post_delay_fixed +
6544 SimpleRND(graphic_info[special_graphic].post_delay_random);
6546 player->special_action_bored = special_action;
6549 if (player->anim_delay_counter > 0)
6551 player->action_waiting = player->special_action_bored;
6552 player->anim_delay_counter--;
6554 else if (player->post_delay_counter > 0)
6556 player->post_delay_counter--;
6561 else if (last_waiting) /* waiting -> not waiting */
6563 player->is_waiting = FALSE;
6564 player->is_bored = FALSE;
6565 player->is_sleeping = FALSE;
6567 player->frame_counter_bored = -1;
6568 player->frame_counter_sleeping = -1;
6570 player->anim_delay_counter = 0;
6571 player->post_delay_counter = 0;
6573 player->action_waiting = ACTION_DEFAULT;
6575 player->special_action_bored = ACTION_DEFAULT;
6576 player->special_action_sleeping = ACTION_DEFAULT;
6581 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6584 static byte stored_player_action[MAX_PLAYERS];
6585 static int num_stored_actions = 0;
6587 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6588 int left = player_action & JOY_LEFT;
6589 int right = player_action & JOY_RIGHT;
6590 int up = player_action & JOY_UP;
6591 int down = player_action & JOY_DOWN;
6592 int button1 = player_action & JOY_BUTTON_1;
6593 int button2 = player_action & JOY_BUTTON_2;
6594 int dx = (left ? -1 : right ? 1 : 0);
6595 int dy = (up ? -1 : down ? 1 : 0);
6598 stored_player_action[player->index_nr] = 0;
6599 num_stored_actions++;
6603 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6606 if (!player->active || tape.pausing)
6610 printf("::: [%d %d %d %d] [%d %d]\n",
6611 left, right, up, down, button1, button2);
6617 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6621 snapped = SnapField(player, dx, dy);
6625 dropped = DropElement(player);
6627 moved = MovePlayer(player, dx, dy);
6630 if (tape.single_step && tape.recording && !tape.pausing)
6632 if (button1 || (dropped && !moved))
6634 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6635 SnapField(player, 0, 0); /* stop snapping */
6639 SetPlayerWaiting(player, FALSE);
6642 return player_action;
6644 stored_player_action[player->index_nr] = player_action;
6650 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6653 /* no actions for this player (no input at player's configured device) */
6655 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6656 SnapField(player, 0, 0);
6657 CheckGravityMovement(player);
6659 if (player->MovPos == 0)
6660 SetPlayerWaiting(player, TRUE);
6662 if (player->MovPos == 0) /* needed for tape.playing */
6663 player->is_moving = FALSE;
6665 player->is_dropping = FALSE;
6671 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6673 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6675 TapeRecordAction(stored_player_action);
6676 num_stored_actions = 0;
6683 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6685 static byte stored_player_action[MAX_PLAYERS];
6686 static int num_stored_actions = 0;
6687 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6688 int left = player_action & JOY_LEFT;
6689 int right = player_action & JOY_RIGHT;
6690 int up = player_action & JOY_UP;
6691 int down = player_action & JOY_DOWN;
6692 int button1 = player_action & JOY_BUTTON_1;
6693 int button2 = player_action & JOY_BUTTON_2;
6694 int dx = (left ? -1 : right ? 1 : 0);
6695 int dy = (up ? -1 : down ? 1 : 0);
6697 stored_player_action[player->index_nr] = 0;
6698 num_stored_actions++;
6700 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6702 if (!player->active || tape.pausing)
6707 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6710 snapped = SnapField(player, dx, dy);
6714 dropped = DropElement(player);
6716 moved = MovePlayer(player, dx, dy);
6719 if (tape.single_step && tape.recording && !tape.pausing)
6721 if (button1 || (dropped && !moved))
6723 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6724 SnapField(player, 0, 0); /* stop snapping */
6728 stored_player_action[player->index_nr] = player_action;
6732 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6734 /* no actions for this player (no input at player's configured device) */
6736 DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6737 SnapField(player, 0, 0);
6738 CheckGravityMovement(player);
6740 if (player->MovPos == 0)
6741 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6743 if (player->MovPos == 0) /* needed for tape.playing */
6744 player->is_moving = FALSE;
6747 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6749 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6751 TapeRecordAction(stored_player_action);
6752 num_stored_actions = 0;
6759 static unsigned long action_delay = 0;
6760 unsigned long action_delay_value;
6761 int magic_wall_x = 0, magic_wall_y = 0;
6762 int i, x, y, element, graphic;
6763 byte *recorded_player_action;
6764 byte summarized_player_action = 0;
6766 byte tape_action[MAX_PLAYERS];
6769 if (game_status != GAME_MODE_PLAYING)
6772 action_delay_value =
6773 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6775 if (tape.playing && tape.index_search && !tape.pausing)
6776 action_delay_value = 0;
6778 /* ---------- main game synchronization point ---------- */
6780 WaitUntilDelayReached(&action_delay, action_delay_value);
6782 if (network_playing && !network_player_action_received)
6786 printf("DEBUG: try to get network player actions in time\n");
6790 #if defined(PLATFORM_UNIX)
6791 /* last chance to get network player actions without main loop delay */
6795 if (game_status != GAME_MODE_PLAYING)
6798 if (!network_player_action_received)
6802 printf("DEBUG: failed to get network player actions in time\n");
6813 printf("::: getting new tape action [%d]\n", FrameCounter);
6816 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6818 for (i = 0; i < MAX_PLAYERS; i++)
6820 summarized_player_action |= stored_player[i].action;
6822 if (!network_playing)
6823 stored_player[i].effective_action = stored_player[i].action;
6826 #if defined(PLATFORM_UNIX)
6827 if (network_playing)
6828 SendToServer_MovePlayer(summarized_player_action);
6831 if (!options.network && !setup.team_mode)
6832 local_player->effective_action = summarized_player_action;
6834 for (i = 0; i < MAX_PLAYERS; i++)
6836 int actual_player_action = stored_player[i].effective_action;
6838 if (stored_player[i].programmed_action)
6839 actual_player_action = stored_player[i].programmed_action;
6841 if (recorded_player_action)
6842 actual_player_action = recorded_player_action[i];
6844 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6846 if (tape.recording && tape_action[i] && !tape.player_participates[i])
6847 tape.player_participates[i] = TRUE; /* player just appeared from CE */
6849 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6854 TapeRecordAction(tape_action);
6857 network_player_action_received = FALSE;
6859 ScrollScreen(NULL, SCROLL_GO_ON);
6865 for (i = 0; i < MAX_PLAYERS; i++)
6866 stored_player[i].Frame++;
6870 if (game.engine_version < VERSION_IDENT(2,2,0,7))
6872 for (i = 0; i < MAX_PLAYERS; i++)
6874 struct PlayerInfo *player = &stored_player[i];
6878 if (player->active && player->is_pushing && player->is_moving &&
6881 ContinueMoving(x, y);
6883 /* continue moving after pushing (this is actually a bug) */
6884 if (!IS_MOVING(x, y))
6893 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6895 Changed[x][y] = CE_BITMASK_DEFAULT;
6896 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6899 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6901 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6902 printf("GameActions(): This should never happen!\n");
6904 ChangePage[x][y] = -1;
6909 if (WasJustMoving[x][y] > 0)
6910 WasJustMoving[x][y]--;
6911 if (WasJustFalling[x][y] > 0)
6912 WasJustFalling[x][y]--;
6917 /* reset finished pushing action (not done in ContinueMoving() to allow
6918 continous pushing animation for elements with zero push delay) */
6919 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6921 ResetGfxAnimation(x, y);
6922 DrawLevelField(x, y);
6927 if (IS_BLOCKED(x, y))
6931 Blocked2Moving(x, y, &oldx, &oldy);
6932 if (!IS_MOVING(oldx, oldy))
6934 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6935 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6936 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6937 printf("GameActions(): This should never happen!\n");
6943 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6945 element = Feld[x][y];
6947 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6949 graphic = el2img(element);
6955 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6957 element = graphic = 0;
6961 if (graphic_info[graphic].anim_global_sync)
6962 GfxFrame[x][y] = FrameCounter;
6964 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6965 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6966 ResetRandomAnimationValue(x, y);
6968 SetRandomAnimationValue(x, y);
6971 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6974 if (IS_INACTIVE(element))
6976 if (IS_ANIMATED(graphic))
6977 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6983 /* this may take place after moving, so 'element' may have changed */
6985 if (IS_CHANGING(x, y))
6987 if (IS_CHANGING(x, y) &&
6988 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6992 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6993 element_info[element].event_page_nr[CE_DELAY]);
6995 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6998 element = Feld[x][y];
6999 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7003 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7008 element = Feld[x][y];
7009 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7011 if (element == EL_MOLE)
7012 printf("::: %d, %d, %d [%d]\n",
7013 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7017 if (element == EL_YAMYAM)
7018 printf("::: %d, %d, %d\n",
7019 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7023 if (IS_ANIMATED(graphic) &&
7027 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7030 if (element == EL_BUG)
7031 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7035 if (element == EL_MOLE)
7036 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7040 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7041 EdelsteinFunkeln(x, y);
7043 else if ((element == EL_ACID ||
7044 element == EL_EXIT_OPEN ||
7045 element == EL_SP_EXIT_OPEN ||
7046 element == EL_SP_TERMINAL ||
7047 element == EL_SP_TERMINAL_ACTIVE ||
7048 element == EL_EXTRA_TIME ||
7049 element == EL_SHIELD_NORMAL ||
7050 element == EL_SHIELD_DEADLY) &&
7051 IS_ANIMATED(graphic))
7052 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7053 else if (IS_MOVING(x, y))
7054 ContinueMoving(x, y);
7055 else if (IS_ACTIVE_BOMB(element))
7056 CheckDynamite(x, y);
7058 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7059 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7061 else if (element == EL_AMOEBA_GROWING)
7062 AmoebeWaechst(x, y);
7063 else if (element == EL_AMOEBA_SHRINKING)
7064 AmoebaDisappearing(x, y);
7066 #if !USE_NEW_AMOEBA_CODE
7067 else if (IS_AMOEBALIVE(element))
7068 AmoebeAbleger(x, y);
7071 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7073 else if (element == EL_EXIT_CLOSED)
7075 else if (element == EL_SP_EXIT_CLOSED)
7077 else if (element == EL_EXPANDABLE_WALL_GROWING)
7079 else if (element == EL_EXPANDABLE_WALL ||
7080 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7081 element == EL_EXPANDABLE_WALL_VERTICAL ||
7082 element == EL_EXPANDABLE_WALL_ANY)
7084 else if (element == EL_FLAMES)
7085 CheckForDragon(x, y);
7087 else if (IS_AUTO_CHANGING(element))
7088 ChangeElement(x, y);
7090 else if (element == EL_EXPLOSION)
7091 ; /* drawing of correct explosion animation is handled separately */
7092 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7093 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7096 /* this may take place after moving, so 'element' may have changed */
7097 if (IS_AUTO_CHANGING(Feld[x][y]))
7098 ChangeElement(x, y);
7101 if (IS_BELT_ACTIVE(element))
7102 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7104 if (game.magic_wall_active)
7106 int jx = local_player->jx, jy = local_player->jy;
7108 /* play the element sound at the position nearest to the player */
7109 if ((element == EL_MAGIC_WALL_FULL ||
7110 element == EL_MAGIC_WALL_ACTIVE ||
7111 element == EL_MAGIC_WALL_EMPTYING ||
7112 element == EL_BD_MAGIC_WALL_FULL ||
7113 element == EL_BD_MAGIC_WALL_ACTIVE ||
7114 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7115 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7123 #if USE_NEW_AMOEBA_CODE
7124 /* new experimental amoeba growth stuff */
7126 if (!(FrameCounter % 8))
7129 static unsigned long random = 1684108901;
7131 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7134 x = (random >> 10) % lev_fieldx;
7135 y = (random >> 20) % lev_fieldy;
7137 x = RND(lev_fieldx);
7138 y = RND(lev_fieldy);
7140 element = Feld[x][y];
7142 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7143 if (!IS_PLAYER(x,y) &&
7144 (element == EL_EMPTY ||
7145 element == EL_SAND ||
7146 element == EL_QUICKSAND_EMPTY ||
7147 element == EL_ACID_SPLASH_LEFT ||
7148 element == EL_ACID_SPLASH_RIGHT))
7150 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7151 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7152 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7153 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7154 Feld[x][y] = EL_AMOEBA_DROP;
7157 random = random * 129 + 1;
7163 if (game.explosions_delayed)
7166 game.explosions_delayed = FALSE;
7168 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7170 element = Feld[x][y];
7172 if (ExplodeField[x][y])
7173 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7174 else if (element == EL_EXPLOSION)
7175 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7177 ExplodeField[x][y] = EX_NO_EXPLOSION;
7180 game.explosions_delayed = TRUE;
7183 if (game.magic_wall_active)
7185 if (!(game.magic_wall_time_left % 4))
7187 int element = Feld[magic_wall_x][magic_wall_y];
7189 if (element == EL_BD_MAGIC_WALL_FULL ||
7190 element == EL_BD_MAGIC_WALL_ACTIVE ||
7191 element == EL_BD_MAGIC_WALL_EMPTYING)
7192 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7194 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7197 if (game.magic_wall_time_left > 0)
7199 game.magic_wall_time_left--;
7200 if (!game.magic_wall_time_left)
7202 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7204 element = Feld[x][y];
7206 if (element == EL_MAGIC_WALL_ACTIVE ||
7207 element == EL_MAGIC_WALL_FULL)
7209 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7210 DrawLevelField(x, y);
7212 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7213 element == EL_BD_MAGIC_WALL_FULL)
7215 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7216 DrawLevelField(x, y);
7220 game.magic_wall_active = FALSE;
7225 if (game.light_time_left > 0)
7227 game.light_time_left--;
7229 if (game.light_time_left == 0)
7230 RedrawAllLightSwitchesAndInvisibleElements();
7233 if (game.timegate_time_left > 0)
7235 game.timegate_time_left--;
7237 if (game.timegate_time_left == 0)
7238 CloseAllOpenTimegates();
7241 for (i = 0; i < MAX_PLAYERS; i++)
7243 struct PlayerInfo *player = &stored_player[i];
7245 if (SHIELD_ON(player))
7247 if (player->shield_deadly_time_left)
7248 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7249 else if (player->shield_normal_time_left)
7250 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7254 if (TimeFrames >= FRAMES_PER_SECOND)
7259 for (i = 0; i < MAX_PLAYERS; i++)
7261 struct PlayerInfo *player = &stored_player[i];
7263 if (SHIELD_ON(player))
7265 player->shield_normal_time_left--;
7267 if (player->shield_deadly_time_left > 0)
7268 player->shield_deadly_time_left--;
7272 if (tape.recording || tape.playing)
7273 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7279 if (TimeLeft <= 10 && setup.time_limit)
7280 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7282 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7284 if (!TimeLeft && setup.time_limit)
7285 for (i = 0; i < MAX_PLAYERS; i++)
7286 KillHero(&stored_player[i]);
7288 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7289 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7293 PlayAllPlayersSound();
7295 if (options.debug) /* calculate frames per second */
7297 static unsigned long fps_counter = 0;
7298 static int fps_frames = 0;
7299 unsigned long fps_delay_ms = Counter() - fps_counter;
7303 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7305 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7308 fps_counter = Counter();
7311 redraw_mask |= REDRAW_FPS;
7315 if (stored_player[0].jx != stored_player[0].last_jx ||
7316 stored_player[0].jy != stored_player[0].last_jy)
7317 printf("::: %d, %d, %d, %d, %d\n",
7318 stored_player[0].MovDir,
7319 stored_player[0].MovPos,
7320 stored_player[0].GfxPos,
7321 stored_player[0].Frame,
7322 stored_player[0].StepFrame);
7329 for (i = 0; i < MAX_PLAYERS; i++)
7332 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7334 stored_player[i].Frame += move_frames;
7336 if (stored_player[i].MovPos != 0)
7337 stored_player[i].StepFrame += move_frames;
7339 if (stored_player[i].drop_delay > 0)
7340 stored_player[i].drop_delay--;
7345 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7347 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7349 local_player->show_envelope = 0;
7354 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7356 int min_x = x, min_y = y, max_x = x, max_y = y;
7359 for (i = 0; i < MAX_PLAYERS; i++)
7361 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7363 if (!stored_player[i].active || &stored_player[i] == player)
7366 min_x = MIN(min_x, jx);
7367 min_y = MIN(min_y, jy);
7368 max_x = MAX(max_x, jx);
7369 max_y = MAX(max_y, jy);
7372 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7375 static boolean AllPlayersInVisibleScreen()
7379 for (i = 0; i < MAX_PLAYERS; i++)
7381 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7383 if (!stored_player[i].active)
7386 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7393 void ScrollLevel(int dx, int dy)
7395 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7398 BlitBitmap(drawto_field, drawto_field,
7399 FX + TILEX * (dx == -1) - softscroll_offset,
7400 FY + TILEY * (dy == -1) - softscroll_offset,
7401 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7402 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7403 FX + TILEX * (dx == 1) - softscroll_offset,
7404 FY + TILEY * (dy == 1) - softscroll_offset);
7408 x = (dx == 1 ? BX1 : BX2);
7409 for (y = BY1; y <= BY2; y++)
7410 DrawScreenField(x, y);
7415 y = (dy == 1 ? BY1 : BY2);
7416 for (x = BX1; x <= BX2; x++)
7417 DrawScreenField(x, y);
7420 redraw_mask |= REDRAW_FIELD;
7423 static void CheckGravityMovement(struct PlayerInfo *player)
7425 if (game.gravity && !player->programmed_action)
7427 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7428 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7430 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7431 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7432 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7433 int jx = player->jx, jy = player->jy;
7434 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7435 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7436 int new_jx = jx + dx, new_jy = jy + dy;
7437 boolean field_under_player_is_free =
7438 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7439 boolean player_is_moving_to_valid_field =
7440 (IN_LEV_FIELD(new_jx, new_jy) &&
7441 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7442 Feld[new_jx][new_jy] == EL_SAND));
7443 /* !!! extend EL_SAND to anything diggable !!! */
7445 if (field_under_player_is_free &&
7446 !player_is_moving_to_valid_field &&
7447 !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7448 player->programmed_action = MV_DOWN;
7454 -----------------------------------------------------------------------------
7455 dx, dy: direction (non-diagonal) to try to move the player to
7456 real_dx, real_dy: direction as read from input device (can be diagonal)
7459 boolean MovePlayerOneStep(struct PlayerInfo *player,
7460 int dx, int dy, int real_dx, int real_dy)
7463 static int change_sides[4][2] =
7465 /* enter side leave side */
7466 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7467 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7468 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7469 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7471 int move_direction = (dx == -1 ? MV_LEFT :
7472 dx == +1 ? MV_RIGHT :
7474 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7475 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7476 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7478 int jx = player->jx, jy = player->jy;
7479 int new_jx = jx + dx, new_jy = jy + dy;
7483 if (!player->active || (!dx && !dy))
7484 return MF_NO_ACTION;
7486 player->MovDir = (dx < 0 ? MV_LEFT :
7489 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7491 if (!IN_LEV_FIELD(new_jx, new_jy))
7492 return MF_NO_ACTION;
7494 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7495 return MF_NO_ACTION;
7498 element = MovingOrBlocked2Element(new_jx, new_jy);
7500 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7503 if (DONT_RUN_INTO(element))
7505 if (element == EL_ACID && dx == 0 && dy == 1)
7508 Feld[jx][jy] = EL_PLAYER_1;
7509 InitMovingField(jx, jy, MV_DOWN);
7510 Store[jx][jy] = EL_ACID;
7511 ContinueMoving(jx, jy);
7515 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7520 can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7521 if (can_move != MF_MOVING)
7524 /* check if DigField() has caused relocation of the player */
7525 if (player->jx != jx || player->jy != jy)
7526 return MF_NO_ACTION;
7528 StorePlayer[jx][jy] = 0;
7529 player->last_jx = jx;
7530 player->last_jy = jy;
7531 player->jx = new_jx;
7532 player->jy = new_jy;
7533 StorePlayer[new_jx][new_jy] = player->element_nr;
7536 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7538 player->step_counter++;
7540 player->drop_delay = 0;
7542 PlayerVisit[jx][jy] = FrameCounter;
7544 ScrollPlayer(player, SCROLL_INIT);
7547 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7549 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7550 CE_OTHER_GETS_LEFT);
7551 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7552 CE_LEFT_BY_PLAYER, -1);
7555 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7557 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7558 enter_side, CE_OTHER_GETS_ENTERED);
7559 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7560 CE_ENTERED_BY_PLAYER, -1);
7567 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7569 int jx = player->jx, jy = player->jy;
7570 int old_jx = jx, old_jy = jy;
7571 int moved = MF_NO_ACTION;
7574 if (!player->active)
7579 if (player->MovPos == 0)
7581 player->is_moving = FALSE;
7582 player->is_digging = FALSE;
7583 player->is_collecting = FALSE;
7584 player->is_snapping = FALSE;
7585 player->is_pushing = FALSE;
7591 if (!player->active || (!dx && !dy))
7596 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7600 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7601 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7605 /* remove the last programmed player action */
7606 player->programmed_action = 0;
7610 /* should only happen if pre-1.2 tape recordings are played */
7611 /* this is only for backward compatibility */
7613 int original_move_delay_value = player->move_delay_value;
7616 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7620 /* scroll remaining steps with finest movement resolution */
7621 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7623 while (player->MovPos)
7625 ScrollPlayer(player, SCROLL_GO_ON);
7626 ScrollScreen(NULL, SCROLL_GO_ON);
7632 player->move_delay_value = original_move_delay_value;
7635 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7637 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7638 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7642 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7643 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7649 if (moved & MF_MOVING && !ScreenMovPos &&
7650 (player == local_player || !options.network))
7652 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7653 int offset = (setup.scroll_delay ? 3 : 0);
7655 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7657 /* actual player has left the screen -- scroll in that direction */
7658 if (jx != old_jx) /* player has moved horizontally */
7659 scroll_x += (jx - old_jx);
7660 else /* player has moved vertically */
7661 scroll_y += (jy - old_jy);
7665 if (jx != old_jx) /* player has moved horizontally */
7667 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7668 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7669 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7671 /* don't scroll over playfield boundaries */
7672 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7673 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7675 /* don't scroll more than one field at a time */
7676 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7678 /* don't scroll against the player's moving direction */
7679 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7680 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7681 scroll_x = old_scroll_x;
7683 else /* player has moved vertically */
7685 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7686 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7687 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7689 /* don't scroll over playfield boundaries */
7690 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7691 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7693 /* don't scroll more than one field at a time */
7694 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7696 /* don't scroll against the player's moving direction */
7697 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7698 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7699 scroll_y = old_scroll_y;
7703 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7705 if (!options.network && !AllPlayersInVisibleScreen())
7707 scroll_x = old_scroll_x;
7708 scroll_y = old_scroll_y;
7712 ScrollScreen(player, SCROLL_INIT);
7713 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7720 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7722 if (!(moved & MF_MOVING) && !player->is_pushing)
7727 player->StepFrame = 0;
7729 if (moved & MF_MOVING)
7731 if (old_jx != jx && old_jy == jy)
7732 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7733 else if (old_jx == jx && old_jy != jy)
7734 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7736 DrawLevelField(jx, jy); /* for "crumbled sand" */
7738 player->last_move_dir = player->MovDir;
7739 player->is_moving = TRUE;
7741 player->is_snapping = FALSE;
7745 player->is_switching = FALSE;
7748 player->is_dropping = FALSE;
7753 static int change_sides[4][2] =
7755 /* enter side leave side */
7756 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7757 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7758 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7759 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7761 int move_direction = player->MovDir;
7762 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7763 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7766 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7768 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7769 leave_side, CE_OTHER_GETS_LEFT);
7770 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7771 leave_side, CE_LEFT_BY_PLAYER, -1);
7774 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7776 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7777 enter_side, CE_OTHER_GETS_ENTERED);
7778 CheckElementSideChange(jx, jy, Feld[jx][jy],
7779 enter_side, CE_ENTERED_BY_PLAYER, -1);
7790 CheckGravityMovement(player);
7793 player->last_move_dir = MV_NO_MOVING;
7795 player->is_moving = FALSE;
7798 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7800 TestIfHeroTouchesBadThing(jx, jy);
7801 TestIfPlayerTouchesCustomElement(jx, jy);
7804 if (!player->active)
7810 void ScrollPlayer(struct PlayerInfo *player, int mode)
7812 int jx = player->jx, jy = player->jy;
7813 int last_jx = player->last_jx, last_jy = player->last_jy;
7814 int move_stepsize = TILEX / player->move_delay_value;
7816 if (!player->active || !player->MovPos)
7819 if (mode == SCROLL_INIT)
7821 player->actual_frame_counter = FrameCounter;
7822 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7824 if (Feld[last_jx][last_jy] == EL_EMPTY)
7825 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7832 else if (!FrameReached(&player->actual_frame_counter, 1))
7835 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7836 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7838 if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7839 Feld[last_jx][last_jy] = EL_EMPTY;
7841 /* before DrawPlayer() to draw correct player graphic for this case */
7842 if (player->MovPos == 0)
7843 CheckGravityMovement(player);
7846 DrawPlayer(player); /* needed here only to cleanup last field */
7849 if (player->MovPos == 0) /* player reached destination field */
7852 if (player->move_delay_reset_counter > 0)
7854 player->move_delay_reset_counter--;
7856 if (player->move_delay_reset_counter == 0)
7858 /* continue with normal speed after quickly moving through gate */
7859 HALVE_PLAYER_SPEED(player);
7861 /* be able to make the next move without delay */
7862 player->move_delay = 0;
7866 if (IS_PASSABLE(Feld[last_jx][last_jy]))
7868 /* continue with normal speed after quickly moving through gate */
7869 HALVE_PLAYER_SPEED(player);
7871 /* be able to make the next move without delay */
7872 player->move_delay = 0;
7876 player->last_jx = jx;
7877 player->last_jy = jy;
7879 if (Feld[jx][jy] == EL_EXIT_OPEN ||
7880 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7881 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
7883 DrawPlayer(player); /* needed here only to cleanup last field */
7886 if (local_player->friends_still_needed == 0 ||
7887 IS_SP_ELEMENT(Feld[jx][jy]))
7888 player->LevelSolved = player->GameOver = TRUE;
7891 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7893 TestIfHeroTouchesBadThing(jx, jy);
7894 TestIfPlayerTouchesCustomElement(jx, jy);
7896 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
7899 if (!player->active)
7903 if (tape.single_step && tape.recording && !tape.pausing &&
7904 !player->programmed_action)
7905 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7909 void ScrollScreen(struct PlayerInfo *player, int mode)
7911 static unsigned long screen_frame_counter = 0;
7913 if (mode == SCROLL_INIT)
7915 /* set scrolling step size according to actual player's moving speed */
7916 ScrollStepSize = TILEX / player->move_delay_value;
7918 screen_frame_counter = FrameCounter;
7919 ScreenMovDir = player->MovDir;
7920 ScreenMovPos = player->MovPos;
7921 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7924 else if (!FrameReached(&screen_frame_counter, 1))
7929 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7930 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7931 redraw_mask |= REDRAW_FIELD;
7934 ScreenMovDir = MV_NO_MOVING;
7937 void TestIfPlayerTouchesCustomElement(int x, int y)
7939 static int xy[4][2] =
7946 static int change_sides[4][2] =
7948 /* center side border side */
7949 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
7950 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
7951 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
7952 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
7954 static int touch_dir[4] =
7961 int center_element = Feld[x][y]; /* should always be non-moving! */
7964 for (i = 0; i < 4; i++)
7966 int xx = x + xy[i][0];
7967 int yy = y + xy[i][1];
7968 int center_side = change_sides[i][0];
7969 int border_side = change_sides[i][1];
7972 if (!IN_LEV_FIELD(xx, yy))
7975 if (IS_PLAYER(x, y))
7977 if (game.engine_version < VERSION_IDENT(3,0,7,0))
7978 border_element = Feld[xx][yy]; /* may be moving! */
7979 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7980 border_element = Feld[xx][yy];
7981 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
7982 border_element = MovingOrBlocked2Element(xx, yy);
7984 continue; /* center and border element do not touch */
7986 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7987 CE_OTHER_GETS_TOUCHED);
7988 CheckElementSideChange(xx, yy, border_element, border_side,
7989 CE_TOUCHED_BY_PLAYER, -1);
7991 else if (IS_PLAYER(xx, yy))
7993 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7995 struct PlayerInfo *player = PLAYERINFO(xx, yy);
7997 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7998 continue; /* center and border element do not touch */
8001 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8002 CE_OTHER_GETS_TOUCHED);
8003 CheckElementSideChange(x, y, center_element, center_side,
8004 CE_TOUCHED_BY_PLAYER, -1);
8011 void TestIfElementTouchesCustomElement(int x, int y)
8013 static int xy[4][2] =
8020 static int change_sides[4][2] =
8022 /* center side border side */
8023 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8024 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8025 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8026 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8028 static int touch_dir[4] =
8035 boolean change_center_element = FALSE;
8036 int center_element_change_page = 0;
8037 int center_element = Feld[x][y]; /* should always be non-moving! */
8040 for (i = 0; i < 4; i++)
8042 int xx = x + xy[i][0];
8043 int yy = y + xy[i][1];
8044 int center_side = change_sides[i][0];
8045 int border_side = change_sides[i][1];
8048 if (!IN_LEV_FIELD(xx, yy))
8051 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8052 border_element = Feld[xx][yy]; /* may be moving! */
8053 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8054 border_element = Feld[xx][yy];
8055 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8056 border_element = MovingOrBlocked2Element(xx, yy);
8058 continue; /* center and border element do not touch */
8060 /* check for change of center element (but change it only once) */
8061 if (IS_CUSTOM_ELEMENT(center_element) &&
8062 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8063 !change_center_element)
8065 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8067 struct ElementChangeInfo *change =
8068 &element_info[center_element].change_page[j];
8070 if (change->can_change &&
8071 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8072 change->sides & border_side &&
8073 change->trigger_element == border_element)
8075 change_center_element = TRUE;
8076 center_element_change_page = j;
8083 /* check for change of border element */
8084 if (IS_CUSTOM_ELEMENT(border_element) &&
8085 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8087 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8089 struct ElementChangeInfo *change =
8090 &element_info[border_element].change_page[j];
8092 if (change->can_change &&
8093 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8094 change->sides & center_side &&
8095 change->trigger_element == center_element)
8097 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8098 CE_OTHER_IS_TOUCHING, j);
8105 if (change_center_element)
8106 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8107 CE_OTHER_IS_TOUCHING, center_element_change_page);
8110 void TestIfElementHitsCustomElement(int x, int y, int direction)
8112 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8113 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8114 int hitx = x + dx, hity = y + dy;
8115 int hitting_element = Feld[x][y];
8117 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8118 !IS_FREE(hitx, hity) &&
8119 (!IS_MOVING(hitx, hity) ||
8120 MovDir[hitx][hity] != direction ||
8121 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8124 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8128 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8132 CheckElementSideChange(x, y, hitting_element,
8133 direction, CE_HITTING_SOMETHING, -1);
8135 if (IN_LEV_FIELD(hitx, hity))
8137 static int opposite_directions[] =
8144 int move_dir_bit = MV_DIR_BIT(direction);
8145 int opposite_direction = opposite_directions[move_dir_bit];
8146 int hitting_side = direction;
8147 int touched_side = opposite_direction;
8148 int touched_element = MovingOrBlocked2Element(hitx, hity);
8150 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8151 MovDir[hitx][hity] != direction ||
8152 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8161 CheckElementSideChange(hitx, hity, touched_element,
8162 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8164 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8165 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8167 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8169 struct ElementChangeInfo *change =
8170 &element_info[hitting_element].change_page[i];
8172 if (change->can_change &&
8173 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8174 change->sides & touched_side &&
8175 change->trigger_element == touched_element)
8177 CheckElementSideChange(x, y, hitting_element,
8178 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8184 if (IS_CUSTOM_ELEMENT(touched_element) &&
8185 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8187 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8189 struct ElementChangeInfo *change =
8190 &element_info[touched_element].change_page[i];
8192 if (change->can_change &&
8193 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8194 change->sides & hitting_side &&
8195 change->trigger_element == hitting_element)
8197 CheckElementSideChange(hitx, hity, touched_element,
8198 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8207 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8209 int i, kill_x = -1, kill_y = -1;
8210 static int test_xy[4][2] =
8217 static int test_dir[4] =
8225 for (i = 0; i < 4; i++)
8227 int test_x, test_y, test_move_dir, test_element;
8229 test_x = good_x + test_xy[i][0];
8230 test_y = good_y + test_xy[i][1];
8231 if (!IN_LEV_FIELD(test_x, test_y))
8235 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8238 test_element = Feld[test_x][test_y];
8240 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8243 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8244 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8246 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8247 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8255 if (kill_x != -1 || kill_y != -1)
8257 if (IS_PLAYER(good_x, good_y))
8259 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8261 if (player->shield_deadly_time_left > 0)
8262 Bang(kill_x, kill_y);
8263 else if (!PLAYER_PROTECTED(good_x, good_y))
8267 Bang(good_x, good_y);
8271 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8273 int i, kill_x = -1, kill_y = -1;
8274 int bad_element = Feld[bad_x][bad_y];
8275 static int test_xy[4][2] =
8282 static int touch_dir[4] =
8289 static int test_dir[4] =
8297 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8300 for (i = 0; i < 4; i++)
8302 int test_x, test_y, test_move_dir, test_element;
8304 test_x = bad_x + test_xy[i][0];
8305 test_y = bad_y + test_xy[i][1];
8306 if (!IN_LEV_FIELD(test_x, test_y))
8310 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8312 test_element = Feld[test_x][test_y];
8314 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8315 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8317 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8318 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8320 /* good thing is player or penguin that does not move away */
8321 if (IS_PLAYER(test_x, test_y))
8323 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8325 if (bad_element == EL_ROBOT && player->is_moving)
8326 continue; /* robot does not kill player if he is moving */
8328 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8330 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8331 continue; /* center and border element do not touch */
8338 else if (test_element == EL_PENGUIN)
8347 if (kill_x != -1 || kill_y != -1)
8349 if (IS_PLAYER(kill_x, kill_y))
8351 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8353 if (player->shield_deadly_time_left > 0)
8355 else if (!PLAYER_PROTECTED(kill_x, kill_y))
8359 Bang(kill_x, kill_y);
8363 void TestIfHeroTouchesBadThing(int x, int y)
8365 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8368 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8370 TestIfGoodThingHitsBadThing(x, y, move_dir);
8373 void TestIfBadThingTouchesHero(int x, int y)
8375 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8378 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8380 TestIfBadThingHitsGoodThing(x, y, move_dir);
8383 void TestIfFriendTouchesBadThing(int x, int y)
8385 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8388 void TestIfBadThingTouchesFriend(int x, int y)
8390 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8393 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8395 int i, kill_x = bad_x, kill_y = bad_y;
8396 static int xy[4][2] =
8404 for (i = 0; i < 4; i++)
8408 x = bad_x + xy[i][0];
8409 y = bad_y + xy[i][1];
8410 if (!IN_LEV_FIELD(x, y))
8413 element = Feld[x][y];
8414 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8415 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8423 if (kill_x != bad_x || kill_y != bad_y)
8427 void KillHero(struct PlayerInfo *player)
8429 int jx = player->jx, jy = player->jy;
8431 if (!player->active)
8434 /* remove accessible field at the player's position */
8435 Feld[jx][jy] = EL_EMPTY;
8437 /* deactivate shield (else Bang()/Explode() would not work right) */
8438 player->shield_normal_time_left = 0;
8439 player->shield_deadly_time_left = 0;
8445 static void KillHeroUnlessProtected(int x, int y)
8447 if (!PLAYER_PROTECTED(x, y))
8448 KillHero(PLAYERINFO(x, y));
8451 void BuryHero(struct PlayerInfo *player)
8453 int jx = player->jx, jy = player->jy;
8455 if (!player->active)
8459 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8461 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8463 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8465 player->GameOver = TRUE;
8469 void RemoveHero(struct PlayerInfo *player)
8471 int jx = player->jx, jy = player->jy;
8472 int i, found = FALSE;
8474 player->present = FALSE;
8475 player->active = FALSE;
8477 if (!ExplodeField[jx][jy])
8478 StorePlayer[jx][jy] = 0;
8480 for (i = 0; i < MAX_PLAYERS; i++)
8481 if (stored_player[i].active)
8485 AllPlayersGone = TRUE;
8492 =============================================================================
8493 checkDiagonalPushing()
8494 -----------------------------------------------------------------------------
8495 check if diagonal input device direction results in pushing of object
8496 (by checking if the alternative direction is walkable, diggable, ...)
8497 =============================================================================
8500 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8501 int x, int y, int real_dx, int real_dy)
8503 int jx, jy, dx, dy, xx, yy;
8505 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8508 /* diagonal direction: check alternative direction */
8513 xx = jx + (dx == 0 ? real_dx : 0);
8514 yy = jy + (dy == 0 ? real_dy : 0);
8516 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8520 =============================================================================
8522 -----------------------------------------------------------------------------
8523 x, y: field next to player (non-diagonal) to try to dig to
8524 real_dx, real_dy: direction as read from input device (can be diagonal)
8525 =============================================================================
8528 int DigField(struct PlayerInfo *player,
8529 int x, int y, int real_dx, int real_dy, int mode)
8531 static int change_sides[4] =
8533 CH_SIDE_RIGHT, /* moving left */
8534 CH_SIDE_LEFT, /* moving right */
8535 CH_SIDE_BOTTOM, /* moving up */
8536 CH_SIDE_TOP, /* moving down */
8538 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8539 int jx = player->jx, jy = player->jy;
8540 int dx = x - jx, dy = y - jy;
8541 int nextx = x + dx, nexty = y + dy;
8542 int move_direction = (dx == -1 ? MV_LEFT :
8543 dx == +1 ? MV_RIGHT :
8545 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8546 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8549 if (player->MovPos == 0)
8551 player->is_digging = FALSE;
8552 player->is_collecting = FALSE;
8555 if (player->MovPos == 0) /* last pushing move finished */
8556 player->is_pushing = FALSE;
8558 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8560 player->is_switching = FALSE;
8561 player->push_delay = 0;
8563 return MF_NO_ACTION;
8566 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8567 return MF_NO_ACTION;
8570 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8572 if (IS_TUBE(Feld[jx][jy]) ||
8573 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8577 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8578 int tube_leave_directions[][2] =
8580 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8581 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8582 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8583 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
8584 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
8585 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
8586 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
8587 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
8588 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
8589 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
8590 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
8591 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8594 while (tube_leave_directions[i][0] != tube_element)
8597 if (tube_leave_directions[i][0] == -1) /* should not happen */
8601 if (!(tube_leave_directions[i][1] & move_direction))
8602 return MF_NO_ACTION; /* tube has no opening in this direction */
8605 element = Feld[x][y];
8607 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8608 game.engine_version >= VERSION_IDENT(2,2,0,0))
8609 return MF_NO_ACTION;
8613 case EL_SP_PORT_LEFT:
8614 case EL_SP_PORT_RIGHT:
8616 case EL_SP_PORT_DOWN:
8617 case EL_SP_PORT_HORIZONTAL:
8618 case EL_SP_PORT_VERTICAL:
8619 case EL_SP_PORT_ANY:
8620 case EL_SP_GRAVITY_PORT_LEFT:
8621 case EL_SP_GRAVITY_PORT_RIGHT:
8622 case EL_SP_GRAVITY_PORT_UP:
8623 case EL_SP_GRAVITY_PORT_DOWN:
8625 element != EL_SP_PORT_LEFT &&
8626 element != EL_SP_GRAVITY_PORT_LEFT &&
8627 element != EL_SP_PORT_HORIZONTAL &&
8628 element != EL_SP_PORT_ANY) ||
8630 element != EL_SP_PORT_RIGHT &&
8631 element != EL_SP_GRAVITY_PORT_RIGHT &&
8632 element != EL_SP_PORT_HORIZONTAL &&
8633 element != EL_SP_PORT_ANY) ||
8635 element != EL_SP_PORT_UP &&
8636 element != EL_SP_GRAVITY_PORT_UP &&
8637 element != EL_SP_PORT_VERTICAL &&
8638 element != EL_SP_PORT_ANY) ||
8640 element != EL_SP_PORT_DOWN &&
8641 element != EL_SP_GRAVITY_PORT_DOWN &&
8642 element != EL_SP_PORT_VERTICAL &&
8643 element != EL_SP_PORT_ANY) ||
8644 !IN_LEV_FIELD(nextx, nexty) ||
8645 !IS_FREE(nextx, nexty))
8646 return MF_NO_ACTION;
8648 if (element == EL_SP_GRAVITY_PORT_LEFT ||
8649 element == EL_SP_GRAVITY_PORT_RIGHT ||
8650 element == EL_SP_GRAVITY_PORT_UP ||
8651 element == EL_SP_GRAVITY_PORT_DOWN)
8652 game.gravity = !game.gravity;
8654 /* automatically move to the next field with double speed */
8655 player->programmed_action = move_direction;
8657 if (player->move_delay_reset_counter == 0)
8659 player->move_delay_reset_counter = 2; /* two double speed steps */
8661 DOUBLE_PLAYER_SPEED(player);
8664 player->move_delay_reset_counter = 2;
8666 DOUBLE_PLAYER_SPEED(player);
8669 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8673 case EL_TUBE_VERTICAL:
8674 case EL_TUBE_HORIZONTAL:
8675 case EL_TUBE_VERTICAL_LEFT:
8676 case EL_TUBE_VERTICAL_RIGHT:
8677 case EL_TUBE_HORIZONTAL_UP:
8678 case EL_TUBE_HORIZONTAL_DOWN:
8679 case EL_TUBE_LEFT_UP:
8680 case EL_TUBE_LEFT_DOWN:
8681 case EL_TUBE_RIGHT_UP:
8682 case EL_TUBE_RIGHT_DOWN:
8685 int tube_enter_directions[][2] =
8687 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8688 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
8689 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
8690 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
8691 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
8692 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
8693 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
8694 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
8695 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
8696 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
8697 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
8698 { -1, MV_NO_MOVING }
8701 while (tube_enter_directions[i][0] != element)
8704 if (tube_enter_directions[i][0] == -1) /* should not happen */
8708 if (!(tube_enter_directions[i][1] & move_direction))
8709 return MF_NO_ACTION; /* tube has no opening in this direction */
8711 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8717 if (IS_WALKABLE(element))
8719 int sound_action = ACTION_WALKING;
8721 if (element >= EL_GATE_1 && element <= EL_GATE_4)
8723 if (!player->key[element - EL_GATE_1])
8724 return MF_NO_ACTION;
8726 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8728 if (!player->key[element - EL_GATE_1_GRAY])
8729 return MF_NO_ACTION;
8731 else if (element == EL_EXIT_OPEN ||
8732 element == EL_SP_EXIT_OPEN ||
8733 element == EL_SP_EXIT_OPENING)
8735 sound_action = ACTION_PASSING; /* player is passing exit */
8737 else if (element == EL_EMPTY)
8739 sound_action = ACTION_MOVING; /* nothing to walk on */
8742 /* play sound from background or player, whatever is available */
8743 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8744 PlayLevelSoundElementAction(x, y, element, sound_action);
8746 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8750 else if (IS_PASSABLE(element))
8752 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8753 return MF_NO_ACTION;
8756 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
8757 return MF_NO_ACTION;
8760 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8762 if (!player->key[element - EL_EM_GATE_1])
8763 return MF_NO_ACTION;
8765 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8767 if (!player->key[element - EL_EM_GATE_1_GRAY])
8768 return MF_NO_ACTION;
8771 /* automatically move to the next field with double speed */
8772 player->programmed_action = move_direction;
8774 if (player->move_delay_reset_counter == 0)
8776 player->move_delay_reset_counter = 2; /* two double speed steps */
8778 DOUBLE_PLAYER_SPEED(player);
8781 player->move_delay_reset_counter = 2;
8783 DOUBLE_PLAYER_SPEED(player);
8786 PlayLevelSoundAction(x, y, ACTION_PASSING);
8790 else if (IS_DIGGABLE(element))
8794 if (mode != DF_SNAP)
8797 GfxElement[x][y] = GFX_ELEMENT(element);
8800 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8802 player->is_digging = TRUE;
8805 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8807 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8810 if (mode == DF_SNAP)
8811 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8816 else if (IS_COLLECTIBLE(element))
8820 if (mode != DF_SNAP)
8822 GfxElement[x][y] = element;
8823 player->is_collecting = TRUE;
8826 if (element == EL_SPEED_PILL)
8827 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8828 else if (element == EL_EXTRA_TIME && level.time > 0)
8831 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8833 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8835 player->shield_normal_time_left += 10;
8836 if (element == EL_SHIELD_DEADLY)
8837 player->shield_deadly_time_left += 10;
8839 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8841 if (player->inventory_size < MAX_INVENTORY_SIZE)
8842 player->inventory_element[player->inventory_size++] = element;
8844 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8845 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8847 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8849 player->dynabomb_count++;
8850 player->dynabombs_left++;
8852 else if (element == EL_DYNABOMB_INCREASE_SIZE)
8854 player->dynabomb_size++;
8856 else if (element == EL_DYNABOMB_INCREASE_POWER)
8858 player->dynabomb_xl = TRUE;
8860 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8861 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8863 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8864 element - EL_KEY_1 : element - EL_EM_KEY_1);
8866 player->key[key_nr] = TRUE;
8868 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8869 el2edimg(EL_KEY_1 + key_nr));
8870 redraw_mask |= REDRAW_DOOR_1;
8872 else if (IS_ENVELOPE(element))
8875 player->show_envelope = element;
8877 ShowEnvelope(element - EL_ENVELOPE_1);
8880 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8884 for (i = 0; i < element_info[element].collect_count; i++)
8885 if (player->inventory_size < MAX_INVENTORY_SIZE)
8886 player->inventory_element[player->inventory_size++] = element;
8888 DrawText(DX_DYNAMITE, DY_DYNAMITE,
8889 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8891 else if (element_info[element].collect_count > 0)
8893 local_player->gems_still_needed -=
8894 element_info[element].collect_count;
8895 if (local_player->gems_still_needed < 0)
8896 local_player->gems_still_needed = 0;
8898 DrawText(DX_EMERALDS, DY_EMERALDS,
8899 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8902 RaiseScoreElement(element);
8903 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8905 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8908 if (mode == DF_SNAP)
8909 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8914 else if (IS_PUSHABLE(element))
8916 if (mode == DF_SNAP && element != EL_BD_ROCK)
8917 return MF_NO_ACTION;
8919 if (CAN_FALL(element) && dy)
8920 return MF_NO_ACTION;
8922 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8923 !(element == EL_SPRING && use_spring_bug))
8924 return MF_NO_ACTION;
8927 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8928 ((move_direction & MV_VERTICAL &&
8929 ((element_info[element].move_pattern & MV_LEFT &&
8930 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8931 (element_info[element].move_pattern & MV_RIGHT &&
8932 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8933 (move_direction & MV_HORIZONTAL &&
8934 ((element_info[element].move_pattern & MV_UP &&
8935 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8936 (element_info[element].move_pattern & MV_DOWN &&
8937 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8938 return MF_NO_ACTION;
8942 /* do not push elements already moving away faster than player */
8943 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8944 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8945 return MF_NO_ACTION;
8947 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8948 return MF_NO_ACTION;
8952 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8954 if (player->push_delay_value == -1)
8955 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8957 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8959 if (!player->is_pushing)
8960 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8964 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8965 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8966 !player_is_pushing))
8967 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8970 if (!player->is_pushing &&
8971 game.engine_version >= VERSION_IDENT(2,2,0,7))
8972 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8976 printf("::: push delay: %ld [%d, %d] [%d]\n",
8977 player->push_delay_value, FrameCounter, game.engine_version,
8978 player->is_pushing);
8981 player->is_pushing = TRUE;
8983 if (!(IN_LEV_FIELD(nextx, nexty) &&
8984 (IS_FREE(nextx, nexty) ||
8985 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8986 IS_SB_ELEMENT(element)))))
8987 return MF_NO_ACTION;
8989 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8990 return MF_NO_ACTION;
8992 if (player->push_delay == 0) /* new pushing; restart delay */
8993 player->push_delay = FrameCounter;
8995 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8996 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8997 element != EL_SPRING && element != EL_BALLOON)
8999 /* make sure that there is no move delay before next try to push */
9000 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9001 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9003 return MF_NO_ACTION;
9007 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9010 if (IS_SB_ELEMENT(element))
9012 if (element == EL_SOKOBAN_FIELD_FULL)
9014 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9015 local_player->sokobanfields_still_needed++;
9018 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9020 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9021 local_player->sokobanfields_still_needed--;
9024 Feld[x][y] = EL_SOKOBAN_OBJECT;
9026 if (Back[x][y] == Back[nextx][nexty])
9027 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9028 else if (Back[x][y] != 0)
9029 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9032 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9035 if (local_player->sokobanfields_still_needed == 0 &&
9036 game.emulation == EMU_SOKOBAN)
9038 player->LevelSolved = player->GameOver = TRUE;
9039 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9043 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9045 InitMovingField(x, y, move_direction);
9046 GfxAction[x][y] = ACTION_PUSHING;
9048 if (mode == DF_SNAP)
9049 ContinueMoving(x, y);
9051 MovPos[x][y] = (dx != 0 ? dx : dy);
9053 Pushed[x][y] = TRUE;
9054 Pushed[nextx][nexty] = TRUE;
9056 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9057 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9059 player->push_delay_value = -1; /* get new value later */
9061 CheckTriggeredElementSideChange(x, y, element, dig_side,
9062 CE_OTHER_GETS_PUSHED);
9063 CheckElementSideChange(x, y, element, dig_side,
9064 CE_PUSHED_BY_PLAYER, -1);
9068 else if (IS_SWITCHABLE(element))
9070 if (PLAYER_SWITCHING(player, x, y))
9073 player->is_switching = TRUE;
9074 player->switch_x = x;
9075 player->switch_y = y;
9077 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9079 if (element == EL_ROBOT_WHEEL)
9081 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9085 DrawLevelField(x, y);
9087 else if (element == EL_SP_TERMINAL)
9091 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9093 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9095 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9096 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9099 else if (IS_BELT_SWITCH(element))
9101 ToggleBeltSwitch(x, y);
9103 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9104 element == EL_SWITCHGATE_SWITCH_DOWN)
9106 ToggleSwitchgateSwitch(x, y);
9108 else if (element == EL_LIGHT_SWITCH ||
9109 element == EL_LIGHT_SWITCH_ACTIVE)
9111 ToggleLightSwitch(x, y);
9114 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9115 SND_LIGHT_SWITCH_ACTIVATING :
9116 SND_LIGHT_SWITCH_DEACTIVATING);
9119 else if (element == EL_TIMEGATE_SWITCH)
9121 ActivateTimegateSwitch(x, y);
9123 else if (element == EL_BALLOON_SWITCH_LEFT ||
9124 element == EL_BALLOON_SWITCH_RIGHT ||
9125 element == EL_BALLOON_SWITCH_UP ||
9126 element == EL_BALLOON_SWITCH_DOWN ||
9127 element == EL_BALLOON_SWITCH_ANY)
9129 if (element == EL_BALLOON_SWITCH_ANY)
9130 game.balloon_dir = move_direction;
9132 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9133 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9134 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9135 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9138 else if (element == EL_LAMP)
9140 Feld[x][y] = EL_LAMP_ACTIVE;
9141 local_player->lights_still_needed--;
9143 DrawLevelField(x, y);
9145 else if (element == EL_TIME_ORB_FULL)
9147 Feld[x][y] = EL_TIME_ORB_EMPTY;
9149 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9151 DrawLevelField(x, y);
9154 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9162 if (!PLAYER_SWITCHING(player, x, y))
9164 player->is_switching = TRUE;
9165 player->switch_x = x;
9166 player->switch_y = y;
9168 CheckTriggeredElementSideChange(x, y, element, dig_side,
9169 CE_OTHER_IS_SWITCHING);
9170 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9173 CheckTriggeredElementSideChange(x, y, element, dig_side,
9174 CE_OTHER_GETS_PRESSED);
9175 CheckElementSideChange(x, y, element, dig_side,
9176 CE_PRESSED_BY_PLAYER, -1);
9179 return MF_NO_ACTION;
9182 player->push_delay = 0;
9184 if (Feld[x][y] != element) /* really digged/collected something */
9185 player->is_collecting = !player->is_digging;
9190 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9192 int jx = player->jx, jy = player->jy;
9193 int x = jx + dx, y = jy + dy;
9194 int snap_direction = (dx == -1 ? MV_LEFT :
9195 dx == +1 ? MV_RIGHT :
9197 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9199 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9202 if (!player->active || !IN_LEV_FIELD(x, y))
9210 if (player->MovPos == 0)
9211 player->is_pushing = FALSE;
9213 player->is_snapping = FALSE;
9215 if (player->MovPos == 0)
9217 player->is_moving = FALSE;
9218 player->is_digging = FALSE;
9219 player->is_collecting = FALSE;
9225 if (player->is_snapping)
9228 player->MovDir = snap_direction;
9231 if (player->MovPos == 0)
9234 player->is_moving = FALSE;
9235 player->is_digging = FALSE;
9236 player->is_collecting = FALSE;
9239 player->is_dropping = FALSE;
9241 if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9244 player->is_snapping = TRUE;
9247 if (player->MovPos == 0)
9250 player->is_moving = FALSE;
9251 player->is_digging = FALSE;
9252 player->is_collecting = FALSE;
9255 DrawLevelField(x, y);
9261 boolean DropElement(struct PlayerInfo *player)
9263 int jx = player->jx, jy = player->jy;
9264 int old_element = Feld[jx][jy];
9267 /* check if player is active, not moving and ready to drop */
9268 if (!player->active || player->MovPos || player->drop_delay > 0)
9271 /* check if player has anything that can be dropped */
9272 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9275 /* check if anything can be dropped at the current position */
9276 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9279 /* collected custom elements can only be dropped on empty fields */
9280 if (player->inventory_size > 0 &&
9281 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9282 && old_element != EL_EMPTY)
9285 if (old_element != EL_EMPTY)
9286 Back[jx][jy] = old_element; /* store old element on this field */
9288 ResetGfxAnimation(jx, jy);
9289 ResetRandomAnimationValue(jx, jy);
9291 if (player->inventory_size > 0)
9293 player->inventory_size--;
9294 new_element = player->inventory_element[player->inventory_size];
9296 if (new_element == EL_DYNAMITE)
9297 new_element = EL_DYNAMITE_ACTIVE;
9298 else if (new_element == EL_SP_DISK_RED)
9299 new_element = EL_SP_DISK_RED_ACTIVE;
9301 Feld[jx][jy] = new_element;
9303 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9304 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9306 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9307 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9309 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9311 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9312 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9314 TestIfElementTouchesCustomElement(jx, jy);
9316 else /* player is dropping a dyna bomb */
9318 player->dynabombs_left--;
9319 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9321 Feld[jx][jy] = new_element;
9323 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9324 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9326 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9333 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9335 InitField(jx, jy, FALSE);
9336 if (CAN_MOVE(Feld[jx][jy]))
9340 new_element = Feld[jx][jy];
9342 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9343 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9345 int move_stepsize = element_info[new_element].move_stepsize;
9346 int direction, dx, dy, nextx, nexty;
9348 if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
9349 MovDir[jx][jy] = player->MovDir;
9351 direction = MovDir[jx][jy];
9352 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9353 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9357 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9360 WasJustMoving[jx][jy] = 3;
9362 InitMovingField(jx, jy, direction);
9363 ContinueMoving(jx, jy);
9368 Changed[jx][jy] = 0; /* allow another change */
9371 TestIfElementHitsCustomElement(jx, jy, direction);
9373 CheckElementSideChange(jx, jy, new_element,
9374 direction, CE_HITTING_SOMETHING, -1);
9378 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9382 player->drop_delay = 8 + 8 + 8;
9387 player->is_dropping = TRUE;
9393 /* ------------------------------------------------------------------------- */
9394 /* game sound playing functions */
9395 /* ------------------------------------------------------------------------- */
9397 static int *loop_sound_frame = NULL;
9398 static int *loop_sound_volume = NULL;
9400 void InitPlayLevelSound()
9402 int num_sounds = getSoundListSize();
9404 checked_free(loop_sound_frame);
9405 checked_free(loop_sound_volume);
9407 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9408 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9411 static void PlayLevelSound(int x, int y, int nr)
9413 int sx = SCREENX(x), sy = SCREENY(y);
9414 int volume, stereo_position;
9415 int max_distance = 8;
9416 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9418 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9419 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9422 if (!IN_LEV_FIELD(x, y) ||
9423 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9424 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9427 volume = SOUND_MAX_VOLUME;
9429 if (!IN_SCR_FIELD(sx, sy))
9431 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9432 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9434 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9437 stereo_position = (SOUND_MAX_LEFT +
9438 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9439 (SCR_FIELDX + 2 * max_distance));
9441 if (IS_LOOP_SOUND(nr))
9443 /* This assures that quieter loop sounds do not overwrite louder ones,
9444 while restarting sound volume comparison with each new game frame. */
9446 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9449 loop_sound_volume[nr] = volume;
9450 loop_sound_frame[nr] = FrameCounter;
9453 PlaySoundExt(nr, volume, stereo_position, type);
9456 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9458 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9459 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9460 y < LEVELY(BY1) ? LEVELY(BY1) :
9461 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9465 static void PlayLevelSoundAction(int x, int y, int action)
9467 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9470 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9472 int sound_effect = element_info[element].sound[action];
9474 if (sound_effect != SND_UNDEFINED)
9475 PlayLevelSound(x, y, sound_effect);
9478 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9481 int sound_effect = element_info[element].sound[action];
9483 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9484 PlayLevelSound(x, y, sound_effect);
9487 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9489 int sound_effect = element_info[Feld[x][y]].sound[action];
9491 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9492 PlayLevelSound(x, y, sound_effect);
9495 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9497 int sound_effect = element_info[Feld[x][y]].sound[action];
9499 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9500 StopSound(sound_effect);
9503 static void PlayLevelMusic()
9505 if (levelset.music[level_nr] != MUS_UNDEFINED)
9506 PlayMusic(levelset.music[level_nr]); /* from config file */
9508 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9511 void RaiseScore(int value)
9513 local_player->score += value;
9514 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9517 void RaiseScoreElement(int element)
9523 case EL_EMERALD_YELLOW:
9524 case EL_EMERALD_RED:
9525 case EL_EMERALD_PURPLE:
9526 case EL_SP_INFOTRON:
9527 RaiseScore(level.score[SC_EMERALD]);
9530 RaiseScore(level.score[SC_DIAMOND]);
9533 RaiseScore(level.score[SC_CRYSTAL]);
9536 RaiseScore(level.score[SC_PEARL]);
9539 case EL_BD_BUTTERFLY:
9540 case EL_SP_ELECTRON:
9541 RaiseScore(level.score[SC_BUG]);
9545 case EL_SP_SNIKSNAK:
9546 RaiseScore(level.score[SC_SPACESHIP]);
9549 case EL_DARK_YAMYAM:
9550 RaiseScore(level.score[SC_YAMYAM]);
9553 RaiseScore(level.score[SC_ROBOT]);
9556 RaiseScore(level.score[SC_PACMAN]);
9559 RaiseScore(level.score[SC_NUT]);
9562 case EL_SP_DISK_RED:
9563 case EL_DYNABOMB_INCREASE_NUMBER:
9564 case EL_DYNABOMB_INCREASE_SIZE:
9565 case EL_DYNABOMB_INCREASE_POWER:
9566 RaiseScore(level.score[SC_DYNAMITE]);
9568 case EL_SHIELD_NORMAL:
9569 case EL_SHIELD_DEADLY:
9570 RaiseScore(level.score[SC_SHIELD]);
9573 RaiseScore(level.score[SC_TIME_BONUS]);
9579 RaiseScore(level.score[SC_KEY]);
9582 RaiseScore(element_info[element].collect_score);
9587 void RequestQuitGame(boolean ask_if_really_quit)
9589 if (AllPlayersGone ||
9590 !ask_if_really_quit ||
9591 level_editor_test_game ||
9592 Request("Do you really want to quit the game ?",
9593 REQ_ASK | REQ_STAY_CLOSED))
9595 #if defined(PLATFORM_UNIX)
9596 if (options.network)
9597 SendToServer_StopPlaying();
9601 game_status = GAME_MODE_MAIN;
9607 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9612 /* ---------- new game button stuff ---------------------------------------- */
9614 /* graphic position values for game buttons */
9615 #define GAME_BUTTON_XSIZE 30
9616 #define GAME_BUTTON_YSIZE 30
9617 #define GAME_BUTTON_XPOS 5
9618 #define GAME_BUTTON_YPOS 215
9619 #define SOUND_BUTTON_XPOS 5
9620 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9622 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9623 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9624 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9625 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9626 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9627 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9634 } gamebutton_info[NUM_GAME_BUTTONS] =
9637 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
9642 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
9647 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
9652 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
9653 SOUND_CTRL_ID_MUSIC,
9654 "background music on/off"
9657 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
9658 SOUND_CTRL_ID_LOOPS,
9659 "sound loops on/off"
9662 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
9663 SOUND_CTRL_ID_SIMPLE,
9664 "normal sounds on/off"
9668 void CreateGameButtons()
9672 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9674 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9675 struct GadgetInfo *gi;
9678 unsigned long event_mask;
9679 int gd_xoffset, gd_yoffset;
9680 int gd_x1, gd_x2, gd_y1, gd_y2;
9683 gd_xoffset = gamebutton_info[i].x;
9684 gd_yoffset = gamebutton_info[i].y;
9685 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9686 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9688 if (id == GAME_CTRL_ID_STOP ||
9689 id == GAME_CTRL_ID_PAUSE ||
9690 id == GAME_CTRL_ID_PLAY)
9692 button_type = GD_TYPE_NORMAL_BUTTON;
9694 event_mask = GD_EVENT_RELEASED;
9695 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9696 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9700 button_type = GD_TYPE_CHECK_BUTTON;
9702 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9703 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9704 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9705 event_mask = GD_EVENT_PRESSED;
9706 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
9707 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9710 gi = CreateGadget(GDI_CUSTOM_ID, id,
9711 GDI_INFO_TEXT, gamebutton_info[i].infotext,
9712 GDI_X, DX + gd_xoffset,
9713 GDI_Y, DY + gd_yoffset,
9714 GDI_WIDTH, GAME_BUTTON_XSIZE,
9715 GDI_HEIGHT, GAME_BUTTON_YSIZE,
9716 GDI_TYPE, button_type,
9717 GDI_STATE, GD_BUTTON_UNPRESSED,
9718 GDI_CHECKED, checked,
9719 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9720 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9721 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9722 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9723 GDI_EVENT_MASK, event_mask,
9724 GDI_CALLBACK_ACTION, HandleGameButtons,
9728 Error(ERR_EXIT, "cannot create gadget");
9730 game_gadget[id] = gi;
9734 void FreeGameButtons()
9738 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9739 FreeGadget(game_gadget[i]);
9742 static void MapGameButtons()
9746 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9747 MapGadget(game_gadget[i]);
9750 void UnmapGameButtons()
9754 for (i = 0; i < NUM_GAME_BUTTONS; i++)
9755 UnmapGadget(game_gadget[i]);
9758 static void HandleGameButtons(struct GadgetInfo *gi)
9760 int id = gi->custom_id;
9762 if (game_status != GAME_MODE_PLAYING)
9767 case GAME_CTRL_ID_STOP:
9768 RequestQuitGame(TRUE);
9771 case GAME_CTRL_ID_PAUSE:
9772 if (options.network)
9774 #if defined(PLATFORM_UNIX)
9776 SendToServer_ContinuePlaying();
9778 SendToServer_PausePlaying();
9782 TapeTogglePause(TAPE_TOGGLE_MANUAL);
9785 case GAME_CTRL_ID_PLAY:
9788 #if defined(PLATFORM_UNIX)
9789 if (options.network)
9790 SendToServer_ContinuePlaying();
9794 tape.pausing = FALSE;
9795 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9800 case SOUND_CTRL_ID_MUSIC:
9801 if (setup.sound_music)
9803 setup.sound_music = FALSE;
9806 else if (audio.music_available)
9808 setup.sound = setup.sound_music = TRUE;
9810 SetAudioMode(setup.sound);
9816 case SOUND_CTRL_ID_LOOPS:
9817 if (setup.sound_loops)
9818 setup.sound_loops = FALSE;
9819 else if (audio.loops_available)
9821 setup.sound = setup.sound_loops = TRUE;
9822 SetAudioMode(setup.sound);
9826 case SOUND_CTRL_ID_SIMPLE:
9827 if (setup.sound_simple)
9828 setup.sound_simple = FALSE;
9829 else if (audio.sound_available)
9831 setup.sound = setup.sound_simple = TRUE;
9832 SetAudioMode(setup.sound);