1
0
mirror of synced 2024-11-28 01:10:53 +01:00

Additional unlockable methods

This commit is contained in:
0auBSQ 2024-06-17 02:00:14 +09:00
parent 7cc431ffbb
commit 598bbb0e06
9 changed files with 468 additions and 107 deletions

View File

@ -3,10 +3,10 @@
* ch : "Coins here", coin requirement, payable within the heya menu, 1 value : [Coin price] * ch : "Coins here", coin requirement, payable within the heya menu, 1 value : [Coin price]
* cs : "Coins shop", coin requirement, payable only within the Medal shop selection screen * cs : "Coins shop", coin requirement, payable only within the Medal shop selection screen
* cm : "Coins menu", coin requirement, payable only within the song select screen (used only for songs) * cm : "Coins menu", coin requirement, payable only within the song select screen (used only for songs)
* dp : "Difficulty pass", count of difficulties pass, unlock check during the results screen, condition 3 values : [Difficulty int (0~4), Clear status (0~2), Number of performances], input 1 value [Plays fitting the condition] * dp : "Difficulty pass", count of difficulties pass, unlock check during the results screen, condition 3 values : [Difficulty int (0~4), Clear status (0~4), Number of performances], input 1 value [Plays fitting the condition]
* lp : "Level pass", count of level pass, unlock check during the results screen, condition 3 values : [Star rating, Clear status (0~2), Number of performances], input 1 value [Plays fitting the condition] * lp : "Level pass", count of level pass, unlock check during the results screen, condition 3 values : [Star rating, Clear status (0~4), Number of performances], input 1 value [Plays fitting the condition]
* sp : "Song performance", count of a specific song pass, unlock check during the results screen, condition 2 x n values for n songs : [Difficulty int (0~4, if -1 : Any), Clear status (0~2), ...], input 1 value [Count of fullfiled songs], n references for n songs (Song ids) * sp : "Song performance", count of a specific song pass, unlock check during the results screen, condition 2 x n values for n songs : [Difficulty int (0~4, if -1 : Any), Clear status (0~2), ...], input 1 value [Count of fullfiled songs], n references for n songs (Song ids)
* sg : "Song genre (performance)", count of any song pass within a specific genre folder, unlock check during the results screen, condition 3 x n values for n songs : [Song count, Difficulty int (0~4, if -1 : Any), Clear status (0~2), ...], input 1 value [Count of fullfiled genres], n references for n genres (Genre names) * sg : "Song genre (performance)", count of any song pass within a specific genre folder, unlock check during the results screen, condition 2 x n values for n songs : [Song count, Clear status (0~4), ...], input 1 value [Count of fullfiled genres], n references for n genres (Genre names)
== UnlockType == == UnlockType ==

