1
0
mirror of synced 2024-11-24 06:50:15 +01:00

Add primitive dan data page

This commit is contained in:
asesidaa 2022-09-12 00:28:12 +08:00
parent 809a2d72f6
commit a147b82ecc
23 changed files with 480 additions and 333 deletions

View File

@ -0,0 +1,7 @@
namespace SharedProject.Enums;
public enum DanBorderType
{
All = 1,
PerSong = 2
}

View File

@ -0,0 +1,13 @@
namespace SharedProject.Enums;
public enum DanConditionType
{
SoulGauge = 1,
GoodCount = 2,
OkCount = 3,
BadCount = 4,
ComboCount = 5,
DrumrollCount = 6,
Score = 7,
TotalHitCount = 8
}

View File

@ -0,0 +1,16 @@
using SharedProject.Enums;
namespace SharedProject.Models;
public class DanBestData
{
public uint DanId { get; set; }
public DanClearState ClearState { get; set; }
public uint SoulGaugeTotal { get; set; }
public uint ComboCountTotal { get; set; }
public List<DanBestStageData> DanBestStageDataList { get; set; } = new();
}

View File

@ -0,0 +1,20 @@
namespace SharedProject.Models;
public class DanBestStageData
{
public uint SongNumber { get; set; }
public uint PlayScore { get; set; }
public uint GoodCount { get; set; }
public uint OkCount { get; set; }
public uint BadCount { get; set; }
public uint DrumrollCount { get; set; }
public uint TotalHitCount { get; set; }
public uint ComboCount { get; set; }
}

View File

