X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=cf7193f87cafe33d5009ae32ac232b784fbb3bb0;hb=f926e522aef77158e0011ae5ad2cf8805509d6d1;hp=f99a7627ed5b1039efa550375a786215b9eb4033;hpb=7696ec27e2e2abd764bc955a32928c58b7deed7f;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index f99a7627..cf7193f8 100644 --- a/src/game.c +++ b/src/game.c @@ -65,6 +65,8 @@ #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1) +#define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1) + #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0) #if USE_DELAYED_GFX_REDRAW @@ -1167,6 +1169,8 @@ static int recursion_loop_depth; static boolean recursion_loop_detected; static boolean recursion_loop_element; +static int map_player_action[MAX_PLAYERS]; + /* ------------------------------------------------------------------------- */ /* definition of elements that automatically change to other elements after */ @@ -3706,6 +3710,8 @@ void InitGame() player->present = FALSE; player->active = FALSE; + player->mapped = FALSE; + player->killed = FALSE; player->reanimated = FALSE; @@ -3846,6 +3852,8 @@ void InitGame() player->LevelSolved_SaveScore = FALSE; player->LevelSolved_CountingTime = 0; player->LevelSolved_CountingScore = 0; + + map_player_action[i] = i; } network_player_action_received = FALSE; @@ -4038,6 +4046,124 @@ void InitGame() if (game.belt_dir[i] == MV_NONE) game.belt_dir_nr[i] = 3; /* not moving, next moving left */ +#if USE_NEW_PLAYER_ASSIGNMENTS + /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */ + /* choose default local player */ + local_player = &stored_player[0]; + + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].connected = FALSE; + + local_player->connected = TRUE; + /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */ + + if (tape.playing) + { + /* try to guess locally connected team mode players (needed for correct + assignment of player figures from level to locally playing players) */ + + for (i = 0; i < MAX_PLAYERS; i++) + if (tape.player_participates[i]) + stored_player[i].connected = TRUE; + } + else if (setup.team_mode && !options.network) + { + /* try to guess locally connected team mode players (needed for correct + assignment of player figures from level to locally playing players) */ + + for (i = 0; i < MAX_PLAYERS; i++) + if (setup.input[i].use_joystick || + setup.input[i].key.left != KSYM_UNDEFINED) + stored_player[i].connected = TRUE; + } + +#if 0 + for (i = 0; i < MAX_PLAYERS; i++) + printf("::: player %d: %s\n", i, + (stored_player[i].connected ? "connected" : "not connected")); + + for (i = 0; i < MAX_PLAYERS; i++) + printf("::: player %d: %s\n", i, + (stored_player[i].present ? "present" : "not present")); +#endif + + /* check if any connected player was not found in playfield */ + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + if (player->connected && !player->present) + { + struct PlayerInfo *field_player = NULL; + +#if 0 + printf("::: looking for field player for player %d ...\n", i); +#endif + + /* assign first free player found that is present in the playfield */ + + /* first try: look for unmapped playfield player that is not connected */ + if (field_player == NULL) + for (j = 0; j < MAX_PLAYERS; j++) + if (stored_player[j].present && + !stored_player[j].mapped && + !stored_player[j].connected) + field_player = &stored_player[j]; + + /* second try: look for *any* unmapped playfield player */ + if (field_player == NULL) + for (j = 0; j < MAX_PLAYERS; j++) + if (stored_player[j].present && + !stored_player[j].mapped) + field_player = &stored_player[j]; + + if (field_player != NULL) + { + int jx = field_player->jx, jy = field_player->jy; + +#if 0 + printf("::: found player figure %d\n", field_player->index_nr); +#endif + + player->present = FALSE; + player->active = FALSE; + + field_player->present = TRUE; + field_player->active = TRUE; + + /* + player->initial_element = field_player->initial_element; + player->artwork_element = field_player->artwork_element; + + player->block_last_field = field_player->block_last_field; + player->block_delay_adjustment = field_player->block_delay_adjustment; + */ + + StorePlayer[jx][jy] = field_player->element_nr; + + field_player->jx = field_player->last_jx = jx; + field_player->jy = field_player->last_jy = jy; + + if (local_player == player) + local_player = field_player; + + map_player_action[field_player->index_nr] = i; + + field_player->mapped = TRUE; + +#if 0 + printf("::: map_player_action[%d] == %d\n", + field_player->index_nr, i); +#endif + } + } + + if (player->connected && player->present) + player->mapped = TRUE; + } + +#else + /* check if any connected player was not found in playfield */ for (i = 0; i < MAX_PLAYERS; i++) { @@ -4047,25 +4173,26 @@ void InitGame() { for (j = 0; j < MAX_PLAYERS; j++) { - struct PlayerInfo *some_player = &stored_player[j]; - int jx = some_player->jx, jy = some_player->jy; + struct PlayerInfo *field_player = &stored_player[j]; + int jx = field_player->jx, jy = field_player->jy; /* assign first free player found that is present in the playfield */ - if (some_player->present && !some_player->connected) + if (field_player->present && !field_player->connected) { player->present = TRUE; player->active = TRUE; - some_player->present = FALSE; - some_player->active = FALSE; + field_player->present = FALSE; + field_player->active = FALSE; - player->initial_element = some_player->initial_element; - player->artwork_element = some_player->artwork_element; + player->initial_element = field_player->initial_element; + player->artwork_element = field_player->artwork_element; - player->block_last_field = some_player->block_last_field; - player->block_delay_adjustment = some_player->block_delay_adjustment; + player->block_last_field = field_player->block_last_field; + player->block_delay_adjustment = field_player->block_delay_adjustment; StorePlayer[jx][jy] = player->element_nr; + player->jx = player->last_jx = jx; player->jy = player->last_jy = jy; @@ -4074,14 +4201,21 @@ void InitGame() } } } +#endif + +#if 0 + printf("::: local_player->present == %d\n", local_player->present); +#endif if (tape.playing) { /* when playing a tape, eliminate all players who do not participate */ +#if USE_NEW_PLAYER_ASSIGNMENTS for (i = 0; i < MAX_PLAYERS; i++) { - if (stored_player[i].active && !tape.player_participates[i]) + if (stored_player[i].active && + !tape.player_participates[map_player_action[i]]) { struct PlayerInfo *player = &stored_player[i]; int jx = player->jx, jy = player->jy; @@ -4091,6 +4225,21 @@ void InitGame() Feld[jx][jy] = EL_EMPTY; } } +#else + for (i = 0; i < MAX_PLAYERS; i++) + { + if (stored_player[i].active && + !tape.player_participates[i]) + { + struct PlayerInfo *player = &stored_player[i]; + int jx = player->jx, jy = player->jy; + + player->active = FALSE; + StorePlayer[jx][jy] = 0; + Feld[jx][jy] = EL_EMPTY; + } + } +#endif } else if (!options.network && !setup.team_mode) /* && !tape.playing */ { @@ -4121,9 +4270,15 @@ void InitGame() /* when recording the game, store which players take part in the game */ if (tape.recording) { +#if USE_NEW_PLAYER_ASSIGNMENTS + for (i = 0; i < MAX_PLAYERS; i++) + if (stored_player[i].connected) + tape.player_participates[i] = TRUE; +#else for (i = 0; i < MAX_PLAYERS; i++) if (stored_player[i].active) tape.player_participates[i] = TRUE; +#endif } if (options.debug) @@ -4313,6 +4468,13 @@ void InitGame() /* blit playfield from scroll buffer to normal back buffer for fading in */ BlitScreenToBitmap_EM(backbuffer); } + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + { + InitGameEngine_SP(); + + /* blit playfield from scroll buffer to normal back buffer for fading in */ + BlitScreenToBitmap_SP(backbuffer); + } else { DrawLevel(); @@ -5348,8 +5510,6 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, if (quick_relocation) { - int offset = game.scroll_delay_value; - if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen) { if (!level.shifted_relocation || center_screen) @@ -5390,8 +5550,47 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, } else { +#if 1 + if (!level.shifted_relocation || center_screen) + { + /* quick relocation (without scrolling), with centering of 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 + { + /* quick relocation (without scrolling), but do not center screen */ + + int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left : + old_x > SBX_Right + MIDPOSX ? SBX_Right : + old_x - MIDPOSX); + + int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper : + old_y > SBY_Lower + MIDPOSY ? SBY_Lower : + old_y - MIDPOSY); + + int offset_x = x + (scroll_x - center_scroll_x); + int offset_y = y + (scroll_y - center_scroll_y); + + scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left : + offset_x > SBX_Right + MIDPOSX ? SBX_Right : + offset_x - MIDPOSX); + + scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper : + offset_y > SBY_Lower + MIDPOSY ? SBY_Lower : + offset_y - MIDPOSY); + } +#else /* quick relocation (without scrolling), inside visible screen area */ + int offset = game.scroll_delay_value; + 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); @@ -5407,6 +5606,7 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, /* 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); +#endif } RedrawPlayfield(TRUE, 0,0,0,0); @@ -5572,10 +5772,19 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) Feld[jx][jy] = el_player; InitPlayerField(jx, jy, el_player, TRUE); + /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be + possible that the relocation target field did not contain a player element, + but a walkable element, to which the new player was relocated -- in this + case, restore that (already initialized!) element on the player field */ if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */ { - Feld[jx][jy] = element; + Feld[jx][jy] = element; /* restore previously existing element */ +#if 0 + /* !!! do not initialize already initialized element a second time !!! */ + /* (this causes at least problems with "element creation" CE trigger for + already existing elements, and existing Sokoban fields counted twice) */ InitField(jx, jy, FALSE); +#endif } /* only visually relocate centered player */ @@ -5591,6 +5800,21 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X, player->index_bit, enter_side); + +#if 1 + if (player->is_switching) + { + /* ensure that relocation while still switching an element does not cause + a new element to be treated as also switched directly after relocation + (this is important for teleporter switches that teleport the player to + a place where another teleporter switch is in the same direction, which + would then incorrectly be treated as immediately switched before the + direction key that caused the switch was released) */ + + player->switch_x += jx - old_jx; + player->switch_y += jy - old_jy; + } +#endif } void Explode(int ex, int ey, int phase, int mode) @@ -10380,6 +10604,33 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } + case CA_SET_LEVEL_RANDOM_SEED: + { +#if 1 + /* ensure that setting a new random seed while playing is predictable */ + InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1); +#else + InitRND(action_arg_number_new); +#endif + +#if 0 + printf("::: %d -> %d\n", action_arg_number_new, RND(10)); +#endif + +#if 0 + { + int i; + + printf("::: "); + for (i = 0; i < 9; i++) + printf("%d, ", RND(2)); + printf("\n"); + } +#endif + + break; + } + /* ---------- player actions ------------------------------------------ */ case CA_MOVE_PLAYER: @@ -11073,6 +11324,7 @@ static void HandleElementChange(int x, int y, int page) int element = MovingOrBlocked2Element(x, y); struct ElementInfo *ei = &element_info[element]; struct ElementChangeInfo *change = &ei->change_page[page]; + boolean handle_action_before_change = FALSE; #ifdef DEBUG if (!CAN_CHANGE_OR_HAS_ACTION(element) && @@ -11155,6 +11407,15 @@ static void HandleElementChange(int x, int y, int page) return; } +#if 1 + /* special case: set new level random seed before changing element */ + if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED) + handle_action_before_change = TRUE; + + if (change->has_action && handle_action_before_change) + ExecuteCustomElementAction(x, y, element, page); +#endif + if (change->can_change) { if (ChangeElement(x, y, element, page)) @@ -11164,7 +11425,7 @@ static void HandleElementChange(int x, int y, int page) } } - if (change->has_action) + if (change->has_action && !handle_action_before_change) ExecuteCustomElementAction(x, y, element, page); } } @@ -11731,7 +11992,16 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) if (tape.single_step && tape.recording && !tape.pausing) { +#if 1 + /* as it is called "single step mode", just return to pause mode when the + player stopped moving after one tile (or never starts moving at all) */ + if (!player->is_moving) +#else + /* this is buggy: there are quite some cases where the single step mode + does not return to pause mode (like pushing things that don't move + or simply by trying to run against a wall) */ if (button1 || (dropped && !moved)) +#endif { TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); SnapField(player, 0, 0); /* stop snapping */ @@ -11768,6 +12038,7 @@ static void CheckLevelTime() { int i; + /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */ if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { if (level.native_em_level->lev->home == 0) /* all players at home */ @@ -11785,6 +12056,21 @@ static void CheckLevelTime() level.native_em_level->ply[3]->alive == 0) /* all dead */ AllPlayersGone = TRUE; } + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + { + if (game_sp.LevelSolved && + !game_sp.GameOver) /* game won */ + { + PlayerWins(local_player); + + game_sp.GameOver = TRUE; + + AllPlayersGone = TRUE; + } + + if (game_sp.GameOver) /* game lost */ + AllPlayersGone = TRUE; + } if (TimeFrames >= FRAMES_PER_SECOND) { @@ -11960,8 +12246,9 @@ void GameActions() } if (game.restart_level) - StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE); + StartGameActions(options.network, setup.autorecord, level.random_seed); + /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */ if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { if (level.native_em_level->lev->home == 0) /* all players at home */ @@ -11979,6 +12266,21 @@ void GameActions() level.native_em_level->ply[3]->alive == 0) /* all dead */ AllPlayersGone = TRUE; } + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + { + if (game_sp.LevelSolved && + !game_sp.GameOver) /* game won */ + { + PlayerWins(local_player); + + game_sp.GameOver = TRUE; + + AllPlayersGone = TRUE; + } + + if (game_sp.GameOver) /* game lost */ + AllPlayersGone = TRUE; + } if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd) GameWon(); @@ -12080,10 +12382,26 @@ void GameActions() if (tape.recording) TapeRecordAction(tape_action); +#if USE_NEW_PLAYER_ASSIGNMENTS + { + byte mapped_action[MAX_PLAYERS]; + + for (i = 0; i < MAX_PLAYERS; i++) + mapped_action[i] = stored_player[map_player_action[i]].effective_action; + + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].effective_action = mapped_action[i]; + } +#endif + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { GameActions_EM_Main(); } + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + { + GameActions_SP_Main(); + } else { GameActions_RND(); @@ -12106,6 +12424,22 @@ void GameActions_EM_Main() AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */ } +void GameActions_SP_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_SP(effective_action, warp_mode); + + CheckLevelTime(); + + AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */ +} + void GameActions_RND() { int magic_wall_x = 0, magic_wall_y = 0; @@ -13535,8 +13869,14 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (Feld[jx][jy] == EL_EXIT_OPEN || Feld[jx][jy] == EL_EM_EXIT_OPEN || +#if 1 + Feld[jx][jy] == EL_EM_EXIT_OPENING || +#endif Feld[jx][jy] == EL_STEEL_EXIT_OPEN || Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN || +#if 1 + Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING || +#endif Feld[jx][jy] == EL_SP_EXIT_OPEN || Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */ { @@ -14673,8 +15013,14 @@ static int DigField(struct PlayerInfo *player, } else if (element == EL_EXIT_OPEN || element == EL_EM_EXIT_OPEN || +#if 1 + element == EL_EM_EXIT_OPENING || +#endif element == EL_STEEL_EXIT_OPEN || element == EL_EM_STEEL_EXIT_OPEN || +#if 1 + element == EL_EM_STEEL_EXIT_OPENING || +#endif element == EL_SP_EXIT_OPEN || element == EL_SP_EXIT_OPENING) { @@ -15052,8 +15398,13 @@ static int DigField(struct PlayerInfo *player, PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY, ACTION_FILLING); +#if 1 + if (local_player->sokobanfields_still_needed == 0 && + (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban)) +#else if (local_player->sokobanfields_still_needed == 0 && game.emulation == EMU_SOKOBAN) +#endif { PlayerWins(player); @@ -16062,8 +16413,6 @@ unsigned int RND(int max) /* game engine snapshot handling functions */ /* ------------------------------------------------------------------------- */ -#define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x)) - struct EngineSnapshotInfo { /* runtime values for custom element collect score */ @@ -16077,28 +16426,10 @@ struct EngineSnapshotInfo int belt_anim_mode[4 * NUM_BELT_PARTS]; }; -struct EngineSnapshotNodeInfo -{ - void *buffer_orig; - void *buffer_copy; - int size; -}; - static struct EngineSnapshotInfo engine_snapshot_rnd; -static ListNode *engine_snapshot_list = NULL; static char *snapshot_level_identifier = NULL; static int snapshot_level_nr = -1; -void FreeEngineSnapshot() -{ - while (engine_snapshot_list != NULL) - deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key, - checked_free); - - setString(&snapshot_level_identifier, NULL); - snapshot_level_nr = -1; -} - static void SaveEngineSnapshotValues_RND() { static int belt_base_active_element[4] = @@ -16184,36 +16515,26 @@ static void LoadEngineSnapshotValues_RND() } } -static void SaveEngineSnapshotBuffer(void *buffer, int size) -{ - struct EngineSnapshotNodeInfo *bi = - checked_calloc(sizeof(struct EngineSnapshotNodeInfo)); - - bi->buffer_orig = buffer; - bi->buffer_copy = checked_malloc(size); - bi->size = size; - - memcpy(bi->buffer_copy, buffer, size); - - addNodeToList(&engine_snapshot_list, NULL, bi); -} - void SaveEngineSnapshot() { - FreeEngineSnapshot(); /* free previous snapshot, if needed */ - - if (level_editor_test_game) /* do not save snapshots from editor */ + /* do not save snapshots from editor */ + if (level_editor_test_game) return; + /* free previous snapshot buffers, if needed */ + FreeEngineSnapshotBuffers(); + /* copy some special values to a structure better suited for the snapshot */ SaveEngineSnapshotValues_RND(); SaveEngineSnapshotValues_EM(); + SaveEngineSnapshotValues_SP(); /* save values stored in special snapshot structure */ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd)); SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em)); + SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp)); /* save further RND engine values */ @@ -16287,7 +16608,7 @@ void SaveEngineSnapshot() snapshot_level_nr = level_nr; #if 0 - ListNode *node = engine_snapshot_list; + ListNode *node = engine_snapshot_list_rnd; int num_bytes = 0; while (node != NULL) @@ -16301,29 +16622,17 @@ void SaveEngineSnapshot() #endif } -static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi) -{ - memcpy(bi->buffer_orig, bi->buffer_copy, bi->size); -} - void LoadEngineSnapshot() { - ListNode *node = engine_snapshot_list; + /* restore generically stored snapshot buffers */ - if (engine_snapshot_list == NULL) - return; - - while (node != NULL) - { - LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content); - - node = node->next; - } + LoadEngineSnapshotBuffers(); /* restore special values from snapshot structure */ LoadEngineSnapshotValues_RND(); LoadEngineSnapshotValues_EM(); + LoadEngineSnapshotValues_SP(); } boolean CheckEngineSnapshot()