1
0
mirror of synced 2024-11-23 22:41:01 +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;
namespace TaikoLocalServer.Models;
namespace SharedProject.Models;
public class OdaiData
public class DanData
{
[JsonPropertyName("danId")]
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.Text.Json;
using SharedProject.Models;
using Swan.Mapping;
namespace TaikoLocalServer.Common.Utils;
@ -16,7 +17,7 @@ public class DanOdaiDataManager
var filePath = Path.Combine(dataPath, Constants.DAN_DATA_FILE_NAME);
var jsonString = File.ReadAllText(filePath);
var result = JsonSerializer.Deserialize<List<OdaiData>>(jsonString);
var result = JsonSerializer.Deserialize<List<DanData>>(jsonString);
if (result is null)
{
@ -25,7 +26,7 @@ public class DanOdaiDataManager
OdaiDataList = result.ToImmutableDictionary(data => data.DanId, ToResponseOdaiData);
}
private GetDanOdaiResponse.OdaiData ToResponseOdaiData(OdaiData data)
private GetDanOdaiResponse.OdaiData ToResponseOdaiData(DanData data)
{
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,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7232;http://localhost:5090",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}

View File

@ -45,7 +45,7 @@
<ItemGroup>
<ProjectReference Include="..\SharedProject\SharedProject.csproj" />
<ProjectReference Include="..\TaikoWebUI\TaikoWebUI.csproj"/>
<ProjectReference Include="..\TaikoWebUI\TaikoWebUI.csproj" />
</ItemGroup>
</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 SharedProject.Models
@using TaikoWebUI.Pages.Dialogs
@using SharedProject.Models.Responses
@inject HttpClient Client
@inject IDialogService DialogService

View File

@ -1,10 +1,4 @@
@using TaikoWebUI.Services
@using SharedProject.Models.Responses
@using SharedProject.Models
@using SharedProject.Models.Requests
@using SharedProject.Enums
@using Throw
@inject IGameDataService GameDataService
@inject IGameDataService GameDataService
@inject HttpClient Client
@page "/Cards/{baid:int}/DaniDojo"
@ -13,10 +7,59 @@
<h1>Dani Dojo</h1>
<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">
</MudGrid>
}
else
{
<MudText>
This dan course hasn't been played
</MudText>
}
</MudTabPanel>
}
</MudTabs>
</MudContainer>
@code {
@ -24,9 +67,9 @@
[Parameter]
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()
{
@ -36,15 +79,24 @@
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
response = await Client.GetFromJsonAsync<SongBestResponse>($"api/PlayData/{Baid}");
response = await Client.GetFromJsonAsync<DanBestDataResponse>($"api/DanBestData/{Baid}");
response.ThrowIfNull();
response.SongBestData.Sort((data1, data2) =>
{
return GameDataService.GetMusicIndexBySongId(data1.SongId)
.CompareTo(GameDataService.GetMusicIndexBySongId(data2.SongId));
});
bestDataMap = response.DanBestDataList.ToDictionary(data => data.DanId);
breadcrumbs.Add(new BreadcrumbItem($"Card: {Baid}", href: null, disabled: true));
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
@using TaikoWebUI.Pages.Dialogs
@using SharedProject.Models
@inject HttpClient Client
@inject NavigationManager UriHelper
@inject IDialogService DialogService
@page "/"
@page "/"
<h1>Dashboard</h1>
<MudText Class="mt-8">
Welcome to TaikoWebUI!
</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");
}
}
</MudText>

View File

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

View File