@ -1,8 +1,8 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace TaikoLocalServer.Models; namespace SharedProject.Models;
public class OdaiData public class DanData
{ {
[JsonPropertyName("danId")] [JsonPropertyName("danId")]
public uint DanId { get; set; } public uint DanId { get; set; }

View File

@ -0,0 +1,6 @@
namespace SharedProject.Models.Responses;
public class DanBestDataResponse
{
public List<DanBestData> DanBestDataList { get; set; } = new();
}

View File

@ -1,5 +1,6 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Text.Json; using System.Text.Json;
using SharedProject.Models;
using Swan.Mapping; using Swan.Mapping;
namespace TaikoLocalServer.Common.Utils; namespace TaikoLocalServer.Common.Utils;
@ -16,7 +17,7 @@ public class DanOdaiDataManager
var filePath = Path.Combine(dataPath, Constants.DAN_DATA_FILE_NAME); var filePath = Path.Combine(dataPath, Constants.DAN_DATA_FILE_NAME);
var jsonString = File.ReadAllText(filePath); var jsonString = File.ReadAllText(filePath);
var result = JsonSerializer.Deserialize<List<OdaiData>>(jsonString); var result = JsonSerializer.Deserialize<List<DanData>>(jsonString);
if (result is null) if (result is null)
{ {
@ -25,7 +26,7 @@ public class DanOdaiDataManager
OdaiDataList = result.ToImmutableDictionary(data => data.DanId, ToResponseOdaiData); OdaiDataList = result.ToImmutableDictionary(data => data.DanId, ToResponseOdaiData);
} }
private GetDanOdaiResponse.OdaiData ToResponseOdaiData(OdaiData data) private GetDanOdaiResponse.OdaiData ToResponseOdaiData(DanData data)
{ {
var responseOdaiData = new GetDanOdaiResponse.OdaiData var responseOdaiData = new GetDanOdaiResponse.OdaiData
{ {

View File

@ -0,0 +1,36 @@
using SharedProject.Models;
using SharedProject.Models.Responses;
using Swan.Mapping;
using TaikoLocalServer.Services.Interfaces;
namespace TaikoLocalServer.Controllers.Api;
[ApiController]
[Route("api/[controller]")]
public class DanBestDataController : BaseController<DanBestDataController>
{
private readonly IDanScoreDatumService danScoreDatumService;
public DanBestDataController(IDanScoreDatumService danScoreDatumService) {
this.danScoreDatumService = danScoreDatumService;
}
[HttpGet("{baid}")]
public async Task<IActionResult> GetDanBestData(uint baid)
{
var danScores = await danScoreDatumService.GetDanScoreDatumByBaid(baid);
var danDataList = new List<DanBestData>();
foreach (var danScore in danScores)
{
var danData = danScore.CopyPropertiesToNew<DanBestData>();
danData.DanBestStageDataList = danScore.DanStageScoreData.Select(datum => datum.CopyPropertiesToNew<DanBestStageData>()).ToList();
danDataList.Add(danData);
}
return Ok(new DanBestDataResponse
{
DanBestDataList = danDataList
});
}
}

View File

@ -14,7 +14,7 @@
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": true,
"launchUrl": "swagger", "launchUrl": "swagger",
"applicationUrl": "https://localhost:7232;http://localhost:5090", "applicationUrl": "http://localhost:5000",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }

View File

@ -45,7 +45,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\SharedProject\SharedProject.csproj" /> <ProjectReference Include="..\SharedProject\SharedProject.csproj" />
<ProjectReference Include="..\TaikoWebUI\TaikoWebUI.csproj"/> <ProjectReference Include="..\TaikoWebUI\TaikoWebUI.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,17 @@
// Global using directives
global using System.Net.Http;
global using System.Net.Http.Json;
global using Microsoft.AspNetCore.Components;
global using Microsoft.AspNetCore.Components.Forms;
global using Microsoft.AspNetCore.Components.Routing;
global using Microsoft.AspNetCore.Components.Web;
global using Microsoft.AspNetCore.Components.Web.Virtualization;
global using Microsoft.AspNetCore.Components.WebAssembly.Http;
global using MudBlazor;
global using TaikoWebUI;
global using TaikoWebUI.Shared;
global using TaikoWebUI.Services;
global using SharedProject.Models;
global using SharedProject.Models.Requests;
global using SharedProject.Enums;
global using Throw;

View File

@ -1,6 +1,5 @@
@using SharedProject.Models.Responses @using TaikoWebUI.Pages.Dialogs
@using TaikoWebUI.Pages.Dialogs @using SharedProject.Models.Responses
@using SharedProject.Models
@inject HttpClient Client @inject HttpClient Client
@inject IDialogService DialogService @inject IDialogService DialogService

View File

@ -1,10 +1,4 @@
@using TaikoWebUI.Services @inject IGameDataService GameDataService
@using SharedProject.Models.Responses
@using SharedProject.Models
@using SharedProject.Models.Requests
@using SharedProject.Enums
@using Throw
@inject IGameDataService GameDataService
@inject HttpClient Client @inject HttpClient Client
@page "/Cards/{baid:int}/DaniDojo" @page "/Cards/{baid:int}/DaniDojo"
@ -13,10 +7,59 @@
<h1>Dani Dojo</h1> <h1>Dani Dojo</h1>
<MudText Typo="Typo.caption">Card: @Baid</MudText> <MudText Typo="Typo.caption">Card: @Baid</MudText>
<MudContainer>
<MudTabs>
@for (uint i = 1; i <= 19; i++)
{
var danId = i;
var danData = GameDataService.GetDanDataById(danId);
<MudTabPanel Text="@danData.Title">
@foreach (var data in danData.OdaiBorderList)
{
<MudText>@DanRequirementToString(data)</MudText>
}
@if (bestDataMap.ContainsKey(danId))
{
var danBestData = bestDataMap[danId];
<MudText>Clear state: @danBestData.ClearState </MudText>
<MudText>Best Soul gauge: @danBestData.SoulGaugeTotal </MudText>
<MudText>Best Total Combo: @danBestData.ComboCountTotal </MudText>
<MudGrid>
@foreach (var bestStage in danBestData.DanBestStageDataList)
{
var songNumber = bestStage.SongNumber;
var danDataOdaiSong = danData.OdaiSongList[(int)songNumber];
<MudItem>
<MudText>Song Number: @songNumber</MudText>
<MudText>
Song Name:
@GameDataService.GetMusicNameBySongId(danDataOdaiSong.SongNo)
</MudText>
<MudText>Song Difficulty: @((Difficulty)danDataOdaiSong.Level)</MudText>
<MudText>
Song play detail <br/>
Good : @bestStage.GoodCount <br/>
Ok : @bestStage.OkCount <br/>
Bad : @bestStage.BadCount <br/>
Combo : @bestStage.ComboCount<br/>
Drumroll : @bestStage.DrumrollCount <br/>
Total hit : @bestStage.TotalHitCount
</MudText>
</MudItem>
}
</MudGrid>
<MudGrid Class="my-8"> }
else
</MudGrid> {
<MudText>
This dan course hasn't been played
</MudText>
}
</MudTabPanel>
}
</MudTabs>
</MudContainer>
@code { @code {
@ -24,9 +67,9 @@
[Parameter] [Parameter]
public int Baid { get; set; } public int Baid { get; set; }
private SongBestResponse? response; private DanBestDataResponse? response;
private const string ICON_STYLE = "width:25px; height:25px;"; private Dictionary<uint, DanBestData> bestDataMap = new();
private readonly List<BreadcrumbItem> breadcrumbs = new() private readonly List<BreadcrumbItem> breadcrumbs = new()
{ {
@ -36,15 +79,24 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await base.OnInitializedAsync(); await base.OnInitializedAsync();
response = await Client.GetFromJsonAsync<SongBestResponse>($"api/PlayData/{Baid}"); response = await Client.GetFromJsonAsync<DanBestDataResponse>($"api/DanBestData/{Baid}");
response.ThrowIfNull(); response.ThrowIfNull();
response.SongBestData.Sort((data1, data2) => bestDataMap = response.DanBestDataList.ToDictionary(data => data.DanId);
{
return GameDataService.GetMusicIndexBySongId(data1.SongId)
.CompareTo(GameDataService.GetMusicIndexBySongId(data2.SongId));
});
breadcrumbs.Add(new BreadcrumbItem($"Card: {Baid}", href: null, disabled: true)); breadcrumbs.Add(new BreadcrumbItem($"Card: {Baid}", href: null, disabled: true));
breadcrumbs.Add(new BreadcrumbItem("Dani Dojo", href: $"/Cards/{Baid}/DaniDojo", disabled: false)); breadcrumbs.Add(new BreadcrumbItem("Dani Dojo", href: $"/Cards/{Baid}/DaniDojo", disabled: false));
} }
private static string DanRequirementToString(DanData.OdaiBorder data)
{
var danConditionType = (DanConditionType)data.OdaiType;
return (DanBorderType)data.BorderType switch
{
DanBorderType.All => $"{danConditionType}, Pass: {data.RedBorderTotal}, Gold: {data.GoldBorderTotal} ",
DanBorderType.PerSong => $"{danConditionType}, " +
$"Pass 1: {data.RedBorder1}, Pass 2: {data.RedBorder2}, Pass 3: {data.RedBorder3}" +
$"Gold 1: {data.GoldBorder1}, Gold 2: {data.GoldBorder1}, Pass 3: {data.GoldBorder1}",
_ => throw new ArgumentOutOfRangeException()
};
}
} }

View File

@ -1,50 +1,7 @@
@using SharedProject.Models.Responses @page "/"
@using TaikoWebUI.Pages.Dialogs
@using SharedProject.Models
@inject HttpClient Client
@inject NavigationManager UriHelper
@inject IDialogService DialogService
@page "/"
<h1>Dashboard</h1> <h1>Dashboard</h1>
<MudText Class="mt-8"> <MudText Class="mt-8">
Welcome to TaikoWebUI! Welcome to TaikoWebUI!
</MudText> </MudText>
@code {
private DashboardResponse? response;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
response = await Client.GetFromJsonAsync<DashboardResponse>("api/Dashboard");
}
private void NavigateToProfile(uint baid)
{
UriHelper.NavigateTo($"/Card/{baid}");
}
private async Task DeleteCard(User user)
{
var parameters = new DialogParameters
{
["user"] = user
};
var dialog = DialogService.Show<CardDeleteConfirmDialog>("Delete Card", parameters);
var result = await dialog.Result;
if (result.Cancelled)
{
return;
}
response = await Client.GetFromJsonAsync<DashboardResponse>("api/Dashboard");
}
}

View File

@ -1,5 +1,4 @@
@using SharedProject.Models @inject HttpClient Client
@inject HttpClient Client
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
<MudDialog> <MudDialog>

View File

@ -1,6 +1,4 @@
@page "/Cards/{baid:int}/Profile" @page "/Cards/{baid:int}/Profile"
@using SharedProject.Enums
@using SharedProject.Models
@inject HttpClient Client @inject HttpClient Client
<MudBreadcrumbs Items="breadcrumbs" Class="px-0"></MudBreadcrumbs> <MudBreadcrumbs Items="breadcrumbs" Class="px-0"></MudBreadcrumbs>
@ -114,60 +112,3 @@
</MudItem> </MudItem>
</MudGrid> </MudGrid>
} }
@code {
[Parameter]
public int Baid { get; set; }
private UserSetting? response;
private bool isSavingOptions;
private readonly string[] speedStrings =
{
"1.0", "1.1", "1.2", "1.3", "1.4",
"1.5", "1.6", "1.7", "1.8", "1.9",
"2.0", "2.5", "3.0", "3.5", "4.0"
};
private readonly string[] notePositionStrings = { "-5", "-4", "-3", "-2", "-1", "0", "+1", "+2", "+3", "+4", "+5" };
private readonly string[] toneStrings =
{
"Taiko", "Festival", "Dogs & Cats", "Deluxe",
"Drumset", "Tambourine", "Don Wada", "Clapping",
"Conga", "8-Bit", "Heave-ho", "Mecha",
"Bujain", "Rap", "Hosogai", "Akemi",
"Synth Drum", "Shuriken", "Bubble Pop", "Electric Guitar"
};
private readonly string[] titlePlateStrings =
{
"Wood", "Rainbow", "Gold", "Purple",
"AI 1", "AI 2", "AI 3", "AI 4"
};
private List<BreadcrumbItem> breadcrumbs = new()
{
new BreadcrumbItem("Cards", href: "/Cards"),
};
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
isSavingOptions = false;
response = await Client.GetFromJsonAsync<UserSetting>($"api/UserSettings/{Baid}");
breadcrumbs.Add(new BreadcrumbItem($"Card: {Baid}", href: null, disabled: true));
breadcrumbs.Add(new BreadcrumbItem("Profile", href: $"/Cards/{Baid}/Profile", disabled: false));
}
private async Task SaveOptions()
{
isSavingOptions = true;
await Client.PostAsJsonAsync($"api/UserSettings/{Baid}", response);
isSavingOptions = false;
}
}

View File

@ -0,0 +1,58 @@
namespace TaikoWebUI.Pages;
public partial class Profile
{
[Parameter]
public int Baid { get; set; }
private UserSetting? response;
private bool isSavingOptions;
private readonly string[] speedStrings =
{
"1.0", "1.1", "1.2", "1.3", "1.4",
"1.5", "1.6", "1.7", "1.8", "1.9",
"2.0", "2.5", "3.0", "3.5", "4.0"
};
private readonly string[] notePositionStrings = { "-5", "-4", "-3", "-2", "-1", "0", "+1", "+2", "+3", "+4", "+5" };
private readonly string[] toneStrings =
{
"Taiko", "Festival", "Dogs & Cats", "Deluxe",
"Drumset", "Tambourine", "Don Wada", "Clapping",
"Conga", "8-Bit", "Heave-ho", "Mecha",
"Bujain", "Rap", "Hosogai", "Akemi",
"Synth Drum", "Shuriken", "Bubble Pop", "Electric Guitar"
};
private readonly string[] titlePlateStrings =
{
"Wood", "Rainbow", "Gold", "Purple",
"AI 1", "AI 2", "AI 3", "AI 4"
};
private List<BreadcrumbItem> breadcrumbs = new()
{
new BreadcrumbItem("Cards", href: "/Cards"),
};
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
isSavingOptions = false;
response = await Client.GetFromJsonAsync<UserSetting>($"api/UserSettings/{Baid}");
breadcrumbs.Add(new BreadcrumbItem($"Card: {Baid}", href: null, disabled: true));
breadcrumbs.Add(new BreadcrumbItem("Profile", href: $"/Cards/{Baid}/Profile", disabled: false));
}
private async Task SaveOptions()
{
isSavingOptions = true;
await Client.PostAsJsonAsync($"api/UserSettings/{Baid}", response);
isSavingOptions = false;
}
}

