X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=5d1f08f90516fbf347f65246e676f1c2fce9b631;hb=b06f921c487c5b1e30e44ec00ec2c08f366f83df;hp=8fdc17e2f81893af4fb7da2d2f07f6e5141d40e8;hpb=a765d70dda7da4500e7005ffa5913e24cd32c0fa;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 8fdc17e2..5d1f08f9 100644 --- a/src/game.c +++ b/src/game.c @@ -40,6 +40,8 @@ #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1) #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1) #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1) +#define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1) +#define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1) #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0) @@ -98,6 +100,11 @@ #define DX_TIME2 (DX + XX_TIME2) #define DY_TIME (DY + YY_TIME) +/* values for delayed check of falling and moving elements and for collision */ +#define CHECK_DELAY_MOVING 3 +#define CHECK_DELAY_FALLING 3 +#define CHECK_DELAY_COLLISION 2 + /* values for initial player move delay (initial delay counter value) */ #define INITIAL_MOVE_DELAY_OFF -1 #define INITIAL_MOVE_DELAY_ON 0 @@ -136,13 +143,15 @@ RND(element_info[e].move_delay_random)) #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \ (element_info[e].move_delay_random)) -#define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\ +#define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\ RND(element_info[e].ce_value_random_initial)) +#define GET_CE_SCORE(e) ( (element_info[e].collect_score)) #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \ RND((c)->delay_random * (c)->delay_frames)) #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \ RND((c)->delay_random)) + #if 1 #define GET_VALID_RUNTIME_ELEMENT(e) \ ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e)) @@ -151,10 +160,13 @@ ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e)) #endif -#define GET_TARGET_ELEMENT(e, ch) \ - ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \ - (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \ - (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e)) +#define GET_TARGET_ELEMENT(e, ch, cv, cs) \ + ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \ + (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \ + (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \ + (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \ + (e) == EL_CURRENT_CE_VALUE ? (cv) : \ + (e) == EL_CURRENT_CE_SCORE ? (cs) : (e)) #define CAN_GROW_INTO(e) \ ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable)) @@ -327,6 +339,34 @@ static void PlayLevelMusic(); static void MapGameButtons(); static void HandleGameButtons(struct GadgetInfo *); +int AmoebeNachbarNr(int, int); +void AmoebeUmwandeln(int, int); +void ContinueMoving(int, int); +void Bang(int, int); +void InitMovDir(int, int); +void InitAmoebaNr(int, int); +int NewHiScore(void); + +void TestIfGoodThingHitsBadThing(int, int, int); +void TestIfBadThingHitsGoodThing(int, int, int); +void TestIfPlayerTouchesBadThing(int, int); +void TestIfPlayerRunsIntoBadThing(int, int, int); +void TestIfBadThingTouchesPlayer(int, int); +void TestIfBadThingRunsIntoPlayer(int, int, int); +void TestIfFriendTouchesBadThing(int, int); +void TestIfBadThingTouchesFriend(int, int); +void TestIfBadThingTouchesOtherBadThing(int, int); + +void KillPlayer(struct PlayerInfo *); +void BuryPlayer(struct PlayerInfo *); +void RemovePlayer(struct PlayerInfo *); + +boolean SnapField(struct PlayerInfo *, int, int); +boolean DropElement(struct PlayerInfo *); + +static int getInvisibleActiveFromInvisibleElement(int); +static int getInvisibleFromInvisibleActiveElement(int); + static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS]; @@ -684,6 +724,18 @@ static int playfield_scan_delta_y = 1; (x) >= 0 && (x) <= lev_fieldx - 1; \ (x) += playfield_scan_delta_x) \ +#ifdef DEBUG +void DEBUG_SetMaximumDynamite() +{ + int i; + + for (i = 0; i < MAX_INVENTORY_SIZE; i++) + if (local_player->inventory_size < MAX_INVENTORY_SIZE) + local_player->inventory_element[local_player->inventory_size++] = + EL_DYNAMITE; +} +#endif + static void InitPlayfieldScanModeVars() { if (game.use_reverse_scan_direction) @@ -817,6 +869,32 @@ static int getBeltDirFromBeltSwitchElement(int element) return belt_move_dir[belt_dir_nr]; } +static int get_element_from_group_element(int element) +{ + if (IS_GROUP_ELEMENT(element)) + { + struct ElementGroupInfo *group = element_info[element].group; + int last_anim_random_frame = gfx.anim_random_frame; + int element_pos; + + if (group->choice_mode == ANIM_RANDOM) + gfx.anim_random_frame = RND(group->num_elements_resolved); + + element_pos = getAnimationFrame(group->num_elements_resolved, 1, + group->choice_mode, 0, + group->choice_pos); + + if (group->choice_mode == ANIM_RANDOM) + gfx.anim_random_frame = last_anim_random_frame; + + group->choice_pos++; + + element = group->element_resolved[element_pos]; + } + + return element; +} + static void InitPlayerField(int x, int y, int element, boolean init_game) { if (element == EL_SP_MURPHY) @@ -832,6 +910,9 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) else { stored_player[0].use_murphy = TRUE; + + if (!level.use_artwork_element[0]) + stored_player[0].artwork_element = EL_SP_MURPHY; } Feld[x][y] = EL_PLAYER_1; @@ -927,41 +1008,45 @@ static void InitField(int x, int y, boolean init_game) Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT; break; + case EL_BUG: case EL_BUG_RIGHT: case EL_BUG_UP: case EL_BUG_LEFT: case EL_BUG_DOWN: - case EL_BUG: + case EL_SPACESHIP: case EL_SPACESHIP_RIGHT: case EL_SPACESHIP_UP: case EL_SPACESHIP_LEFT: case EL_SPACESHIP_DOWN: - case EL_SPACESHIP: + case EL_BD_BUTTERFLY: case EL_BD_BUTTERFLY_RIGHT: case EL_BD_BUTTERFLY_UP: case EL_BD_BUTTERFLY_LEFT: case EL_BD_BUTTERFLY_DOWN: - case EL_BD_BUTTERFLY: + case EL_BD_FIREFLY: case EL_BD_FIREFLY_RIGHT: case EL_BD_FIREFLY_UP: case EL_BD_FIREFLY_LEFT: case EL_BD_FIREFLY_DOWN: - case EL_BD_FIREFLY: case EL_PACMAN_RIGHT: case EL_PACMAN_UP: case EL_PACMAN_LEFT: case EL_PACMAN_DOWN: case EL_YAMYAM: + case EL_YAMYAM_LEFT: + case EL_YAMYAM_RIGHT: + case EL_YAMYAM_UP: + case EL_YAMYAM_DOWN: case EL_DARK_YAMYAM: case EL_ROBOT: case EL_PACMAN: case EL_SP_SNIKSNAK: case EL_SP_ELECTRON: + case EL_MOLE: case EL_MOLE_LEFT: case EL_MOLE_RIGHT: case EL_MOLE_UP: case EL_MOLE_DOWN: - case EL_MOLE: InitMovDir(x, y); break; @@ -1044,6 +1129,14 @@ static void InitField(int x, int y, boolean init_game) game.light_time_left = level.time_light * FRAMES_PER_SECOND; break; + case EL_INVISIBLE_STEELWALL: + case EL_INVISIBLE_WALL: + case EL_INVISIBLE_SAND: + if (game.light_time_left > 0 || + game.lenses_time_left > 0) + Feld[x][y] = getInvisibleActiveFromInvisibleElement(element); + break; + case EL_EMC_MAGIC_BALL: if (game.ball_state) Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE; @@ -1063,7 +1156,7 @@ static void InitField(int x, int y, boolean init_game) #if USE_NEW_CUSTOM_VALUE if (!element_info[element].use_last_ce_value || init_game) - CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]); + CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]); #endif } #else @@ -1072,6 +1165,11 @@ static void InitField(int x, int y, boolean init_game) #endif else if (IS_GROUP_ELEMENT(element)) { +#if 1 + Feld[x][y] = get_element_from_group_element(element); + + InitField(x, y, init_game); +#else struct ElementGroupInfo *group = element_info[element].group; int last_anim_random_frame = gfx.anim_random_frame; int element_pos; @@ -1091,6 +1189,7 @@ static void InitField(int x, int y, boolean init_game) Feld[x][y] = group->element_resolved[element_pos]; InitField(x, y, init_game); +#endif } break; } @@ -1105,7 +1204,7 @@ static void InitField(int x, int y, boolean init_game) #if USE_NEW_CUSTOM_VALUE #if 1 - CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]); + CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]); #else CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial; #endif @@ -1148,24 +1247,32 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game) inline void DrawGameValue_Emeralds(int value) { - DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2); + int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2; + + DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2); } inline void DrawGameValue_Dynamite(int value) { - DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2); + int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2; + + DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2); } inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) { + int base_key_graphic = EL_KEY_1; int i; + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + base_key_graphic = EL_EM_KEY_1; + /* currently only 4 of 8 possible keys are displayed */ for (i = 0; i < STD_NUM_KEYS; i++) { if (key[i]) DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS, - el2edimg(EL_KEY_1 + i)); + el2edimg(base_key_graphic + i)); else BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS, @@ -1175,15 +1282,24 @@ inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) inline void DrawGameValue_Score(int value) { - DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2); + int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2; + + DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2); } inline void DrawGameValue_Time(int value) { + int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2; + int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2; + + /* clear background if value just changed its size */ + if (value == 999 || value == 1000) + ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14); + if (value < 1000) - DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2); + DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2); else - DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER); + DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER); } inline void DrawGameValue_Level(int value) @@ -1231,7 +1347,9 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time, void DrawGameDoorValues() { - int i; + int dynamite_state = 0; + int key_bits = 0; + int i, j; if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { @@ -1240,6 +1358,7 @@ void DrawGameDoorValues() return; } +#if 0 DrawGameValue_Level(level_nr); DrawGameValue_Emeralds(local_player->gems_still_needed); @@ -1247,8 +1366,37 @@ void DrawGameDoorValues() DrawGameValue_Score(local_player->score); DrawGameValue_Time(TimeLeft); - for (i = 0; i < MAX_PLAYERS; i++) +#else + + if (game.centered_player_nr == -1) + { + for (i = 0; i < MAX_PLAYERS; i++) + { + for (j = 0; j < MAX_NUM_KEYS; j++) + if (stored_player[i].key[j]) + key_bits |= (1 << j); + + dynamite_state += stored_player[i].inventory_size; + } + +#if 0 DrawGameValue_Keys(stored_player[i].key); +#endif + } + else + { + int player_nr = game.centered_player_nr; + + for (i = 0; i < MAX_NUM_KEYS; i++) + if (stored_player[player_nr].key[i]) + key_bits |= (1 << i); + + dynamite_state = stored_player[player_nr].inventory_size; + } + + DrawAllGameValues(local_player->gems_still_needed, dynamite_state, + local_player->score, TimeLeft, key_bits); +#endif } #if 0 @@ -1559,6 +1707,7 @@ static void InitGameEngine() ei->change_page[j].actual_trigger_player = EL_PLAYER_1; ei->change_page[j].actual_trigger_side = CH_SIDE_NONE; ei->change_page[j].actual_trigger_ce_value = 0; + ei->change_page[j].actual_trigger_ce_score = 0; } } @@ -1740,6 +1889,10 @@ int get_num_special_action(int element, int action_first, int action_last) break; } +#if 0 + printf("::: %d->%d: %d\n", action_first, action_last, num_special_action); +#endif + return num_special_action; } @@ -1825,6 +1978,7 @@ void InitGame() player->is_pushing = FALSE; player->is_switching = FALSE; player->is_dropping = FALSE; + player->is_dropping_pressed = FALSE; player->is_bored = FALSE; player->is_sleeping = FALSE; @@ -1835,11 +1989,15 @@ void InitGame() player->anim_delay_counter = 0; player->post_delay_counter = 0; + player->dir_waiting = MV_NONE; player->action_waiting = ACTION_DEFAULT; player->last_action_waiting = ACTION_DEFAULT; player->special_action_bored = ACTION_DEFAULT; player->special_action_sleeping = ACTION_DEFAULT; +#if 1 + /* cannot be set here -- could be modified in Init[Player]Field() below */ +#else /* set number of special actions for bored and sleeping animation */ player->num_special_action_bored = get_num_special_action(player->artwork_element, @@ -1847,6 +2005,7 @@ void InitGame() player->num_special_action_sleeping = get_num_special_action(player->artwork_element, ACTION_SLEEPING_1, ACTION_SLEEPING_LAST); +#endif player->switch_x = -1; player->switch_y = -1; @@ -1873,6 +2032,7 @@ void InitGame() player->push_delay_value = game.initial_push_delay_value; player->drop_delay = 0; + player->drop_pressed_delay = 0; player->last_jx = player->last_jy = 0; player->jx = player->jy = 0; @@ -1933,7 +2093,22 @@ void InitGame() game.envelope_active = FALSE; - game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */ + /* set focus to local player for network games, else to all players */ + game.centered_player_nr = (network_playing ? local_player->index_nr : -1); + game.centered_player_nr_next = game.centered_player_nr; + game.set_centered_player = FALSE; + + if (network_playing && tape.recording) + { + /* store client dependent player focus when recording network games */ + tape.centered_player_nr_next = game.centered_player_nr_next; + tape.set_centered_player = TRUE; + } + +#if 0 + printf("::: focus set to player %d [%d]\n", + game.centered_player_nr, local_player->index_nr); +#endif for (i = 0; i < NUM_BELTS; i++) { @@ -2000,6 +2175,22 @@ void InitGame() InitBeltMovement(); + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + +#if 1 + /* set number of special actions for bored and sleeping animation */ + player->num_special_action_bored = + get_num_special_action(player->artwork_element, + ACTION_BORING_1, ACTION_BORING_LAST); + player->num_special_action_sleeping = + get_num_special_action(player->artwork_element, + ACTION_SLEEPING_1, ACTION_SLEEPING_LAST); +#endif + + } + game.emulation = (emulate_bd ? EMU_BOULDERDASH : emulate_sb ? EMU_SOKOBAN : emulate_sp ? EMU_SUPAPLEX : EMU_NONE); @@ -2099,7 +2290,7 @@ void InitGame() if (tape.playing) { - /* when playing a tape, eliminate all players which do not participate */ + /* when playing a tape, eliminate all players who do not participate */ for (i = 0; i < MAX_PLAYERS; i++) { @@ -2431,6 +2622,14 @@ void InitMovDir(int x, int y) MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT]; break; + case EL_YAMYAM_LEFT: + case EL_YAMYAM_RIGHT: + case EL_YAMYAM_UP: + case EL_YAMYAM_DOWN: + Feld[x][y] = EL_YAMYAM; + MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT]; + break; + case EL_SP_SNIKSNAK: MovDir[x][y] = MV_UP; break; @@ -2723,7 +2922,7 @@ int NewHiScore() LoadScore(level_nr); - if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 || + if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) || local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) return -1; @@ -2739,7 +2938,7 @@ int NewHiScore() #ifdef ONE_PER_NAME for (l = k; l < MAX_SCORE_ENTRIES; l++) - if (!strcmp(setup.player_name, highscore[l].Name)) + if (strEqual(setup.player_name, highscore[l].Name)) m = l; if (m == k) /* player's new highscore overwrites his old one */ goto put_into_list; @@ -2823,10 +3022,24 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir) } } -static void ResetRandomAnimationValue(int x, int y) +#if USE_GFX_RESET_GFX_ANIMATION +static void ResetGfxFrame(int x, int y, boolean redraw) { - GfxRandom[x][y] = INIT_GFX_RANDOM(); + int element = Feld[x][y]; + int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + int last_gfx_frame = GfxFrame[x][y]; + + if (graphic_info[graphic].anim_global_sync) + GfxFrame[x][y] = FrameCounter; + else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) + GfxFrame[x][y] = CustomValue[x][y]; + else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + GfxFrame[x][y] = element_info[element].collect_score; + + if (redraw && GfxFrame[x][y] != last_gfx_frame) + DrawLevelGraphicAnimation(x, y, graphic); } +#endif static void ResetGfxAnimation(int x, int y) { @@ -2834,9 +3047,9 @@ static void ResetGfxAnimation(int x, int y) int element, graphic; #endif - GfxFrame[x][y] = 0; GfxAction[x][y] = ACTION_DEFAULT; GfxDir[x][y] = MovDir[x][y]; + GfxFrame[x][y] = 0; #if 0 element = Feld[x][y]; @@ -2849,6 +3062,15 @@ static void ResetGfxAnimation(int x, int y) else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) GfxFrame[x][y] = element_info[element].collect_score; #endif + +#if USE_GFX_RESET_GFX_ANIMATION + ResetGfxFrame(x, y, FALSE); +#endif +} + +static void ResetRandomAnimationValue(int x, int y) +{ + GfxRandom[x][y] = INIT_GFX_RANDOM(); } void InitMovingField(int x, int y, int direction) @@ -3097,6 +3319,183 @@ void CheckDynamite(int x, int y) Bang(x, y); } +#if 1 + +static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2) +{ + boolean num_checked_players = 0; + int i; + + for (i = 0; i < MAX_PLAYERS; i++) + { + if (stored_player[i].active) + { + int sx = stored_player[i].jx; + int sy = stored_player[i].jy; + + if (num_checked_players == 0) + { + *sx1 = *sx2 = sx; + *sy1 = *sy2 = sy; + } + else + { + *sx1 = MIN(*sx1, sx); + *sy1 = MIN(*sy1, sy); + *sx2 = MAX(*sx2, sx); + *sy2 = MAX(*sy2, sy); + } + + num_checked_players++; + } + } +} + +static boolean checkIfAllPlayersFitToScreen_RND() +{ + int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0; + + setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2); + + return (sx2 - sx1 < SCR_FIELDX && + sy2 - sy1 < SCR_FIELDY); +} + +static void setScreenCenteredToAllPlayers(int *sx, int *sy) +{ + int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y; + + setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2); + + *sx = (sx1 + sx2) / 2; + *sy = (sy1 + sy2) / 2; +} + +#if 0 +static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy, + int center_x, int center_y) +{ + int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y; + + setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2); + + *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x)); + *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y)); +} + +static boolean checkIfAllPlayersAreVisible(int center_x, int center_y) +{ + int max_dx, max_dy; + + setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y); + + return (max_dx <= SCR_FIELDX / 2 && + max_dy <= SCR_FIELDY / 2); +} +#endif + +#endif + +#if 1 + +void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen, + boolean quick_relocation) +{ + boolean ffwd_delay = (tape.playing && tape.fast_forward); + boolean no_delay = (tape.warp_forward); + int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay); + int wait_delay_value = (no_delay ? 0 : frame_delay_value); + + if (quick_relocation) + { + int offset = (setup.scroll_delay ? 3 : 0); + +#if 0 + if (center_screen) + offset = 0; +#endif + + if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen) + { + scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : + x > SBX_Right + MIDPOSX ? SBX_Right : + x - MIDPOSX); + + scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper : + y > SBY_Lower + MIDPOSY ? SBY_Lower : + y - MIDPOSY); + } + else + { + if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) || + (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset)) + scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset); + + if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) || + (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset)) + scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset); + + /* don't scroll over playfield boundaries */ + if (scroll_x < SBX_Left || scroll_x > SBX_Right) + scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right); + + /* don't scroll over playfield boundaries */ + if (scroll_y < SBY_Upper || scroll_y > SBY_Lower) + scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower); + } + + RedrawPlayfield(TRUE, 0,0,0,0); + } + else + { + int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left : + x > SBX_Right + MIDPOSX ? SBX_Right : + x - MIDPOSX); + + int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper : + y > SBY_Lower + MIDPOSY ? SBY_Lower : + y - MIDPOSY); + + ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */ + + while (scroll_x != scroll_xx || scroll_y != scroll_yy) + { + int dx = 0, dy = 0; + int fx = FX, fy = FY; + + dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0); + dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0); + + if (dx == 0 && dy == 0) /* no scrolling needed at all */ + break; + + scroll_x -= dx; + scroll_y -= dy; + + fx += dx * TILEX / 2; + fy += dy * TILEY / 2; + + ScrollLevel(dx, dy); + DrawAllPlayers(); + + /* scroll in two steps of half tile size to make things smoother */ + BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY); + FlushDisplay(); + Delay(wait_delay_value); + + /* scroll second step to align at full tile size */ + BackToFront(); + Delay(wait_delay_value); + } + + DrawAllPlayers(); + BackToFront(); + Delay(wait_delay_value); + } +} + +#else + void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation) { boolean ffwd_delay = (tape.playing && tape.fast_forward); @@ -3143,23 +3542,21 @@ void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation) } else { - int scroll_xx = -999, scroll_yy = -999; + int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left : + player->jx > SBX_Right + MIDPOSX ? SBX_Right : + player->jx - MIDPOSX); + + int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : + player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : + player->jy - MIDPOSY); ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */ - while (scroll_xx != scroll_x || scroll_yy != scroll_y) + while (scroll_x != scroll_xx || scroll_y != scroll_yy) { int dx = 0, dy = 0; int fx = FX, fy = FY; - scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left : - player->jx > SBX_Right + MIDPOSX ? SBX_Right : - player->jx - MIDPOSX); - - scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : - player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : - player->jy - MIDPOSY); - dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0); dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0); @@ -3191,6 +3588,8 @@ void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation) } } +#endif + void RelocatePlayer(int jx, int jy, int el_player_raw) { int el_player = GET_PLAYER_ELEMENT(el_player_raw); @@ -3268,8 +3667,13 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) #if 1 /* only visually relocate centered player */ +#if 1 + DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE, + level.instant_relocation); +#else if (player->index_nr == game.centered_player_nr) DrawRelocatePlayer(player, level.instant_relocation); +#endif #else if (player == local_player) /* only visually relocate local player */ DrawRelocatePlayer(player, level.instant_relocation); @@ -3530,6 +3934,11 @@ void Explode(int ex, int ey, int phase, int mode) Feld[x][y] = EL_EXPLOSION; GfxElement[x][y] = artwork_element; +#if 0 + printf(":: setting gfx(%d,%d) to %d ['%s']\n", + x, y, artwork_element, EL_NAME(artwork_element)); +#endif + ExplodePhase[x][y] = 1; ExplodeDelay[x][y] = last_phase; @@ -3564,6 +3973,11 @@ void Explode(int ex, int ey, int phase, int mode) #endif #if 1 +#if 1 + /* this can happen if the player leaves an explosion just in time */ + if (GfxElement[x][y] == EL_UNDEFINED) + GfxElement[x][y] = EL_EMPTY; +#else if (GfxElement[x][y] == EL_UNDEFINED) { printf("\n\n"); @@ -3573,6 +3987,8 @@ void Explode(int ex, int ey, int phase, int mode) GfxElement[x][y] = EL_EMPTY; } +#endif + #endif border_element = Store2[x][y]; @@ -5314,7 +5730,7 @@ inline static void TurnRoundExt(int x, int y) static void TurnRound(int x, int y) { int direction = MovDir[x][y]; -#if 1 +#if 0 int element, graphic; #endif @@ -5329,6 +5745,8 @@ static void TurnRound(int x, int y) GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction); #if 1 + ResetGfxFrame(x, y, FALSE); +#else element = Feld[x][y]; graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); @@ -6471,13 +6889,13 @@ void ContinueMoving(int x, int y) int nextx = newx + dx, nexty = newy + dy; boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty); - WasJustMoving[newx][newy] = 3; + WasJustMoving[newx][newy] = CHECK_DELAY_MOVING; if (CAN_FALL(element) && direction == MV_DOWN) - WasJustFalling[newx][newy] = 3; + WasJustFalling[newx][newy] = CHECK_DELAY_FALLING; if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again) - CheckCollision[newx][newy] = 2; + CheckCollision[newx][newy] = CHECK_DELAY_COLLISION; } if (DONT_TOUCH(element)) /* object may be nasty to player or others */ @@ -7513,6 +7931,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { struct ElementInfo *ei = &element_info[element]; struct ElementChangeInfo *change = &ei->change_page[page]; + int target_element = change->target_element; int action_type = change->action_type; int action_mode = change->action_mode; int action_arg = change->action_arg; @@ -7551,8 +7970,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_type == CA_SET_LEVEL_GEMS ? 999 : action_type == CA_SET_LEVEL_TIME ? 9999 : action_type == CA_SET_LEVEL_SCORE ? 99999 : - action_type == CA_SET_CE_SCORE ? 9999 : action_type == CA_SET_CE_VALUE ? 9999 : + action_type == CA_SET_CE_SCORE ? 9999 : CA_ARG_MAX); int action_arg_number_reset = @@ -7560,12 +7979,12 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_type == CA_SET_LEVEL_GEMS ? level.gems_needed : action_type == CA_SET_LEVEL_TIME ? level.time : action_type == CA_SET_LEVEL_SCORE ? 0 : - action_type == CA_SET_CE_SCORE ? 0 : #if 1 - action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) : + action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) : #else action_type == CA_SET_CE_VALUE ? ei->custom_value_initial : #endif + action_type == CA_SET_CE_SCORE ? 0 : 0); int action_arg_number = @@ -7576,18 +7995,20 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min : action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max : action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset : - action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score : #if USE_NEW_CUSTOM_VALUE action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] : #else action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial : #endif + action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score : action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) : action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value : action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed : action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score : - action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) : + action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element): action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value: + action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) : + action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score: action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element : action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element : -1); @@ -7596,8 +8017,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed : action_type == CA_SET_LEVEL_TIME ? TimeLeft : action_type == CA_SET_LEVEL_SCORE ? local_player->score : - action_type == CA_SET_CE_SCORE ? ei->collect_score : action_type == CA_SET_CE_VALUE ? CustomValue[x][y] : + action_type == CA_SET_CE_SCORE ? ei->collect_score : 0); int action_arg_number_new = @@ -7740,7 +8161,11 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { stored_player[i].key[KEY_NR(element)] = key_state; +#if 1 + DrawGameDoorValues(); +#else DrawGameValue_Keys(stored_player[i].key); +#endif redraw_mask |= REDRAW_DOOR_1; } @@ -7854,25 +8279,18 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) /* ---------- CE actions ---------------------------------------------- */ - case CA_SET_CE_SCORE: - { - ei->collect_score = action_arg_number_new; - - break; - } - case CA_SET_CE_VALUE: { #if USE_NEW_CUSTOM_VALUE - int last_custom_value = CustomValue[x][y]; + int last_ce_value = CustomValue[x][y]; CustomValue[x][y] = action_arg_number_new; #if 0 - printf("::: Count == %d\n", CustomValue[x][y]); + printf("::: CE value == %d\n", CustomValue[x][y]); #endif - if (CustomValue[x][y] == 0 && last_custom_value > 0) + if (CustomValue[x][y] == 0 && last_ce_value > 0) { #if 0 printf("::: CE_VALUE_GETS_ZERO\n"); @@ -7881,6 +8299,35 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO); CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X); +#if 0 + printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]); +#endif + } +#endif + + break; + } + + case CA_SET_CE_SCORE: + { +#if USE_NEW_CUSTOM_VALUE + int last_ce_score = ei->collect_score; + + ei->collect_score = action_arg_number_new; + +#if 0 + printf("::: CE score == %d\n", ei->collect_score); +#endif + + if (ei->collect_score == 0 && last_ce_score > 0) + { +#if 0 + printf("::: CE_SCORE_GETS_ZERO\n"); +#endif + + CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO); + CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X); + #if 0 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]); #endif @@ -7906,22 +8353,26 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) static void CreateFieldExt(int x, int y, int element, boolean is_change) { + int old_element = Feld[x][y]; + int new_element = get_element_from_group_element(element); int previous_move_direction = MovDir[x][y]; #if USE_NEW_CUSTOM_VALUE int last_ce_value = CustomValue[x][y]; #endif - boolean add_player = (ELEM_IS_PLAYER(element) && - IS_WALKABLE(Feld[x][y])); + boolean add_player = (ELEM_IS_PLAYER(new_element) && + IS_WALKABLE(old_element)); +#if 0 /* check if element under player changes from accessible to unaccessible (needed for special case of dropping element which then changes) */ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && - IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element)) + IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) { Bang(x, y); return; } +#endif if (!add_player) { @@ -7930,30 +8381,51 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) else RemoveField(x, y); - Feld[x][y] = element; + Feld[x][y] = new_element; +#if !USE_GFX_RESET_GFX_ANIMATION ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); +#endif - if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS) + if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS) MovDir[x][y] = previous_move_direction; #if USE_NEW_CUSTOM_VALUE - if (element_info[Feld[x][y]].use_last_ce_value) + if (element_info[new_element].use_last_ce_value) CustomValue[x][y] = last_ce_value; #endif InitField_WithBug1(x, y, FALSE); + new_element = Feld[x][y]; /* element may have changed */ + +#if USE_GFX_RESET_GFX_ANIMATION + ResetGfxAnimation(x, y); + ResetRandomAnimationValue(x, y); +#endif + DrawLevelField(x, y); - if (GFX_CRUMBLED(Feld[x][y])) + if (GFX_CRUMBLED(new_element)) DrawLevelFieldCrumbledSandNeighbours(x, y); } +#if 1 + /* check if element under player changes from accessible to unaccessible + (needed for special case of dropping element which then changes) */ + if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && + IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) + { + Bang(x, y); + + return; + } +#endif + /* "ChangeCount" not set yet to allow "entered by player" change one time */ - if (ELEM_IS_PLAYER(element)) - RelocatePlayer(x, y, element); + if (ELEM_IS_PLAYER(new_element)) + RelocatePlayer(x, y, new_element); if (is_change) ChangeCount[x][y]++; /* count number of changes in the same frame */ @@ -7990,7 +8462,10 @@ static void CreateElementFromChange(int x, int y, int element) static boolean ChangeElement(int x, int y, int element, int page) { - struct ElementChangeInfo *change = &element_info[element].change_page[page]; + struct ElementInfo *ei = &element_info[element]; + struct ElementChangeInfo *change = &ei->change_page[page]; + int ce_value = CustomValue[x][y]; + int ce_score = ei->collect_score; int target_element; int old_element = Feld[x][y]; @@ -8005,6 +8480,7 @@ static boolean ChangeElement(int x, int y, int element, int page) change->actual_trigger_player = EL_PLAYER_1; change->actual_trigger_side = CH_SIDE_NONE; change->actual_trigger_ce_value = 0; + change->actual_trigger_ce_score = 0; } /* do not change elements more than a specified maximum number of changes */ @@ -8109,7 +8585,8 @@ static boolean ChangeElement(int x, int y, int element, int page) ChangeEvent[ex][ey] = ChangeEvent[x][y]; content_element = change->target_content.e[xx][yy]; - target_element = GET_TARGET_ELEMENT(content_element, change); + target_element = GET_TARGET_ELEMENT(content_element, change, + ce_value, ce_score); CreateElementFromChange(ex, ey, target_element); @@ -8130,7 +8607,8 @@ static boolean ChangeElement(int x, int y, int element, int page) } else { - target_element = GET_TARGET_ELEMENT(change->target_element, change); + target_element = GET_TARGET_ELEMENT(change->target_element, change, + ce_value, ce_score); if (element == EL_DIAGONAL_GROWING || element == EL_DIAGONAL_SHRINKING) @@ -8364,6 +8842,7 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player); change->actual_trigger_side = trigger_side; change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y]; + change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element); if ((change->can_change && !change_done) || change->has_action) { @@ -8468,6 +8947,7 @@ static boolean CheckElementChangeExt(int x, int y, change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player); change->actual_trigger_side = trigger_side; change->actual_trigger_ce_value = CustomValue[x][y]; + change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element); /* special case: trigger element not at (x,y) position for some events */ if (check_trigger_element) @@ -8490,6 +8970,7 @@ static boolean CheckElementChangeExt(int x, int y, int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy; change->actual_trigger_ce_value = CustomValue[xx][yy]; + change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element); } if (change->can_change && !change_done) @@ -8558,6 +9039,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) boolean last_waiting = player->is_waiting; int move_dir = player->MovDir; + player->dir_waiting = move_dir; player->last_action_waiting = player->action_waiting; if (is_waiting) @@ -8575,7 +9057,11 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) game.player_sleeping_delay_fixed + SimpleRND(game.player_sleeping_delay_random); +#if 1 + InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir); +#else InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir); +#endif } if (game.player_sleeping_delay_fixed + @@ -8593,6 +9079,26 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) player->is_bored ? ACTION_BORING : ACTION_WAITING); +#if 1 + if (player->is_sleeping && player->use_murphy) + { + /* special case for sleeping Murphy when leaning against non-free tile */ + + if (!IN_LEV_FIELD(player->jx - 1, player->jy) || + (Feld[player->jx - 1][player->jy] != EL_EMPTY && + !IS_MOVING(player->jx - 1, player->jy))) + move_dir = MV_LEFT; + else if (!IN_LEV_FIELD(player->jx + 1, player->jy) || + (Feld[player->jx + 1][player->jy] != EL_EMPTY && + !IS_MOVING(player->jx + 1, player->jy))) + move_dir = MV_RIGHT; + else + player->is_sleeping = FALSE; + + player->dir_waiting = move_dir; + } +#endif + if (player->is_sleeping) { if (player->num_special_action_sleeping > 0) @@ -8675,6 +9181,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) player->anim_delay_counter = 0; player->post_delay_counter = 0; + player->dir_waiting = player->MovDir; player->action_waiting = ACTION_DEFAULT; player->special_action_bored = ACTION_DEFAULT; @@ -8691,8 +9198,8 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) int down = player_action & JOY_DOWN; int button1 = player_action & JOY_BUTTON_1; int button2 = player_action & JOY_BUTTON_2; - int dx = (left ? -1 : right ? 1 : 0); - int dy = (up ? -1 : down ? 1 : 0); + int dx = (left ? -1 : right ? 1 : 0); + int dy = (up ? -1 : down ? 1 : 0); if (!player->active || tape.pausing) return 0; @@ -8737,15 +9244,95 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) player->is_moving = FALSE; player->is_dropping = FALSE; + player->is_dropping_pressed = FALSE; + player->drop_pressed_delay = 0; return 0; } } +static void CheckLevelTime() +{ + int i; + + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + { + if (level.native_em_level->lev->home == 0) /* all players at home */ + { + local_player->LevelSolved = TRUE; + AllPlayersGone = TRUE; + + level.native_em_level->lev->home = -1; + } + + if (level.native_em_level->ply[0]->alive == 0 && + level.native_em_level->ply[1]->alive == 0 && + level.native_em_level->ply[2]->alive == 0 && + level.native_em_level->ply[3]->alive == 0) /* all dead */ + AllPlayersGone = TRUE; + } + + if (TimeFrames >= FRAMES_PER_SECOND) + { + TimeFrames = 0; + TapeTime++; + + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + if (SHIELD_ON(player)) + { + player->shield_normal_time_left--; + + if (player->shield_deadly_time_left > 0) + player->shield_deadly_time_left--; + } + } + + if (!level.use_step_counter) + { + TimePlayed++; + + if (TimeLeft > 0) + { + TimeLeft--; + + if (TimeLeft <= 10 && setup.time_limit) + PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE); + + DrawGameValue_Time(TimeLeft); + + if (!TimeLeft && setup.time_limit) + { + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + level.native_em_level->lev->killed_out_of_time = TRUE; + else + for (i = 0; i < MAX_PLAYERS; i++) + KillPlayer(&stored_player[i]); + } + } + else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ + DrawGameValue_Time(TimePlayed); + + level.native_em_level->lev->time = + (level.time == 0 ? TimePlayed : TimeLeft); + } + + if (tape.recording || tape.playing) + DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); + } +} + void AdvanceFrameAndPlayerCounters(int player_nr) { int i; +#if 0 + Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d", + FrameCounter, FrameCounter + 1); +#endif + /* advance frame counters (global frame counter and time frame counter) */ FrameCounter++; TimeFrames++; @@ -8787,20 +9374,72 @@ void AdvanceFrameAndPlayerCounters(int player_nr) if (stored_player[i].drop_delay > 0) stored_player[i].drop_delay--; + + if (stored_player[i].is_dropping_pressed) + stored_player[i].drop_pressed_delay++; } } +void StartGameActions(boolean init_network_game, boolean record_tape, + long random_seed) +{ + unsigned long new_random_seed = InitRND(random_seed); + + if (record_tape) + TapeStartRecording(new_random_seed); + +#if defined(NETWORK_AVALIABLE) + if (init_network_game) + { + SendToServer_StartPlaying(); + + return; + } +#endif + + StopAnimation(); + + game_status = GAME_MODE_PLAYING; + + InitGame(); +} + void GameActions() { static unsigned long game_frame_delay = 0; unsigned long game_frame_delay_value; - int magic_wall_x = 0, magic_wall_y = 0; - int i, x, y, element, graphic; byte *recorded_player_action; byte summarized_player_action = 0; byte tape_action[MAX_PLAYERS]; + int i; - if (game_status != GAME_MODE_PLAYING) + if (game.restart_level) + StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE); + + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + { + if (level.native_em_level->lev->home == 0) /* all players at home */ + { + local_player->LevelSolved = TRUE; + AllPlayersGone = TRUE; + + level.native_em_level->lev->home = -1; + } + + if (level.native_em_level->ply[0]->alive == 0 && + level.native_em_level->ply[1]->alive == 0 && + level.native_em_level->ply[2]->alive == 0 && + level.native_em_level->ply[3]->alive == 0) /* all dead */ + AllPlayersGone = TRUE; + } + + if (local_player->LevelSolved) + GameWon(); + + if (AllPlayersGone && !TAPE_IS_STOPPED(tape)) + TapeStop(); + + if (game_status != GAME_MODE_PLAYING) /* status might have changed */ return; game_frame_delay_value = @@ -8813,40 +9452,6 @@ void GameActions() WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value); - InitPlayfieldScanModeVars(); - - if (ScreenMovPos == 0) /* screen currently aligned at tile position */ - { - struct PlayerInfo *player; - int player_nr = game.centered_player_nr_next; - - if (game.centered_player_nr_next == -1) - player_nr = local_player->index_nr; - - player = &stored_player[player_nr]; - - if (!player->active) - game.centered_player_nr_next = game.centered_player_nr; - - if (game.centered_player_nr != game.centered_player_nr_next) - { - DrawRelocatePlayer(player, setup.quick_switch); - - game.centered_player_nr = game.centered_player_nr_next; - } - } - -#if USE_ONE_MORE_CHANGE_PER_FRAME - if (game.engine_version >= VERSION_IDENT(3,2,0,7)) - { - SCAN_PLAYFIELD(x, y) - { - ChangeCount[x][y] = 0; - ChangeEvent[x][y] = -1; - } - } -#endif - if (network_playing && !network_player_action_received) { /* try to get network player actions in time */ @@ -8862,19 +9467,27 @@ void GameActions() if (!network_player_action_received) return; /* failed to get network player actions in time */ + + /* do not yet reset "network_player_action_received" (for tape.pausing) */ } if (tape.pausing) return; - recorded_player_action = (tape.playing ? TapePlayAction() : NULL); + /* at this point we know that we really continue executing the game */ #if 1 - /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */ - if (recorded_player_action == NULL && tape.pausing) - return; + network_player_action_received = FALSE; #endif + recorded_player_action = (tape.playing ? TapePlayAction() : NULL); + + if (tape.set_centered_player) + { + game.centered_player_nr_next = tape.centered_player_nr_next; + game.set_centered_player = TRUE; + } + for (i = 0; i < MAX_PLAYERS; i++) { summarized_player_action |= stored_player[i].action; @@ -8891,6 +9504,13 @@ void GameActions() if (!options.network && !setup.team_mode) local_player->effective_action = summarized_player_action; + if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1) + { + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].effective_action = + (i == game.centered_player_nr ? summarized_player_action : 0); + } + if (recorded_player_action != NULL) for (i = 0; i < MAX_PLAYERS; i++) stored_player[i].effective_action = recorded_player_action[i]; @@ -8899,14 +9519,103 @@ void GameActions() { tape_action[i] = stored_player[i].effective_action; + /* (this can only happen in the R'n'D game engine) */ if (tape.recording && tape_action[i] && !tape.player_participates[i]) tape.player_participates[i] = TRUE; /* player just appeared from CE */ } - /* only save actions from input devices, but not programmed actions */ + /* only record actions from input devices, but not programmed actions */ if (tape.recording) TapeRecordAction(tape_action); + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + { + GameActions_EM_Main(); + } + else + { + GameActions_RND(); + } +} + +void GameActions_EM_Main() +{ + byte effective_action[MAX_PLAYERS]; + boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); + int i; + + for (i = 0; i < MAX_PLAYERS; i++) + effective_action[i] = stored_player[i].effective_action; + + GameActions_EM(effective_action, warp_mode); + + CheckLevelTime(); + + AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */ +} + +void GameActions_RND() +{ + int magic_wall_x = 0, magic_wall_y = 0; + int i, x, y, element, graphic; + + InitPlayfieldScanModeVars(); + +#if USE_ONE_MORE_CHANGE_PER_FRAME + if (game.engine_version >= VERSION_IDENT(3,2,0,7)) + { + SCAN_PLAYFIELD(x, y) + { + ChangeCount[x][y] = 0; + ChangeEvent[x][y] = -1; + } + } +#endif + +#if 1 + if (game.set_centered_player) + { + boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND(); + + /* switching to "all players" only possible if all players fit to screen */ + if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen) + { + game.centered_player_nr_next = game.centered_player_nr; + game.set_centered_player = FALSE; + } + + /* do not switch focus to non-existing (or non-active) player */ + if (game.centered_player_nr_next >= 0 && + !stored_player[game.centered_player_nr_next].active) + { + game.centered_player_nr_next = game.centered_player_nr; + game.set_centered_player = FALSE; + } + } + + if (game.set_centered_player && + ScreenMovPos == 0) /* screen currently aligned at tile position */ + { + int sx, sy; + + if (game.centered_player_nr_next == -1) + { + setScreenCenteredToAllPlayers(&sx, &sy); + } + else + { + sx = stored_player[game.centered_player_nr_next].jx; + sy = stored_player[game.centered_player_nr_next].jy; + } + + game.centered_player_nr = game.centered_player_nr_next; + game.set_centered_player = FALSE; + + DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch); + DrawGameDoorValues(); + } +#endif + for (i = 0; i < MAX_PLAYERS; i++) { int actual_player_action = stored_player[i].effective_action; @@ -8938,7 +9647,9 @@ void GameActions() ScrollPlayer(&stored_player[i], SCROLL_GO_ON); } +#if 0 network_player_action_received = FALSE; +#endif ScrollScreen(NULL, SCROLL_GO_ON); @@ -9066,6 +9777,9 @@ void GameActions() printf("::: Yo man! Rocks can fall!\n"); #endif +#if 1 + ResetGfxFrame(x, y, TRUE); +#else if (graphic_info[graphic].anim_global_sync) GfxFrame[x][y] = FrameCounter; else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) @@ -9090,6 +9804,7 @@ void GameActions() #endif DrawLevelGraphicAnimation(x, y, graphic); } +#endif if (ANIM_MODE(graphic) == ANIM_RANDOM && IS_NEXT_FRAME(GfxFrame[x][y], graphic)) @@ -9385,48 +10100,7 @@ void GameActions() } } - if (TimeFrames >= FRAMES_PER_SECOND) - { - TimeFrames = 0; - TapeTime++; - - for (i = 0; i < MAX_PLAYERS; i++) - { - struct PlayerInfo *player = &stored_player[i]; - - if (SHIELD_ON(player)) - { - player->shield_normal_time_left--; - - if (player->shield_deadly_time_left > 0) - player->shield_deadly_time_left--; - } - } - - if (!level.use_step_counter) - { - TimePlayed++; - - if (TimeLeft > 0) - { - TimeLeft--; - - if (TimeLeft <= 10 && setup.time_limit) - PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE); - - DrawGameValue_Time(TimeLeft); - - if (!TimeLeft && setup.time_limit) - for (i = 0; i < MAX_PLAYERS; i++) - KillPlayer(&stored_player[i]); - } - else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ - DrawGameValue_Time(TimePlayed); - } - - if (tape.recording || tape.playing) - DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); - } + CheckLevelTime(); DrawAllPlayers(); PlayAllPlayersSound(); @@ -9915,6 +10589,8 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->is_snapping = FALSE; player->is_switching = FALSE; player->is_dropping = FALSE; + player->is_dropping_pressed = FALSE; + player->drop_pressed_delay = 0; } else { @@ -11169,7 +11845,11 @@ int DigField(struct PlayerInfo *player, if (player->inventory_size < MAX_INVENTORY_SIZE) player->inventory_element[player->inventory_size++] = element; +#if 1 + DrawGameDoorValues(); +#else DrawGameValue_Dynamite(local_player->inventory_size); +#endif } else if (element == EL_DYNABOMB_INCREASE_NUMBER) { @@ -11188,7 +11868,11 @@ int DigField(struct PlayerInfo *player, { player->key[KEY_NR(element)] = TRUE; +#if 1 + DrawGameDoorValues(); +#else DrawGameValue_Keys(player->key); +#endif redraw_mask |= REDRAW_DOOR_1; } @@ -11220,7 +11904,11 @@ int DigField(struct PlayerInfo *player, if (player->inventory_size < MAX_INVENTORY_SIZE) player->inventory_element[player->inventory_size++] = element; +#if 1 + DrawGameDoorValues(); +#else DrawGameValue_Dynamite(local_player->inventory_size); +#endif } else if (collect_count > 0) { @@ -11566,6 +12254,8 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) dx == +1 ? MV_RIGHT : dy == -1 ? MV_UP : dy == +1 ? MV_DOWN : MV_NONE); + boolean can_continue_snapping = (level.continuous_snapping && + WasJustFalling[x][y] < CHECK_DELAY_FALLING); if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0)) return FALSE; @@ -11593,8 +12283,14 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) return FALSE; } +#if USE_NEW_CONTINUOUS_SNAPPING + /* prevent snapping with already pressed snap key when not allowed */ + if (player->is_snapping && !can_continue_snapping) + return FALSE; +#else if (player->is_snapping) return FALSE; +#endif player->MovDir = snap_direction; @@ -11606,6 +12302,8 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) } player->is_dropping = FALSE; + player->is_dropping_pressed = FALSE; + player->drop_pressed_delay = 0; if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION) return FALSE; @@ -11641,6 +12339,8 @@ boolean DropElement(struct PlayerInfo *player) EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr : EL_UNDEFINED); + player->is_dropping_pressed = TRUE; + /* do not drop an element on top of another element; when holding drop key pressed without moving, dropped element must move away before the next element can be dropped (this is especially important if the next element @@ -11668,6 +12368,10 @@ boolean DropElement(struct PlayerInfo *player) if (new_element == EL_UNDEFINED) return FALSE; + /* check if drop key was pressed long enough for EM style dynamite */ + if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40) + return FALSE; + /* check if anything can be dropped at the current position */ if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION) return FALSE; @@ -11689,7 +12393,11 @@ boolean DropElement(struct PlayerInfo *player) { player->inventory_size--; +#if 1 + DrawGameDoorValues(); +#else DrawGameValue_Dynamite(local_player->inventory_size); +#endif if (new_element == EL_DYNAMITE) new_element = EL_DYNAMITE_ACTIVE; @@ -11749,12 +12457,15 @@ boolean DropElement(struct PlayerInfo *player) nexty = dropy + GET_DY_FROM_DIR(move_direction); ChangeCount[dropx][dropy] = 0; /* allow at least one more change */ - CheckCollision[dropx][dropy] = 2; + CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION; } player->drop_delay = GET_NEW_DROP_DELAY(drop_element); player->is_dropping = TRUE; + player->drop_pressed_delay = 0; + player->is_dropping_pressed = FALSE; + player->drop_x = dropx; player->drop_y = dropy; @@ -12132,7 +12843,7 @@ void RequestQuitGame(boolean ask_if_really_quit) { #if defined(NETWORK_AVALIABLE) if (options.network) - SendToServer_StopPlaying(); + SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER); else #endif { @@ -12309,7 +13020,10 @@ static void HandleGameButtons(struct GadgetInfo *gi) switch (id) { case GAME_CTRL_ID_STOP: - RequestQuitGame(TRUE); + if (tape.playing) + TapeStop(); + else + RequestQuitGame(TRUE); break; case GAME_CTRL_ID_PAUSE: @@ -12336,7 +13050,7 @@ static void HandleGameButtons(struct GadgetInfo *gi) #endif { tape.pausing = FALSE; - DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0); + DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0); } } break;