@ -1,6 +1,4 @@
@page "/Cards/{baid:int}/Profile"
@using SharedProject.Enums
@using SharedProject.Models
@inject HttpClient Client
<MudBreadcrumbs Items="breadcrumbs" Class="px-0"></MudBreadcrumbs>
@ -113,61 +111,4 @@
</MudStack>
</MudItem>
</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
@using SharedProject.Models.Responses
@using SharedProject.Models
@using SharedProject.Models.Requests
@using SharedProject.Enums
@using Throw
@inject IGameDataService GameDataService
@inject IGameDataService GameDataService
@inject HttpClient Client
@page "/Cards/{baid:int}/TaikoMode"
@ -31,75 +25,78 @@
{
@if (difficulty is not Difficulty.None)
{
<MudTabPanel Text="@GetDifficultyTitle(difficulty)"
<MudTabPanel Text="@GetDifficultyTitle(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)"
ColumnResizeMode="ResizeMode.Container" RowsPerPage="25" Elevation="0">
<Columns>
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Song" StickyLeft="true" Class="clm-row-large">
<CellTemplate>
<MudGrid Justify="Justify.Center">
<MudItem xs="10">
<MudStack Spacing="0">
<MudText Typo="Typo.body2" Style="font-weight:bold">@GameDataService.GetMusicNameBySongId(context.Item.SongId)</MudText>
<MudText Typo="Typo.caption">@GameDataService.GetMusicArtistBySongId(context.Item.SongId)</MudText>
</MudStack>
</MudItem>
<MudItem xs="2">
<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">
<MudToggleIconButton Toggled="@context.Item.IsFavorite"
ToggledChanged="@(async () => await OnFavoriteToggled(context.Item))"
Icon="@Icons.Material.Filled.FavoriteBorder" Color="@Color.Secondary"
ToggledIcon="@Icons.Material.Filled.Favorite" ToggledColor="@Color.Secondary"
Size="Size.Small"
ToggledSize="Size.Small"/>
</MudTooltip>
</MudStack>
</MudItem>
</MudGrid>
</CellTemplate>
</Column>
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Genre" Sortable="false">
<CellTemplate>
<MudChip Style="@GetGenreStyle(GameDataService.GetMusicGenreBySongId(context.Item.SongId))"
Size="Size.Small">
@GetGenreTitle(GameDataService.GetMusicGenreBySongId(context.Item.SongId))
</MudChip>
</CellTemplate>
</Column>
<Column T="SongBestData" Field="@nameof(SongBestData.BestScore)" Title="Best Score"/>
<Column T="SongBestData" Field="@nameof(SongBestData.BestCrown)" Title="Best Crown">
<CellTemplate>
<MudTooltip Text="@(GetCrownText(context.Item.BestCrown))" Arrow="true" Placement="Placement.Top">
<img src="@($"/images/crown_{context.Item.BestCrown}.png")" alt="@(context.Item.BestCrown)" style="@ICON_STYLE"/>
</MudTooltip>
</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"/>
<MudDataGrid Items="@songBestDataMap[difficulty]"
ColumnResizeMode="ResizeMode.Container" RowsPerPage="25" Elevation="0">
<Columns>
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Song" StickyLeft="true" Class="clm-row-large">
<CellTemplate>
<MudGrid Justify="Justify.Center">
<MudItem xs="10">
<MudStack Spacing="0">
<MudText Typo="Typo.body2" Style="font-weight:bold">@GameDataService.GetMusicNameBySongId(context.Item.SongId)</MudText>
<MudText Typo="Typo.caption">@GameDataService.GetMusicArtistBySongId(context.Item.SongId)</MudText>
</MudStack>
</MudItem>
<MudItem xs="2">
<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">
<MudToggleIconButton Toggled="@context.Item.IsFavorite"
ToggledChanged="@(async () => await OnFavoriteToggled(context.Item))"
Icon="@Icons.Material.Filled.FavoriteBorder" Color="@Color.Secondary"
ToggledIcon="@Icons.Material.Filled.Favorite" ToggledColor="@Color.Secondary"
Size="Size.Small"
ToggledSize="Size.Small"/>
</MudTooltip>
</MudStack>
</MudItem>
</MudGrid>
</CellTemplate>
</Column>
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Genre"
Sortable="false" Filterable="true">
<CellTemplate>
<MudChip Style="@GetGenreStyle(GameDataService.GetMusicGenreBySongId(context.Item.SongId))"
Size="Size.Small">
@GetGenreTitle(GameDataService.GetMusicGenreBySongId(context.Item.SongId))
</MudChip>
</CellTemplate>
</Column>
<Column T="SongBestData" Field="@nameof(SongBestData.BestScore)" Title="Best Score"/>
<Column T="SongBestData" Field="@nameof(SongBestData.BestCrown)" Title="Best Crown">
<CellTemplate>
<MudTooltip Text="@(GetCrownText(context.Item.BestCrown))" Arrow="true" Placement="Placement.Top">
<img src="@($"/images/crown_{context.Item.BestCrown}.png")" alt="@(context.Item.BestCrown)" style="@IconStyle"/>
</MudTooltip>
}
</CellTemplate>
</Column>
<Column T="SongBestData" Field="@nameof(SongBestData.GoodCount)" Title="Good" Sortable="false"/>
<Column T="SongBestData" Field="@nameof(SongBestData.OkCount)" Title="Ok" Sortable="false"/>
<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 {
</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="@IconStyle"/>
</MudTooltip>
}
</CellTemplate>
</Column>
<Column T="SongBestData" Field="@nameof(SongBestData.GoodCount)" Title="Good" Sortable="false"/>
<Column T="SongBestData" Field="@nameof(SongBestData.OkCount)" Title="Ok" Sortable="false"/>
<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>
<MudItem xs="12">
<MudText Align="Align.Center" Class="my-8">
@ -108,134 +105,10 @@
</MudItem>
</MudGrid>
}
</MudTabPanel>
}
}
</MudTabs>
</MudItem>
}
</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",
_ => ""
};
}
}
</MudGrid>

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.Net.Http.Json;
using SharedProject.Enums;
using SharedProject.Models;
using TaikoWebUI.Shared.Models;
using Throw;
@ -12,6 +13,8 @@ public class GameDataService : IGameDataService
private readonly Dictionary<uint, MusicDetail> musicMap = new();
private ImmutableDictionary<uint, DanData> danMap = null!;
public GameDataService(HttpClient client)
{
this.client = client;
@ -22,10 +25,14 @@ public class GameDataService : IGameDataService
var musicInfo = await client.GetFromJsonAsync<MusicInfo>($"{dataBaseUrl}/data/musicinfo.json");
var wordList = await client.GetFromJsonAsync<WordList>($"{dataBaseUrl}/data/wordlist.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();
wordList.ThrowIfNull();
musicOrder.ThrowIfNull();
danData.ThrowIfNull();
danMap = danData.ToImmutableDictionary(data => data.DanId);
// To prevent duplicate entries in wordlist
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;
}
public DanData GetDanDataById(uint danId)
{
return danMap.GetValueOrDefault(danId, new DanData());
}
}

View File

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

View File

@ -8,4 +8,10 @@
@using Microsoft.JSInterop
@using MudBlazor
@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"
}