View File

@ -1,10 +1,4 @@
@using TaikoWebUI.Services @inject IGameDataService GameDataService
@using SharedProject.Models.Responses
@using SharedProject.Models
@using SharedProject.Models.Requests
@using SharedProject.Enums
@using Throw
@inject IGameDataService GameDataService
@inject HttpClient Client @inject HttpClient Client
@page "/Cards/{baid:int}/TaikoMode" @page "/Cards/{baid:int}/TaikoMode"
@ -33,73 +27,76 @@
{ {
<MudTabPanel Text="@GetDifficultyTitle(difficulty)" <MudTabPanel Text="@GetDifficultyTitle(difficulty)"
Icon="@GetDifficultyIcon(difficulty)"> Icon="@GetDifficultyIcon(difficulty)">
@if (@response.SongBestData.Where(data => data.Difficulty == difficulty).Count() > 0) @if (songBestDataMap.ContainsKey(difficulty))
{ {
<MudDataGrid Items="@response.SongBestData.Where(data => data.Difficulty == difficulty)" <MudDataGrid Items="@songBestDataMap[difficulty]"
ColumnResizeMode="ResizeMode.Container" RowsPerPage="25" Elevation="0"> ColumnResizeMode="ResizeMode.Container" RowsPerPage="25" Elevation="0">
<Columns> <Columns>
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Song" StickyLeft="true" Class="clm-row-large"> <Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Song" StickyLeft="true" Class="clm-row-large">
<CellTemplate> <CellTemplate>
<MudGrid Justify="Justify.Center"> <MudGrid Justify="Justify.Center">
<MudItem xs="10"> <MudItem xs="10">
<MudStack Spacing="0"> <MudStack Spacing="0">
<MudText Typo="Typo.body2" Style="font-weight:bold">@GameDataService.GetMusicNameBySongId(context.Item.SongId)</MudText> <MudText Typo="Typo.body2" Style="font-weight:bold">@GameDataService.GetMusicNameBySongId(context.Item.SongId)</MudText>
<MudText Typo="Typo.caption">@GameDataService.GetMusicArtistBySongId(context.Item.SongId)</MudText> <MudText Typo="Typo.caption">@GameDataService.GetMusicArtistBySongId(context.Item.SongId)</MudText>
</MudStack> </MudStack>
</MudItem> </MudItem>
<MudItem xs="2"> <MudItem xs="2">
<MudStack Justify="Justify.Center" AlignItems="AlignItems.End" Style="height:100%"> <MudStack Justify="Justify.Center" AlignItems="AlignItems.End" Style="height:100%">
<MudTooltip Text="@(context.Item.IsFavorite ? "Remove from favorites" : "Add to favorites")" Arrow="true" Placement="Placement.Top"> <MudTooltip Text="@(context.Item.IsFavorite ? "Remove from favorites" : "Add to favorites")" Arrow="true" Placement="Placement.Top">
<MudToggleIconButton Toggled="@context.Item.IsFavorite" <MudToggleIconButton Toggled="@context.Item.IsFavorite"
ToggledChanged="@(async () => await OnFavoriteToggled(context.Item))" ToggledChanged="@(async () => await OnFavoriteToggled(context.Item))"
Icon="@Icons.Material.Filled.FavoriteBorder" Color="@Color.Secondary" Icon="@Icons.Material.Filled.FavoriteBorder" Color="@Color.Secondary"
ToggledIcon="@Icons.Material.Filled.Favorite" ToggledColor="@Color.Secondary" ToggledIcon="@Icons.Material.Filled.Favorite" ToggledColor="@Color.Secondary"
Size="Size.Small" Size="Size.Small"
ToggledSize="Size.Small"/> ToggledSize="Size.Small"/>
</MudTooltip> </MudTooltip>
</MudStack> </MudStack>
</MudItem> </MudItem>
</MudGrid> </MudGrid>
</CellTemplate> </CellTemplate>
</Column> </Column>
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Genre" Sortable="false"> <Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Genre"
<CellTemplate> Sortable="false" Filterable="true">
<MudChip Style="@GetGenreStyle(GameDataService.GetMusicGenreBySongId(context.Item.SongId))" <CellTemplate>
Size="Size.Small"> <MudChip Style="@GetGenreStyle(GameDataService.GetMusicGenreBySongId(context.Item.SongId))"
@GetGenreTitle(GameDataService.GetMusicGenreBySongId(context.Item.SongId)) Size="Size.Small">
</MudChip> @GetGenreTitle(GameDataService.GetMusicGenreBySongId(context.Item.SongId))
</CellTemplate> </MudChip>
</Column> </CellTemplate>
<Column T="SongBestData" Field="@nameof(SongBestData.BestScore)" Title="Best Score"/> </Column>
<Column T="SongBestData" Field="@nameof(SongBestData.BestCrown)" Title="Best Crown"> <Column T="SongBestData" Field="@nameof(SongBestData.BestScore)" Title="Best Score"/>
<CellTemplate> <Column T="SongBestData" Field="@nameof(SongBestData.BestCrown)" Title="Best Crown">
<MudTooltip Text="@(GetCrownText(context.Item.BestCrown))" Arrow="true" Placement="Placement.Top"> <CellTemplate>
<img src="@($"/images/crown_{context.Item.BestCrown}.png")" alt="@(context.Item.BestCrown)" style="@ICON_STYLE"/> <MudTooltip Text="@(GetCrownText(context.Item.BestCrown))" Arrow="true" Placement="Placement.Top">
</MudTooltip> <img src="@($"/images/crown_{context.Item.BestCrown}.png")" alt="@(context.Item.BestCrown)" style="@IconStyle"/>
</CellTemplate>
</Column>
<Column T="SongBestData" Field="@nameof(SongBestData.BestScoreRank)" Title="Best Rank" Sortable="false">
<CellTemplate>
@if (context.Item.BestScoreRank is not ScoreRank.None)
{
<MudTooltip Text="@(GetRankText(context.Item.BestScoreRank))" Arrow="true" Placement="Placement.Top">
<img src="@($"/images/rank_{context.Item.BestScoreRank}.png")" alt="@(context.Item.BestScoreRank)" style="@ICON_STYLE"/>
</MudTooltip> </MudTooltip>
} </CellTemplate>
</CellTemplate> </Column>
</Column> <Column T="SongBestData" Field="@nameof(SongBestData.BestScoreRank)" Title="Best Rank" Sortable="false">
<Column T="SongBestData" Field="@nameof(SongBestData.GoodCount)" Title="Good" Sortable="false"/> <CellTemplate>
<Column T="SongBestData" Field="@nameof(SongBestData.OkCount)" Title="Ok" Sortable="false"/> @if (context.Item.BestScoreRank is not ScoreRank.None)
<Column T="SongBestData" Field="@nameof(SongBestData.MissCount)" Title="Bad" Sortable="false"/> {
<Column T="SongBestData" Field="@nameof(SongBestData.DrumrollCount)" Title="Drum Roll" Sortable="false"/> <MudTooltip Text="@(GetRankText(context.Item.BestScoreRank))" Arrow="true" Placement="Placement.Top">
<Column T="SongBestData" Field="@nameof(SongBestData.ComboCount)" Title="Max Combo" Sortable="false"/> <img src="@($"/images/rank_{context.Item.BestScoreRank}.png")" alt="@(context.Item.BestScoreRank)" style="@IconStyle"/>
<Column T="SongBestData" Field="@nameof(SongBestData.LastPlayTime)" Title="Last Played"/> </MudTooltip>
</Columns> }
<PagerContent> </CellTemplate>
<MudDataGridPager T="SongBestData"/> </Column>
</PagerContent> <Column T="SongBestData" Field="@nameof(SongBestData.GoodCount)" Title="Good" Sortable="false"/>
</MudDataGrid> <Column T="SongBestData" Field="@nameof(SongBestData.OkCount)" Title="Ok" Sortable="false"/>
} else { <Column T="SongBestData" Field="@nameof(SongBestData.MissCount)" Title="Bad" Sortable="false"/>
<Column T="SongBestData" Field="@nameof(SongBestData.DrumrollCount)" Title="Drum Roll" Sortable="false"/>
<Column T="SongBestData" Field="@nameof(SongBestData.ComboCount)" Title="Max Combo" Sortable="false"/>
<Column T="SongBestData" Field="@nameof(SongBestData.LastPlayTime)" Title="Last Played"/>
</Columns>
<PagerContent>
<MudDataGridPager T="SongBestData"/>
</PagerContent>
</MudDataGrid>
}
else
{
<MudGrid> <MudGrid>
<MudItem xs="12"> <MudItem xs="12">
<MudText Align="Align.Center" Class="my-8"> <MudText Align="Align.Center" Class="my-8">
@ -108,7 +105,6 @@
</MudItem> </MudItem>
</MudGrid> </MudGrid>
} }
</MudTabPanel> </MudTabPanel>
} }
} }
@ -116,126 +112,3 @@
</MudItem> </MudItem>
} }
</MudGrid> </MudGrid>
@code {
[Parameter]
public int Baid { get; set; }
private SongBestResponse? response;
private const string ICON_STYLE = "width:25px; height:25px;";
private readonly List<BreadcrumbItem> breadcrumbs = new()
{
new BreadcrumbItem("Cards", href: "/Cards"),
};
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
response = await Client.GetFromJsonAsync<SongBestResponse>($"api/PlayData/{Baid}");
response.ThrowIfNull();
response.SongBestData.Sort((data1, data2) =>
{
return GameDataService.GetMusicIndexBySongId(data1.SongId)
.CompareTo(GameDataService.GetMusicIndexBySongId(data2.SongId));
});
breadcrumbs.Add(new BreadcrumbItem($"Card: {Baid}", href: null, disabled: true));
breadcrumbs.Add(new BreadcrumbItem("Taiko Mode", href: $"/Cards/{Baid}/TaikoMode", disabled: false));
}
private async Task OnFavoriteToggled(SongBestData data)
{
var request = new SetFavoriteRequest
{
Baid = (uint)Baid,
IsFavorite = !data.IsFavorite,
SongId = data.SongId
};
var result = await Client.PostAsJsonAsync("api/FavoriteSongs", request);
if (result.IsSuccessStatusCode)
{
data.IsFavorite = !data.IsFavorite;
}
}
private static string GetCrownText(CrownType crown)
{
return crown switch
{
CrownType.None => "Fail",
CrownType.Clear => "Clear",
CrownType.Gold => "Full Combo",
CrownType.Dondaful => "Donderful Combo",
_ => ""
};
}
private static string GetRankText(ScoreRank rank)
{
return rank switch
{
ScoreRank.White => "Stylish",
ScoreRank.Bronze => "Stylish",
ScoreRank.Silver => "Stylish",
ScoreRank.Gold => "Graceful",
ScoreRank.Sakura => "Graceful",
ScoreRank.Purple => "Graceful",
ScoreRank.Dondaful => "Top Class",
_ => ""
};
}
private static string GetDifficultyTitle(Difficulty difficulty)
{
return difficulty switch
{
Difficulty.Easy => "Easy",
Difficulty.Normal => "Normal",
Difficulty.Hard => "Hard",
Difficulty.Oni => "Oni",
Difficulty.UraOni => "Ura Oni",
_ => ""
};
}
private static string GetDifficultyIcon(Difficulty difficulty)
{
return $"<image href='/images/difficulty_{difficulty}.png' alt='{difficulty}' width='24' height='24'/>";
}
private static string GetGenreTitle(SongGenre genre)
{
return genre switch
{
SongGenre.Pop => "Pop",
SongGenre.Anime => "Anime",
SongGenre.Kids => "Kids",
SongGenre.Vocaloid => "Vocaloid",
SongGenre.GameMusic => "Game Music",
SongGenre.NamcoOriginal => "NAMCO Original",
SongGenre.Variety => "Variety",
SongGenre.Classical => "Classical",
_ => ""
};
}
private static string GetGenreStyle(SongGenre genre)
{
return genre switch
{
SongGenre.Pop => "background: #42c0d2; color: #fff",
SongGenre.Anime => "background: #ff90d3; color: #fff",
SongGenre.Kids => "background: #fec000; color: #fff",
SongGenre.Vocaloid => "background: #ddd",
SongGenre.GameMusic => "background: #cc8aea; color: #fff",
SongGenre.NamcoOriginal => "background: #ff7027; color: #fff",
SongGenre.Variety => "background: #1dc83b; color: #fff",
SongGenre.Classical => "background: #bfa356",
_ => ""
};
}
}

