X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=367a86a4e38ed0663bc41a4ad40075ff393e0880;hb=cd8ffa5913dbad16024c5cf4232610c69dc7fac4;hp=4e1c699d28a5768cc56a2a6c80586f85a9f85403;hpb=3c0d675c61f850035b0c13853d93180c6b16dae7;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 4e1c699d..367a86a4 100644 --- a/src/game.c +++ b/src/game.c @@ -1058,7 +1058,10 @@ static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *); static void KillPlayerUnlessEnemyProtected(int, int); static void KillPlayerUnlessExplosionProtected(int, int); +static void CheckNextToConditions(int, int); +static void TestIfPlayerNextToCustomElement(int, int); static void TestIfPlayerTouchesCustomElement(int, int); +static void TestIfElementNextToCustomElement(int, int); static void TestIfElementTouchesCustomElement(int, int); static void TestIfElementHitsCustomElement(int, int, int); @@ -1106,7 +1109,7 @@ void ContinueMoving(int, int); void Bang(int, int); void InitMovDir(int, int); void InitAmoebaNr(int, int); -void NewHighScore(int); +void NewHighScore(int, boolean); void TestIfGoodThingHitsBadThing(int, int, int); void TestIfBadThingHitsGoodThing(int, int, int); @@ -2025,6 +2028,14 @@ static void InitField(int x, int y, boolean init_game) InitField(x, y, init_game); } + else if (IS_EMPTY_ELEMENT(element)) + { + GfxElementEmpty[x][y] = element; + Tile[x][y] = EL_EMPTY; + + if (element_info[element].use_gfx_element) + game.use_masked_elements = TRUE; + } break; } @@ -3875,7 +3886,9 @@ void InitGame(void) GfxFrame[x][y] = 0; GfxRandom[x][y] = INIT_GFX_RANDOM(); + GfxRandomStatic[x][y] = INIT_GFX_RANDOM(); GfxElement[x][y] = EL_UNDEFINED; + GfxElementEmpty[x][y] = EL_EMPTY; GfxAction[x][y] = ACTION_DEFAULT; GfxDir[x][y] = MV_NONE; GfxRedraw[x][y] = GFX_REDRAW_NONE; @@ -4476,6 +4489,8 @@ void InitGame(void) if (setup.sound_music) PlayLevelMusic(); } + + SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions); } void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y, @@ -4717,6 +4732,19 @@ static void LevelSolved_SetFinalGameValues(void) game.LevelSolved_CountingHealth = game.health_final; } +static void LevelSolved_DisplayFinalGameValues(int time, int score, int health) +{ + game.LevelSolved_CountingTime = time; + game.LevelSolved_CountingScore = score; + game.LevelSolved_CountingHealth = health; + + game_panel_controls[GAME_PANEL_TIME].value = time; + game_panel_controls[GAME_PANEL_SCORE].value = score; + game_panel_controls[GAME_PANEL_HEALTH].value = health; + + DisplayGameControlValues(); +} + static void LevelSolved(void) { if (level.game_engine_type == GAME_ENGINE_TYPE_RND && @@ -4777,6 +4805,9 @@ void GameWon(void) score = score_final = game.score_final; health = health_final = game.health_final; + // update game panel values before (delayed) counting of score (if any) + LevelSolved_DisplayFinalGameValues(time, score, health); + // if level has time score defined, calculate new final game values if (time_score > 0) { @@ -4817,13 +4848,7 @@ void GameWon(void) time = time_final; score = score_final; - game.LevelSolved_CountingTime = time; - game.LevelSolved_CountingScore = score; - - game_panel_controls[GAME_PANEL_TIME].value = time; - game_panel_controls[GAME_PANEL_SCORE].value = score; - - DisplayGameControlValues(); + LevelSolved_DisplayFinalGameValues(time, score, health); } if (level.game_engine_type == GAME_ENGINE_TYPE_RND) @@ -4900,13 +4925,7 @@ void GameWon(void) if (time == time_final) score = score_final; - game.LevelSolved_CountingTime = time; - game.LevelSolved_CountingScore = score; - - game_panel_controls[GAME_PANEL_TIME].value = time; - game_panel_controls[GAME_PANEL_SCORE].value = score; - - DisplayGameControlValues(); + LevelSolved_DisplayFinalGameValues(time, score, health); if (time == time_final) StopSound(SND_GAME_LEVELTIME_BONUS); @@ -4932,13 +4951,7 @@ void GameWon(void) health += health_count_dir; score += time_score; - game.LevelSolved_CountingHealth = health; - game.LevelSolved_CountingScore = score; - - game_panel_controls[GAME_PANEL_HEALTH].value = health; - game_panel_controls[GAME_PANEL_SCORE].value = score; - - DisplayGameControlValues(); + LevelSolved_DisplayFinalGameValues(time, score, health); if (health == health_final) StopSound(SND_GAME_LEVELTIME_BONUS); @@ -4967,6 +4980,7 @@ void GameEnd(void) { // used instead of "level_nr" (needed for network games) int last_level_nr = levelset.level_nr; + boolean tape_saved = FALSE; game.LevelSolved_GameEnd = TRUE; @@ -4976,7 +4990,8 @@ void GameEnd(void) if (!global.use_envelope_request) CloseDoor(DOOR_CLOSE_1); - SaveTapeChecked_LevelSolved(tape.level_nr); // ask to save tape + // ask to save tape + tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr); // set unique basename for score tape (also saved in high score table) strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name)); @@ -5011,7 +5026,7 @@ void GameEnd(void) } // save score and score tape before potentially erasing tape below - NewHighScore(last_level_nr); + NewHighScore(last_level_nr, tape_saved); if (setup.increment_levels && level_nr < leveldir_current->last_level && @@ -5119,7 +5134,7 @@ static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry, return -1; } -void NewHighScore(int level_nr) +void NewHighScore(int level_nr, boolean tape_saved) { struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119) boolean one_per_name = FALSE; @@ -5134,19 +5149,37 @@ void NewHighScore(int level_nr) scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name); - if (scores.last_added >= 0) - { - SaveScore(level_nr); + if (scores.last_added < 0) + return; - // store last added local score entry (before merging server scores) - scores.last_added_local = scores.last_added; - } + SaveScore(level_nr); - if (game.LevelSolved_SaveTape) + // store last added local score entry (before merging server scores) + scores.last_added_local = scores.last_added; + + if (!game.LevelSolved_SaveTape) + return; + + SaveScoreTape(level_nr); + + if (setup.ask_for_using_api_server) { - SaveScoreTape(level_nr); - SaveServerScore(level_nr); + setup.use_api_server = + Request("Upload your score and tape to the high score server?", REQ_ASK); + + if (!setup.use_api_server) + Request("Not using high score server! Use setup menu to enable again!", + REQ_CONFIRM); + + runtime.use_api_server = setup.use_api_server; + + // after asking for using API server once, do not ask again + setup.ask_for_using_api_server = FALSE; + + SaveSetup_ServerSetup(); } + + SaveServerScore(level_nr, tape_saved); } void MergeServerScore(void) @@ -5452,7 +5485,7 @@ void DrawDynamite(int x, int y) else if (game.use_masked_elements) DrawLevelElement(x, y, EL_EMPTY); - frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]); + frame = getGraphicAnimationFrameXY(graphic, x, y); if (Back[x][y] || Store[x][y] || game.use_masked_elements) DrawGraphicThruMask(sx, sy, graphic, frame); @@ -6011,7 +6044,7 @@ static void Explode(int ex, int ey, int phase, int mode) else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y))) { int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING); - int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]); + int frame = getGraphicAnimationFrameXY(graphic, x, y); if (phase == delay) TEST_DrawLevelFieldCrumbled(x, y); @@ -8200,7 +8233,7 @@ static void StartMoving(int x, int y) dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT : dir == MV_UP ? IMG_FLAMES_1_UP : dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY); - int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]); + int frame = getGraphicAnimationFrameXY(graphic, x, y); GfxAction[x][y] = ACTION_ATTACKING; @@ -8387,6 +8420,9 @@ static void StartMoving(int x, int y) GfxDir[x][y] = diagonal_move_dir; ChangeDelay[x][y] = change_delay; + if (Store[x][y] == EL_EMPTY) + Store[x][y] = GfxElementEmpty[x][y]; + graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]); @@ -11132,7 +11168,8 @@ static boolean CheckElementChangeExt(int x, int y, different to element changes that affect other elements to change on the whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */ boolean check_trigger_element = - (trigger_event == CE_TOUCHING_X || + (trigger_event == CE_NEXT_TO_X || + trigger_event == CE_TOUCHING_X || trigger_event == CE_HITTING_X || trigger_event == CE_HIT_BY_X || trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3 @@ -11713,7 +11750,7 @@ static void GameActionsExt(void) Warn("element '%s' caused endless loop in game engine", EL_NAME(recursion_loop_element)); - RequestQuitGameExt(FALSE, level_editor_test_game, message); + RequestQuitGameExt(program.headless, level_editor_test_game, message); recursion_loop_detected = FALSE; // if game should be continued @@ -12265,6 +12302,9 @@ void GameActions_RND(void) graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); last_gfx_frame = GfxFrame[x][y]; + if (element == EL_EMPTY) + graphic = el2img(GfxElementEmpty[x][y]); + ResetGfxFrame(x, y); if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y]) @@ -12298,6 +12338,8 @@ void GameActions_RND(void) graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); } + CheckNextToConditions(x, y); + if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element))) { StartMoving(x, y); @@ -13275,6 +13317,76 @@ void ScrollScreen(struct PlayerInfo *player, int mode) ScreenMovDir = MV_NONE; } +void CheckNextToConditions(int x, int y) +{ + int element = Tile[x][y]; + + if (IS_PLAYER(x, y)) + TestIfPlayerNextToCustomElement(x, y); + + if (CAN_CHANGE_OR_HAS_ACTION(element) && + HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X)) + TestIfElementNextToCustomElement(x, y); +} + +void TestIfPlayerNextToCustomElement(int x, int y) +{ + static int xy[4][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } + }; + static int trigger_sides[4][2] = + { + // center side border side + { CH_SIDE_TOP, CH_SIDE_BOTTOM }, // check top + { CH_SIDE_LEFT, CH_SIDE_RIGHT }, // check left + { CH_SIDE_RIGHT, CH_SIDE_LEFT }, // check right + { CH_SIDE_BOTTOM, CH_SIDE_TOP } // check bottom + }; + int i; + + if (!IS_PLAYER(x, y)) + return; + + struct PlayerInfo *player = PLAYERINFO(x, y); + + if (player->is_moving) + return; + + for (i = 0; i < NUM_DIRECTIONS; i++) + { + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; + int border_side = trigger_sides[i][1]; + int border_element; + + if (!IN_LEV_FIELD(xx, yy)) + continue; + + if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy)) + continue; // center and border element not connected + + border_element = Tile[xx][yy]; + + CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER, + player->index_bit, border_side); + CheckTriggeredElementChangeByPlayer(xx, yy, border_element, + CE_PLAYER_NEXT_TO_X, + player->index_bit, border_side); + + /* use player element that is initially defined in the level playfield, + not the player element that corresponds to the runtime player number + (example: a level that contains EL_PLAYER_3 as the only player would + incorrectly give EL_PLAYER_1 for "player->element_nr") */ + + CheckElementChangeBySide(xx, yy, border_element, player->initial_element, + CE_NEXT_TO_X, border_side); + } +} + void TestIfPlayerTouchesCustomElement(int x, int y) { static int xy[4][2] = @@ -13375,6 +13487,51 @@ void TestIfPlayerTouchesCustomElement(int x, int y) } } +void TestIfElementNextToCustomElement(int x, int y) +{ + static int xy[4][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } + }; + static int trigger_sides[4][2] = + { + // center side border side + { CH_SIDE_TOP, CH_SIDE_BOTTOM }, // check top + { CH_SIDE_LEFT, CH_SIDE_RIGHT }, // check left + { CH_SIDE_RIGHT, CH_SIDE_LEFT }, // check right + { CH_SIDE_BOTTOM, CH_SIDE_TOP } // check bottom + }; + int center_element = Tile[x][y]; // should always be non-moving! + int i; + + if (IS_MOVING(x, y) || IS_BLOCKED(x, y)) + return; + + for (i = 0; i < NUM_DIRECTIONS; i++) + { + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; + int border_side = trigger_sides[i][1]; + int border_element; + + if (!IN_LEV_FIELD(xx, yy)) + continue; + + if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy)) + continue; // center and border element not connected + + border_element = Tile[xx][yy]; + + // check for change of center element (but change it only once) + if (CheckElementChangeBySide(x, y, center_element, border_element, + CE_NEXT_TO_X, border_side)) + break; + } +} + void TestIfElementTouchesCustomElement(int x, int y) { static int xy[4][2] = @@ -13970,7 +14127,11 @@ static void TestFieldAfterSnapping(int x, int y, int element, int direction, if (level.finish_dig_collect) { int dig_side = MV_DIR_OPPOSITE(direction); + int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X : + CE_PLAYER_COLLECTS_X); + CheckTriggeredElementChangeByPlayer(x, y, element, change_event, + player_index_bit, dig_side); CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, player_index_bit, dig_side); } @@ -14053,7 +14214,6 @@ static int DigField(struct PlayerInfo *player, return MP_NO_ACTION; } } - if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)) old_element = Back[jx][jy]; @@ -15823,6 +15983,7 @@ static ListNode *SaveEngineSnapshotBuffers(void) SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir)); @@ -16164,12 +16325,18 @@ static void UnmapGameButtonsAtSamePosition(int id) static void UnmapGameButtonsAtSamePosition_All(void) { - if (setup.show_snapshot_buttons) + if (setup.show_load_save_buttons) { UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE); UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2); UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD); } + else if (setup.show_undo_redo_buttons) + { + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); + } else { UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP); @@ -16182,17 +16349,13 @@ static void UnmapGameButtonsAtSamePosition_All(void) } } -static void MapGameButtonsAtSamePosition(int id) +void MapLoadSaveButtons(void) { - int i; + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE); - for (i = 0; i < NUM_GAME_BUTTONS; i++) - if (i != id && - gamebutton_info[i].pos->x == gamebutton_info[id].pos->x && - gamebutton_info[i].pos->y == gamebutton_info[id].pos->y) - MapGadget(game_gadget[i]); - - UnmapGameButtonsAtSamePosition_All(); + MapGadget(game_gadget[GAME_CTRL_ID_LOAD]); + MapGadget(game_gadget[GAME_CTRL_ID_SAVE]); } void MapUndoRedoButtons(void) @@ -16204,15 +16367,6 @@ void MapUndoRedoButtons(void) MapGadget(game_gadget[GAME_CTRL_ID_REDO]); } -void UnmapUndoRedoButtons(void) -{ - UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]); - UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]); - - MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); - MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); -} - void ModifyPauseButtons(void) { static int ids[] = @@ -16234,9 +16388,7 @@ static void MapGameButtonsExt(boolean on_tape) int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - if ((!on_tape || gamebutton_info[i].allowed_on_tape) && - i != GAME_CTRL_ID_UNDO && - i != GAME_CTRL_ID_REDO) + if (!on_tape || gamebutton_info[i].allowed_on_tape) MapGadget(game_gadget[i]); UnmapGameButtonsAtSamePosition_All(); @@ -16328,6 +16480,8 @@ static void GameUndoRedoExt(void) DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0); + ModifyPauseButtons(); + BackToFront(); } @@ -16336,8 +16490,12 @@ static void GameUndo(int steps) if (!CheckEngineSnapshotList()) return; + int tape_property_bits = tape.property_bits; + LoadEngineSnapshot_Undo(steps); + tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT; + GameUndoRedoExt(); } @@ -16346,8 +16504,12 @@ static void GameRedo(int steps) if (!CheckEngineSnapshotList()) return; + int tape_property_bits = tape.property_bits; + LoadEngineSnapshot_Redo(steps); + tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT; + GameUndoRedoExt(); }