View File

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace TJAPlayer3
{
internal class BestPlayRecords
{
public enum EClearStatus
{
NONE = 0,
ASSISTED_CLEAR = 1,
CLEAR = 2,
FC = 3,
PERFECT = 4,
TOTAL = 5
}
public class CBestPlayRecord
{
public string ChartUniqueId = "none";
public string ChartGenre = "none";
public string Charter = "none";
public string Artist = "none";
public Int64 PlayMods = 0;
public Int64 ChartDifficulty = 3;
public Int64 ChartLevel = 8;
public Int64 ClearStatus = -1;
public Int64 ScoreRank = -1;
public Int64 HighScore = 0;
public Int64 TowerBestFloor = 0;
public List<int> DanExam1 = new List<int> { -1 };
public List<int> DanExam2 = new List<int> { -1 };
public List<int> DanExam3 = new List<int> { -1 };
public List<int> DanExam4 = new List<int> { -1 };
public List<int> DanExam5 = new List<int> { -1 };
public List<int> DanExam6 = new List<int> { -1 };
public List<int> DanExam7 = new List<int> { -1 };
public Int64 PlayCount = 1;
public Int64 HighScoreGoodCount = 0;
public Int64 HighScoreOkCount = 0;
public Int64 HighScoreBadCount = 0;
public Int64 HighScoreMaxCombo = 0;
public Int64 HighScoreRollCount = 0;
public Int64 HighScoreADLibCount = 0;
public Int64 HighScoreBoomCount = 0;
}
public class CBestPlayStats
{
public int DistinctPlays = 0;
public int DistinctClears = 0;
public int DistinctFCs = 0;
public int DistinctPerfects = 0;
public int SongDistinctPlays = 0;
public int SongDistinctClears = 0;
public int SongDistinctFCs = 0;
public int SongDistinctPerfects = 0;
public int[][] ClearStatuses = new int[(int)Difficulty.Total][];
public int[][] ScoreRanks = new int[(int)Difficulty.Total][];
public Dictionary<int, int> LevelPlays = new Dictionary<int, int>();
public Dictionary<int, int> LevelClears = new Dictionary<int, int>();
public Dictionary<int, int> LevelFCs = new Dictionary<int, int>();
public Dictionary<int, int> LevelPerfects = new Dictionary<int, int>();
public Dictionary<string, int> GenrePlays = new Dictionary<string, int>();
public Dictionary<string, int> GenreClears = new Dictionary<string, int>();
public Dictionary<string, int> GenreFCs = new Dictionary<string, int>();
public Dictionary<string, int> GenrePerfects = new Dictionary<string, int>();
public Dictionary<string, int> SongGenrePlays = new Dictionary<string, int>();
public Dictionary<string, int> SongGenreClears = new Dictionary<string, int>();
public Dictionary<string, int> SongGenreFCs = new Dictionary<string, int>();
public Dictionary<string, int> SongGenrePerfects = new Dictionary<string, int>();
public Dictionary<string, int> CharterPlays = new Dictionary<string, int>();
public Dictionary<string, int> CharterClears = new Dictionary<string, int>();
public Dictionary<string, int> CharterFCs = new Dictionary<string, int>();
public Dictionary<string, int> CharterPerfects = new Dictionary<string, int>();
public CBestPlayStats()
{
// 0 : Not clear, 1 : Assisted clear, 2 : Clear, 3 : FC, 4 : Perfect
ClearStatuses[0] = new int[(int)EClearStatus.TOTAL] { 0, 0, 0, 0, 0 };
ClearStatuses[1] = new int[(int)EClearStatus.TOTAL] { 0, 0, 0, 0, 0 };
ClearStatuses[2] = new int[(int)EClearStatus.TOTAL] { 0, 0, 0, 0, 0 };
ClearStatuses[3] = new int[(int)EClearStatus.TOTAL] { 0, 0, 0, 0, 0 };
ClearStatuses[4] = new int[(int)EClearStatus.TOTAL] { 0, 0, 0, 0, 0 };
// 0 : None, 1 : E, 2 : D, 3 : C, 4 : B, 5 : A, 6 : S, 7 : Omega
ScoreRanks[0] = new int[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
ScoreRanks[1] = new int[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
ScoreRanks[2] = new int[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
ScoreRanks[3] = new int[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
ScoreRanks[4] = new int[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
}
}
static private void InitOrAddDict<T>(Dictionary<T, int> dict, T entry) where T : notnull
{
if (!dict.ContainsKey(entry))
dict[entry] = 0;
dict[entry]++;
}
static public CBestPlayStats tGenerateBestPlayStats(
Dictionary<string, CBestPlayRecord>.ValueCollection uniqueChartBestPlays,
Dictionary<string, CBestPlayRecord>.ValueCollection uniqueSongBestPlays
)
{
CBestPlayStats stats = new CBestPlayStats();
// Individual charts
foreach (CBestPlayRecord record in uniqueChartBestPlays)
{
Int64 roundedDifficulty = Math.Max((int)Difficulty.Easy, Math.Min((int)Difficulty.Total - 1, record.ChartDifficulty));
if (roundedDifficulty <= (int)Difficulty.Edit)
{
string[] ChartersArr = record.Charter.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
Int64 roundedScoreRank = Math.Max(0, Math.Min(7, record.ScoreRank + 1));
Int64 roundedClearStatus = Math.Max((int)EClearStatus.NONE, Math.Min((int)EClearStatus.PERFECT, record.ClearStatus + 1));
stats.ScoreRanks[roundedDifficulty][roundedScoreRank]++;
stats.ClearStatuses[roundedDifficulty][roundedClearStatus]++;
foreach (string Charter in ChartersArr)
{
InitOrAddDict(stats.CharterPlays, Charter);
if (roundedClearStatus >= (int)EClearStatus.CLEAR) InitOrAddDict(stats.CharterClears, Charter);
if (roundedClearStatus >= (int)EClearStatus.FC) InitOrAddDict(stats.CharterFCs, Charter);
if (roundedClearStatus == (int)EClearStatus.PERFECT) InitOrAddDict(stats.CharterPerfects, Charter);
}
InitOrAddDict(stats.GenrePlays, record.ChartGenre);
if (roundedClearStatus >= (int)EClearStatus.CLEAR) InitOrAddDict(stats.GenreClears, record.ChartGenre);
if (roundedClearStatus >= (int)EClearStatus.FC) InitOrAddDict(stats.GenreFCs, record.ChartGenre);
if (roundedClearStatus == (int)EClearStatus.PERFECT) InitOrAddDict(stats.GenrePerfects, record.ChartGenre);
InitOrAddDict(stats.LevelPlays, (int)record.ChartLevel);
if (roundedClearStatus >= (int)EClearStatus.CLEAR) InitOrAddDict(stats.LevelClears, (int)record.ChartLevel);
if (roundedClearStatus >= (int)EClearStatus.FC) InitOrAddDict(stats.LevelFCs, (int)record.ChartLevel);
if (roundedClearStatus == (int)EClearStatus.PERFECT) InitOrAddDict(stats.LevelPerfects, (int)record.ChartLevel);
stats.DistinctPlays++;
if (roundedClearStatus >= (int)EClearStatus.CLEAR) stats.DistinctClears++;
if (roundedClearStatus >= (int)EClearStatus.FC) stats.DistinctFCs++;
if (roundedClearStatus == (int)EClearStatus.PERFECT) stats.DistinctPerfects++;
}
// TODO: Add Dan and Tower
}
// Individual songs
foreach (CBestPlayRecord record in uniqueSongBestPlays)
{
Int64 roundedDifficulty = Math.Max((int)Difficulty.Easy, Math.Min((int)Difficulty.Total - 1, record.ChartDifficulty));
if (roundedDifficulty <= (int)Difficulty.Edit)
{
Int64 roundedClearStatus = Math.Max((int)EClearStatus.NONE, Math.Min((int)EClearStatus.PERFECT, record.ClearStatus + 1));
InitOrAddDict(stats.SongGenrePlays, record.ChartGenre);
if (roundedClearStatus >= (int)EClearStatus.CLEAR) InitOrAddDict(stats.SongGenreClears, record.ChartGenre);
if (roundedClearStatus >= (int)EClearStatus.FC) InitOrAddDict(stats.SongGenreFCs, record.ChartGenre);
if (roundedClearStatus == (int)EClearStatus.PERFECT) InitOrAddDict(stats.SongGenrePerfects, record.ChartGenre);
stats.SongDistinctPlays++;
if (roundedClearStatus >= (int)EClearStatus.CLEAR) stats.SongDistinctClears++;
if (roundedClearStatus >= (int)EClearStatus.FC) stats.SongDistinctFCs++;
if (roundedClearStatus == (int)EClearStatus.PERFECT) stats.SongDistinctPerfects++;
}
}
return stats;
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FDK;
namespace TJAPlayer3
{
internal class LogNotification
{
private static Queue<CLogNotification> Notifications = new Queue<CLogNotification>();
public enum ENotificationType
{
EINFO,
ESUCCESS,
EWARNING,
EERROR,
}
public class CLogNotification
{
public CLogNotification(ENotificationType nt, string msg)
{
NotificationType = nt;
Message = msg;
}
public ENotificationType NotificationType = ENotificationType.EINFO;
public string Message = "";
public CCounter LifeTime = new CCounter(0, 1000, 1, TJAPlayer3.Timer);
}
public static void PopError(string message)
{
Notifications.Enqueue(new CLogNotification(ENotificationType.EERROR, message));
Trace.TraceError("<Runtime Error>: " + message);
}
public static void PopWarning(string message)
{
Notifications.Enqueue(new CLogNotification(ENotificationType.EWARNING, message));
Trace.TraceWarning("<Runtime Warning>: " + message);
}
public static void PopSuccess(string message)
{
Notifications.Enqueue(new CLogNotification(ENotificationType.ESUCCESS, message));
Trace.TraceInformation("<Runtime Success>: " + message);
}
public static void PopInfo(string message)
{
Notifications.Enqueue(new CLogNotification(ENotificationType.EINFO, message));
Trace.TraceInformation("<Runtime Info>: " + message);
}
public static void Display()
{
while (Notifications.Count > 0 && Notifications.Peek().LifeTime.IsEnded) Notifications.Dequeue();
// Add an optimized method to display the notifications here
}
}
}

View File

@ -24,8 +24,13 @@ namespace TJAPlayer3
} }
tLoadFile(); tLoadFile();
data.bestPlays = DBSaves.GetBestPlaysAsDict(data.SaveId);
data.tFactorizeBestPlays();
} }
#region [Medals] #region [Medals]
public void tEarnCoins(int amount) public void tEarnCoins(int amount)
@ -193,7 +198,7 @@ namespace TJAPlayer3
public class Data public class Data
{ {
[JsonProperty("saveId")] [JsonProperty("saveId")]
public long SaveId = 0; public Int64 SaveId = 0;
[JsonProperty("name")] [JsonProperty("name")]
public string Name = "プレイヤー1"; public string Name = "プレイヤー1";
@ -249,6 +254,80 @@ namespace TJAPlayer3
[JsonProperty("standardPasses")] [JsonProperty("standardPasses")]
public Dictionary<string, CPassStatus> standardPasses = new Dictionary<string, CPassStatus>(); public Dictionary<string, CPassStatus> standardPasses = new Dictionary<string, CPassStatus>();
[JsonProperty("___unused_00")]
public Dictionary<string, BestPlayRecords.CBestPlayRecord> bestPlays = new Dictionary<string, BestPlayRecords.CBestPlayRecord> ();
[JsonProperty("___unused_01")]
public Dictionary<string, BestPlayRecords.CBestPlayRecord> bestPlaysDistinctCharts = new Dictionary<string, BestPlayRecords.CBestPlayRecord>();
[JsonProperty("___unused_02")]
public Dictionary<string, BestPlayRecords.CBestPlayRecord> bestPlaysDistinctSongs = new Dictionary<string, BestPlayRecords.CBestPlayRecord>();
[JsonProperty("___unused_03")]
public BestPlayRecords.CBestPlayStats bestPlaysStats = new BestPlayRecords.CBestPlayStats ();
#region [Factorize best plays]
public void tFactorizeBestPlays()
{
bestPlaysDistinctCharts = new Dictionary<string, BestPlayRecords.CBestPlayRecord>();
foreach (BestPlayRecords.CBestPlayRecord bestPlay in bestPlays.Values)
{
string key = bestPlay.ChartUniqueId + bestPlay.ChartDifficulty.ToString();
if (!bestPlaysDistinctCharts.ContainsKey(key))
{
bestPlaysDistinctCharts[key] = bestPlay;
}
else
{
if (bestPlay.HighScore > bestPlaysDistinctCharts[key].HighScore)
{
bestPlaysDistinctCharts[key].HighScore = bestPlay.HighScore;
bestPlaysDistinctCharts[key].HighScoreGoodCount = bestPlay.HighScoreGoodCount;
bestPlaysDistinctCharts[key].HighScoreOkCount = bestPlay.HighScoreOkCount;
bestPlaysDistinctCharts[key].HighScoreBadCount = bestPlay.HighScoreBadCount;
bestPlaysDistinctCharts[key].HighScoreRollCount = bestPlay.HighScoreRollCount;
bestPlaysDistinctCharts[key].HighScoreBoomCount = bestPlay.HighScoreBoomCount;
bestPlaysDistinctCharts[key].HighScoreMaxCombo = bestPlay.HighScoreMaxCombo;
bestPlaysDistinctCharts[key].HighScoreADLibCount = bestPlay.HighScoreADLibCount;
}
bestPlaysDistinctCharts[key].ScoreRank = Math.Max(bestPlaysDistinctCharts[key].ScoreRank, bestPlay.ScoreRank);
bestPlaysDistinctCharts[key].ClearStatus = Math.Max(bestPlaysDistinctCharts[key].ClearStatus, bestPlay.ClearStatus);
}
}
bestPlaysDistinctSongs = new Dictionary<string, BestPlayRecords.CBestPlayRecord>();
foreach (BestPlayRecords.CBestPlayRecord bestPlay in bestPlaysDistinctCharts.Values)
{
string key = bestPlay.ChartUniqueId;
if (!bestPlaysDistinctSongs.ContainsKey(key))
{
bestPlaysDistinctSongs[key] = bestPlay;
}
else
{
if (bestPlay.HighScore > bestPlaysDistinctSongs[key].HighScore)
{
bestPlaysDistinctSongs[key].HighScore = bestPlay.HighScore;
bestPlaysDistinctSongs[key].HighScoreGoodCount = bestPlay.HighScoreGoodCount;
bestPlaysDistinctSongs[key].HighScoreOkCount = bestPlay.HighScoreOkCount;
bestPlaysDistinctSongs[key].HighScoreBadCount = bestPlay.HighScoreBadCount;
bestPlaysDistinctSongs[key].HighScoreRollCount = bestPlay.HighScoreRollCount;
bestPlaysDistinctSongs[key].HighScoreBoomCount = bestPlay.HighScoreBoomCount;
bestPlaysDistinctSongs[key].HighScoreMaxCombo = bestPlay.HighScoreMaxCombo;
bestPlaysDistinctSongs[key].HighScoreADLibCount = bestPlay.HighScoreADLibCount;
}
bestPlaysDistinctSongs[key].ScoreRank = Math.Max(bestPlaysDistinctSongs[key].ScoreRank, bestPlay.ScoreRank);
bestPlaysDistinctSongs[key].ClearStatus = Math.Max(bestPlaysDistinctSongs[key].ClearStatus, bestPlay.ClearStatus);
}
}
bestPlaysStats = BestPlayRecords.tGenerateBestPlayStats(bestPlaysDistinctCharts.Values, bestPlaysDistinctSongs.Values);
}
#endregion
} }
public Data data = new Data(); public Data data = new Data();

View File

@ -53,7 +53,7 @@ namespace TJAPlayer3
data[((Int64)reader["NameplateId"]).ToString()] = nu; data[((Int64)reader["NameplateId"]).ToString()] = nu;
} }
reader.Close();
} }
} }
public class NameplateUnlockable public class NameplateUnlockable