View File

@ -0,0 +1,132 @@
using SharedProject.Models.Responses;
namespace TaikoWebUI.Pages;
public partial class TaikoMode
{
[Parameter]
public int Baid { get; set; }
private const string IconStyle = "width:25px; height:25px;";
private SongBestResponse? response;
private Dictionary<Difficulty, List<SongBestData>> songBestDataMap = new();
private readonly List<BreadcrumbItem> breadcrumbs = new()
{
new BreadcrumbItem("Cards", href: "/Cards"),
};
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
response = await Client.GetFromJsonAsync<SongBestResponse>($"api/PlayData/{Baid}");
response.ThrowIfNull();
songBestDataMap = response.SongBestData.GroupBy(data => data.Difficulty)
.ToDictionary(data => data.Key,
data => data.ToList());
foreach (var songBestDataList in songBestDataMap.Values)
{
songBestDataList.Sort((data1, data2) => GameDataService.GetMusicIndexBySongId(data1.SongId)
.CompareTo(GameDataService.GetMusicIndexBySongId(data2.SongId)));
}
breadcrumbs.Add(new BreadcrumbItem($"Card: {Baid}", href: null, disabled: true));
breadcrumbs.Add(new BreadcrumbItem("Taiko Mode", href: $"/Cards/{Baid}/TaikoMode", disabled: false));
}
private async Task OnFavoriteToggled(SongBestData data)
{
var request = new SetFavoriteRequest
{
Baid = (uint)Baid,
IsFavorite = !data.IsFavorite,
SongId = data.SongId
};
var result = await Client.PostAsJsonAsync("api/FavoriteSongs", request);
if (result.IsSuccessStatusCode)
{
data.IsFavorite = !data.IsFavorite;
}
}
private static string GetCrownText(CrownType crown)
{
return crown switch
{
CrownType.None => "Fail",
CrownType.Clear => "Clear",
CrownType.Gold => "Full Combo",
CrownType.Dondaful => "Donderful Combo",
_ => ""
};
}
private static string GetRankText(ScoreRank rank)
{
return rank switch
{
ScoreRank.White => "Stylish",
ScoreRank.Bronze => "Stylish",
ScoreRank.Silver => "Stylish",
ScoreRank.Gold => "Graceful",
ScoreRank.Sakura => "Graceful",
ScoreRank.Purple => "Graceful",
ScoreRank.Dondaful => "Top Class",
_ => ""
};
}
private static string GetDifficultyTitle(Difficulty difficulty)
{
return difficulty switch
{
Difficulty.Easy => "Easy",
Difficulty.Normal => "Normal",
Difficulty.Hard => "Hard",
Difficulty.Oni => "Oni",
Difficulty.UraOni => "Ura Oni",
_ => ""
};
}
private static string GetDifficultyIcon(Difficulty difficulty)
{
return $"<image href='/images/difficulty_{difficulty}.png' alt='{difficulty}' width='24' height='24'/>";
}
private static string GetGenreTitle(SongGenre genre)
{
return genre switch
{
SongGenre.Pop => "Pop",
SongGenre.Anime => "Anime",
SongGenre.Kids => "Kids",
SongGenre.Vocaloid => "Vocaloid",
SongGenre.GameMusic => "Game Music",
SongGenre.NamcoOriginal => "NAMCO Original",
SongGenre.Variety => "Variety",
SongGenre.Classical => "Classical",
_ => ""
};
}
private static string GetGenreStyle(SongGenre genre)
{
return genre switch
{
SongGenre.Pop => "background: #42c0d2; color: #fff",
SongGenre.Anime => "background: #ff90d3; color: #fff",
SongGenre.Kids => "background: #fec000; color: #fff",
SongGenre.Vocaloid => "background: #ddd",
SongGenre.GameMusic => "background: #cc8aea; color: #fff",
SongGenre.NamcoOriginal => "background: #ff7027; color: #fff",
SongGenre.Variety => "background: #1dc83b; color: #fff",
SongGenre.Classical => "background: #bfa356",
_ => ""
};
}
}

View File

@ -1,6 +1,7 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Net.Http.Json; using System.Net.Http.Json;
using SharedProject.Enums; using SharedProject.Enums;
using SharedProject.Models;
using TaikoWebUI.Shared.Models; using TaikoWebUI.Shared.Models;
using Throw; using Throw;
@ -12,6 +13,8 @@ public class GameDataService : IGameDataService
private readonly Dictionary<uint, MusicDetail> musicMap = new(); private readonly Dictionary<uint, MusicDetail> musicMap = new();
private ImmutableDictionary<uint, DanData> danMap = null!;
public GameDataService(HttpClient client) public GameDataService(HttpClient client)
{ {
this.client = client; this.client = client;
@ -22,10 +25,14 @@ public class GameDataService : IGameDataService
var musicInfo = await client.GetFromJsonAsync<MusicInfo>($"{dataBaseUrl}/data/musicinfo.json"); var musicInfo = await client.GetFromJsonAsync<MusicInfo>($"{dataBaseUrl}/data/musicinfo.json");
var wordList = await client.GetFromJsonAsync<WordList>($"{dataBaseUrl}/data/wordlist.json"); var wordList = await client.GetFromJsonAsync<WordList>($"{dataBaseUrl}/data/wordlist.json");
var musicOrder = await client.GetFromJsonAsync<MusicOrder>($"{dataBaseUrl}/data/music_order.json"); var musicOrder = await client.GetFromJsonAsync<MusicOrder>($"{dataBaseUrl}/data/music_order.json");
var danData = await client.GetFromJsonAsync<List<DanData>>($"{dataBaseUrl}/data/dan_data.json");
musicInfo.ThrowIfNull(); musicInfo.ThrowIfNull();
wordList.ThrowIfNull(); wordList.ThrowIfNull();
musicOrder.ThrowIfNull(); musicOrder.ThrowIfNull();
danData.ThrowIfNull();
danMap = danData.ToImmutableDictionary(data => data.DanId);
// To prevent duplicate entries in wordlist // To prevent duplicate entries in wordlist
var dict = wordList.WordListEntries.GroupBy(entry => entry.Key) var dict = wordList.WordListEntries.GroupBy(entry => entry.Key)
@ -79,5 +86,9 @@ public class GameDataService : IGameDataService
{ {
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.Index : int.MaxValue; return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.Index : int.MaxValue;
} }
public DanData GetDanDataById(uint danId)
{
return danMap.GetValueOrDefault(danId, new DanData());
}
} }

View File

@ -1,4 +1,5 @@
using SharedProject.Enums; using SharedProject.Enums;
using SharedProject.Models;
using TaikoWebUI.Shared.Models; using TaikoWebUI.Shared.Models;
namespace TaikoWebUI.Services; namespace TaikoWebUI.Services;
@ -14,4 +15,6 @@ public interface IGameDataService
public SongGenre GetMusicGenreBySongId(uint songId); public SongGenre GetMusicGenreBySongId(uint songId);
public int GetMusicIndexBySongId(uint songId); public int GetMusicIndexBySongId(uint songId);
public DanData GetDanDataById(uint danId);
} }

View File

@ -9,3 +9,9 @@
@using MudBlazor @using MudBlazor
@using TaikoWebUI @using TaikoWebUI
@using TaikoWebUI.Shared @using TaikoWebUI.Shared
@using TaikoWebUI.Services
@using SharedProject.Models.Responses
@using SharedProject.Models
@using SharedProject.Models.Requests
@using SharedProject.Enums
@using Throw

View File

@ -1,3 +1,3 @@
{ {
"DataBaseUrl": "https://localhost:44398" "DataBaseUrl": "http://localhost:5000"
} }