View File

@ -11,56 +11,103 @@ namespace TJAPlayer3
{ {
internal class DBSaves internal class DBSaves
{ {
private static string _savesDBPath = @$"{TJAPlayer3.strEXEのあるフォルダ}Saves.db3"; private static string _savesDBFilename = $@"Saves.db3";
private static string _savesDBPath = @$"{TJAPlayer3.strEXEのあるフォルダ}{_savesDBFilename}";
private static SqliteConnection SavesDBConnection = new SqliteConnection(@$"Data Source={_savesDBPath}"); private static SqliteConnection SavesDBConnection = new SqliteConnection(@$"Data Source={_savesDBPath}");
public static SqliteConnection GetSavesDBConnection() private static string _DBNotFoundError = @$"The database {_savesDBFilename} was not found or the connection failed";
public static SqliteConnection? GetSavesDBConnection()
{
try
{ {
if (SavesDBConnection != null && SavesDBConnection.State == ConnectionState.Closed) SavesDBConnection.Open(); if (SavesDBConnection != null && SavesDBConnection.State == ConnectionState.Closed) SavesDBConnection.Open();
return SavesDBConnection; return SavesDBConnection;
} }
catch
public class CBestPlayRecord
{ {
public string ChartUniqueId = "none"; LogNotification.PopError(_DBNotFoundError);
public string ChartGenre = "none"; return null;
public string Charter = "none"; }
public string Artist = "none";
public Int64 PlayMods = 0;
public Int64 ChartDifficulty = 3;
public Int64 ChartLevel = 8;
public Int64 ClearStatus = -1;
public Int64 ScoreRank = -1;
public Int64 HighScore = 0;
public Int64 TowerBestFloor = 0;
public List<int> DanExam1 = new List<int> { -1 };
public List<int> DanExam2 = new List<int> { -1 };
public List<int> DanExam3 = new List<int> { -1 };
public List<int> DanExam4 = new List<int> { -1 };
public List<int> DanExam5 = new List<int> { -1 };
public List<int> DanExam6 = new List<int> { -1 };
public List<int> DanExam7 = new List<int> { -1 };
public Int64 PlayCount = 1;
public Int64 HighScoreGoodCount = 0;
public Int64 HighScoreOkCount = 0;
public Int64 HighScoreBadCount = 0;
public Int64 HighScoreMaxCombo = 0;
public Int64 HighScoreRollCount = 0;
public Int64 HighScoreADLibCount = 0;
public Int64 HighScoreBoomCount = 0;
} }
public static Int64 GetPlayerSaveId(int player)
{
return TJAPlayer3.SaveFileInstances[TJAPlayer3.GetActualPlayer(player)].data.SaveId;
}
#region [best_plays Table] #region [best_plays Table]
public static Dictionary<string, BestPlayRecords.CBestPlayRecord> GetBestPlaysAsDict(Int64 saveId)
{
Dictionary<string, BestPlayRecords.CBestPlayRecord> _bestPlays = new Dictionary<string, BestPlayRecords.CBestPlayRecord>();
SqliteConnection? connection = GetSavesDBConnection();
if (connection == null) return _bestPlays;
var command = connection.CreateCommand();
command.CommandText =
@$"
SELECT *
FROM best_plays
WHERE SaveId={saveId};
";
SqliteDataReader reader = command.ExecuteReader();
while (reader.Read())
{
BestPlayRecords.CBestPlayRecord record = new BestPlayRecords.CBestPlayRecord();
record.ChartUniqueId = (string)reader["ChartUniqueId"];
record.ChartGenre = (string)reader["ChartGenre"];
record.Charter = (string)reader["Charter"];
record.Artist = (string)reader["Artist"];
record.PlayMods = (Int64)reader["PlayMods"];
record.ChartDifficulty = (Int64)reader["ChartDifficulty"]; ;
record.ChartLevel = (Int64)reader["ChartLevel"];
record.ClearStatus = (Int64)reader["ClearStatus"];
record.ScoreRank = (Int64)reader["ScoreRank"];
record.HighScore = (Int64)reader["HighScore"];
record.TowerBestFloor = (Int64)reader["TowerBestFloor"];
record.DanExam1 = JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam1"] ?? "[]") ?? new List<int>();
record.DanExam2 = JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam2"] ?? "[]") ?? new List<int>();
record.DanExam3 = JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam3"] ?? "[]") ?? new List<int>();
record.DanExam4 = JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam4"] ?? "[]") ?? new List<int>();
record.DanExam5 = JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam5"] ?? "[]") ?? new List<int>();
record.DanExam6 = JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam6"] ?? "[]") ?? new List<int>();
record.DanExam7 = JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam7"] ?? "[]") ?? new List<int>();
record.PlayCount = (Int64)reader["PlayCount"];
record.HighScoreGoodCount = (Int64)reader["HighScoreGoodCount"];
record.HighScoreOkCount = (Int64)reader["HighScoreOkCount"];
record.HighScoreBadCount = (Int64)reader["HighScoreBadCount"];
record.HighScoreMaxCombo = (Int64)reader["HighScoreMaxCombo"];
record.HighScoreRollCount = (Int64)reader["HighScoreRollCount"];
record.HighScoreADLibCount = (Int64)reader["HighScoreADLibCount"];
record.HighScoreBoomCount = (Int64)reader["HighScoreBoomCount"];
string key = record.ChartUniqueId + record.ChartDifficulty.ToString() + record.PlayMods.ToString();
_bestPlays[key] = record;
}
reader.Close();
return _bestPlays;
}
public static void RegisterPlay(int player, int clearStatus, int scoreRank) public static void RegisterPlay(int player, int clearStatus, int scoreRank)
{ {
SqliteConnection connection = GetSavesDBConnection(); SqliteConnection? connection = GetSavesDBConnection();
if (connection == null) return;
SaveFile.Data saveData = TJAPlayer3.SaveFileInstances[TJAPlayer3.GetActualPlayer(player)].data; SaveFile.Data saveData = TJAPlayer3.SaveFileInstances[TJAPlayer3.GetActualPlayer(player)].data;
CBestPlayRecord currentPlay = new CBestPlayRecord(); BestPlayRecords.CBestPlayRecord currentPlay = new BestPlayRecords.CBestPlayRecord();
var choosenSong = TJAPlayer3.stageSongSelect.rChoosenSong; var choosenSong = TJAPlayer3.stageSongSelect.rChoosenSong;
var choosenDifficulty = TJAPlayer3.stageSongSelect.nChoosenSongDifficulty[player]; var choosenDifficulty = TJAPlayer3.stageSongSelect.nChoosenSongDifficulty[player];
var chartScore = TJAPlayer3.stage演奏ドラム画面.CChartScore[player]; var chartScore = TJAPlayer3.stage演奏ドラム画面.CChartScore[player];
List<int>[] danResults = new List<int>[7] { new List<int>(), new List<int>(), new List<int>(), new List<int>(), new List<int>(), new List<int>(), new List<int>() }; List<int>[] danResults = new List<int>[7] { new List<int>(), new List<int>(), new List<int>(), new List<int>(), new List<int>(), new List<int>(), new List<int>() };
// Do not register the play if Dan/Tower and any mod is ON
if ((choosenDifficulty == (int)Difficulty.Tower || choosenDifficulty == (int)Difficulty.Dan) && !ModIcons.tPlayIsStock(player)) return;
// 1st step: Init best play record class // 1st step: Init best play record class
{ {
@ -167,6 +214,7 @@ namespace TJAPlayer3
} }
} }
} }
reader.Close();
} }
// Intermede: Dan results to Dan exams // Intermede: Dan results to Dan exams
@ -183,6 +231,13 @@ namespace TJAPlayer3
} }
} }
// Intermede: Update locally the play on the save file to reload it without requerying the database
{
string key = currentPlay.ChartUniqueId + currentPlay.ChartDifficulty.ToString() + currentPlay.PlayMods.ToString();
saveData.bestPlays[key] = currentPlay;
saveData.tFactorizeBestPlays();
}
// 3rd step: Insert/Update to database // 3rd step: Insert/Update to database
{ {
SqliteCommand cmd = connection.CreateCommand(); SqliteCommand cmd = connection.CreateCommand();

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Newtonsoft.Json; using Newtonsoft.Json;
using static TJAPlayer3.BestPlayRecords;
namespace TJAPlayer3 namespace TJAPlayer3
{ {
@ -15,7 +16,7 @@ namespace TJAPlayer3
["dp"] = 3, ["dp"] = 3,
["lp"] = 3, ["lp"] = 3,
["sp"] = 2, ["sp"] = 2,
["sg"] = 3, ["sg"] = 2,
}; };
public class CUnlockConditions public class CUnlockConditions
@ -94,10 +95,10 @@ namespace TJAPlayer3
* ch : "Coins here", coin requirement, payable within the heya menu, 1 value : [Coin price] * ch : "Coins here", coin requirement, payable within the heya menu, 1 value : [Coin price]
* cs : "Coins shop", coin requirement, payable only within the Medal shop selection screen * cs : "Coins shop", coin requirement, payable only within the Medal shop selection screen
* cm : "Coins menu", coin requirement, payable only within the song select screen (used only for songs) * cm : "Coins menu", coin requirement, payable only within the song select screen (used only for songs)
* dp : "Difficulty pass", count of difficulties pass, unlock check during the results screen, condition 3 values : [Difficulty int (0~4), Clear status (0~2), Number of performances], input 1 value [Plays fitting the condition] * dp : "Difficulty pass", count of difficulties pass, unlock check during the results screen, condition 3 values : [Difficulty int (0~4), Clear status (0~4), Number of performances], input 1 value [Plays fitting the condition]
* lp : "Level pass", count of level pass, unlock check during the results screen, condition 3 values : [Star rating, Clear status (0~2), Number of performances], input 1 value [Plays fitting the condition] * lp : "Level pass", count of level pass, unlock check during the results screen, condition 3 values : [Star rating, Clear status (0~4), Number of performances], input 1 value [Plays fitting the condition]
* sp : "Song performance", count of a specific song pass, unlock check during the results screen, condition 2 x n values for n songs : [Difficulty int (0~4, if -1 : Any), Clear status (0~2), ...], input 1 value [Count of fullfiled songs], n references for n songs (Song ids) * sp : "Song performance", count of a specific song pass, unlock check during the results screen, condition 2 x n values for n songs : [Difficulty int (0~4, if -1 : Any), Clear status (0~4), ...], input 1 value [Count of fullfiled songs], n references for n songs (Song ids)
* sg : "Song genre (performance)", count of any song pass within a specific genre folder, unlock check during the results screen, condition 3 x n values for n songs : [Song count, Difficulty int (0~4, if -1 : Any), Clear status (0~2), ...], input 1 value [Count of fullfiled genres], n references for n genres (Genre names) * sg : "Song genre (performance)", count of any unique song pass within a specific genre folder, unlock check during the results screen, condition 2 x n values for n songs : [Song count, Clear status (0~4), ...], input 1 value [Count of fullfiled genres], n references for n genres (Genre names)
* *
* *
*/ */
@ -237,47 +238,32 @@ namespace TJAPlayer3
{ {
_aimedDifficulty = this.Values[0]; // Difficulty if dp, Level if lp _aimedDifficulty = this.Values[0]; // Difficulty if dp, Level if lp
_aimedStatus = this.Values[1]; _aimedStatus = this.Values[1];
// dp and lp only work for regular (Dan and Tower excluded) charts
if (_aimedStatus < (int)EClearStatus.NONE || _aimedStatus >= (int)EClearStatus.TOTAL) return 0;
if (this.Condition == "dp" && (_aimedDifficulty < (int)Difficulty.Easy || _aimedDifficulty > (int)Difficulty.Edit)) return 0;
} }
var _sf = TJAPlayer3.SaveFileInstances[player].data.standardPasses; var bpDistinctCharts = TJAPlayer3.SaveFileInstances[player].data.bestPlaysDistinctCharts;
if (_sf == null var chartStats = TJAPlayer3.SaveFileInstances[player].data.bestPlaysStats;
|| _aimedDifficulty < 0
|| _aimedStatus < 0
|| (this.Condition == "dp" && _aimedDifficulty > 4)
|| _aimedStatus > 2)
return -1;
switch (this.Condition) switch (this.Condition)
{ {
case "dp": case "dp":
var _table = chartStats.ClearStatuses[_aimedDifficulty];
var _ura = chartStats.ClearStatuses[(int)Difficulty.Edit];
int _count = 0; int _count = 0;
foreach (KeyValuePair<string, SaveFile.CPassStatus> cps in _sf) for (int i = _aimedStatus; i < (int)EClearStatus.TOTAL; i++)
{ {
var _values = cps.Value.d; _count += _table[i];
if (_values[_aimedDifficulty] >= _aimedStatus) if (_aimedDifficulty == (int)Difficulty.Oni) _count += _ura[i];
_count++;
// Extreme includes Extra
if (_aimedDifficulty == 3 && _values[4] >= _aimedStatus)
_count++;
} }
return _count; return _count;
case "lp": case "lp":
_count = 0; if (_aimedStatus == (int)EClearStatus.NONE) return chartStats.LevelPlays.TryGetValue(_aimedDifficulty, out var value) ? value : 0;
foreach (KeyValuePair<string, SaveFile.CPassStatus> cps in _sf) else if (_aimedStatus <= (int)EClearStatus.CLEAR) return chartStats.LevelClears.TryGetValue(_aimedDifficulty, out var value) ? value : 0;
{ else if (_aimedStatus == (int)EClearStatus.FC) return chartStats.LevelFCs.TryGetValue(_aimedDifficulty, out var value) ? value : 0;
var _node = CSongDict.tGetNodeFromID(cps.Key); else return chartStats.LevelPerfects.TryGetValue(_aimedDifficulty, out var value) ? value : 0;
if (_node != null)
{
var _values = cps.Value.d;
var _levels = _node.nLevel;
for (int i = 0; i <= (int)Difficulty.Edit; i++)
{
if (_levels[i] == _aimedDifficulty && _values[i] >= _aimedStatus)
_count++;
}
}
}
return _count;
case "sp": case "sp":
_count = 0; _count = 0;
for (int i = 0; i < this.Values.Length / this.RequiredArgCount; i++) for (int i = 0; i < this.Values.Length / this.RequiredArgCount; i++)
@ -287,18 +273,24 @@ namespace TJAPlayer3
_aimedDifficulty = this.Values[_base]; _aimedDifficulty = this.Values[_base];
_aimedStatus = this.Values[_base + 1]; _aimedStatus = this.Values[_base + 1];
if (_sf.ContainsKey(_songId)) if (_aimedDifficulty >= (int)Difficulty.Easy && _aimedDifficulty <= (int)Difficulty.Edit)
{ {
var _values = _sf[_songId].d; string key = _songId + _aimedDifficulty.ToString();
if (_aimedDifficulty >= 0 && _aimedDifficulty < 4) var _cht = bpDistinctCharts.TryGetValue(key, out var value) ? value : null;
{ if (_cht != null && _cht.ClearStatus >= _aimedStatus) _count++;
if (_values[_aimedDifficulty] >= _aimedStatus)
_count++;
} }
else else if (_aimedDifficulty < (int)Difficulty.Easy)
{
for (int diff = (int)Difficulty.Easy; diff <= (int)Difficulty.Edit; diff++)
{
string key = _songId + diff.ToString();
var _cht = bpDistinctCharts.TryGetValue(key, out var value) ? value : null;
if (_cht != null && _cht.ClearStatus >= _aimedStatus)
{ {
if (Array.Exists(_values, _v => _v >= _aimedStatus))
_count++; _count++;
break;
}
} }
} }
} }
@ -310,33 +302,15 @@ namespace TJAPlayer3
int _base = i * this.RequiredArgCount; int _base = i * this.RequiredArgCount;
string _genreName = this.Reference[i]; string _genreName = this.Reference[i];
int _songCount = this.Values[_base]; int _songCount = this.Values[_base];
_aimedDifficulty = this.Values[_base + 1]; _aimedStatus = this.Values[_base + 1];
_aimedStatus = this.Values[_base + 2];
int _innerCount = 0;
var _songList = CSongDict.tGetNodesByGenreName(_genreName); int _satifsiedCount = 0;
foreach (string songId in _songList) if (_aimedStatus == (int)EClearStatus.NONE) _satifsiedCount = chartStats.SongGenrePlays.TryGetValue(_genreName, out var value) ? value : 0;
{ else if (_aimedStatus <= (int)EClearStatus.CLEAR) _satifsiedCount = chartStats.SongGenreClears.TryGetValue(_genreName, out var value) ? value : 0;
_innerCount = 0; else if (_aimedStatus == (int)EClearStatus.FC) _satifsiedCount = chartStats.SongGenreFCs.TryGetValue(_genreName, out var value) ? value : 0;
if (_sf.ContainsKey(songId)) else return _satifsiedCount = chartStats.SongGenrePerfects.TryGetValue(_genreName, out var value) ? value : 0;
{
var _values = _sf[songId].d;
if (_aimedDifficulty >= 0 && _aimedDifficulty < 4)
{
if (_values[_aimedDifficulty] >= _aimedStatus)
_innerCount++;
}
else
{
if (Array.Exists(_values, _v => _v >= _aimedStatus))
_innerCount++;
}
}
}
if (_innerCount >= _songCount)
_count++;
if (_satifsiedCount >= _songCount) _count++;
} }
return _count; return _count;
} }

View File

@ -162,6 +162,20 @@ namespace TJAPlayer3
#region [Mod flags] #region [Mod flags]
static public bool tPlayIsStock(int player)
{
int actual = TJAPlayer3.GetActualPlayer(player);
if (TJAPlayer3.ConfigIni.nFunMods[actual] != EFunMods.NONE) return false;
if (TJAPlayer3.ConfigIni.bJust[actual] != 0) return false;
if (TJAPlayer3.ConfigIni.nTimingZones[actual] != 2) return false;
if (TJAPlayer3.ConfigIni.n演奏速度 != 20) return false;
if (TJAPlayer3.ConfigIni.eRandom[actual] != Eランダムモード.OFF) return false;
if (TJAPlayer3.ConfigIni.eSTEALTH[actual] != EStealthMode.OFF) return false;
if (TJAPlayer3.ConfigIni.nScrollSpeed[actual] != 9) return false;
return true;
}
static public Int64 tModsToPlayModsFlags(int player) static public Int64 tModsToPlayModsFlags(int player)
{ {
byte[] _flags = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 }; byte[] _flags = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };