1
0
mirror of synced 2024-11-23 23:21:06 +01:00

handle best scores on database

This commit is contained in:
0auBSQ 2024-06-10 04:26:52 +09:00
parent 14d9b8ce60
commit c43a3b2727
6 changed files with 343 additions and 16 deletions

View File

@ -15,4 +15,16 @@
* e : "Equal"
* me : "More or equal" (Default)
* m : "More than"
* d : "Different"
* d : "Different"
== PlayMods flags ==
A 8 bytes value, from upper to lower bytes:
* Byte 1: Scroll speed: The mod value (x0.1 = 0, +1 per 0.1 increment) capped to 255
* Byte 2: Doron: The mod value (0 - 2 for None/Doron/Stealth)
* Byte 3: Random: The mod value (0 - 5 for None/Mirror/Random/SuperRandom/HyperRandom)
* Byte 4: Song speed: The mod value (x1 is 20, +-1 per 0.05 change) capped to 255
* Byte 5: Timing : The mod value (0 - 5 from Loose to Rigorous)
* Byte 6: Just: The mod value (0 - 2 for None/Just/Safe)
* Byte 7: Unused
* Byte 8: Fun mods: The mod value (0 - 2 for None/Avalanche/Minesweeper)

Binary file not shown.

View File

@ -1,25 +1,66 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
using Newtonsoft.Json;
namespace TJAPlayer3
{
public static class HDatabaseHelpers
{
private static string _savesDBPath = @$"{TJAPlayer3.strEXEのあるフォルダ}Saves.db3";
private static SqliteConnection SavesDBConnection = new SqliteConnection(@$"Data Source={_savesDBPath}");
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 static SqliteConnection GetSavesDBConnection()
{
if (SavesDBConnection != null && SavesDBConnection.State == ConnectionState.Closed) SavesDBConnection.Open();
return SavesDBConnection;
}
public static List<string> GetAvailableLanguage(SqliteConnection connection, string prefix)
{
List<string> _translations = new List<string>();
foreach (string cd in CLangManager.Langcodes)
{
var chk = connection.CreateCommand();
SqliteCommand chk = connection.CreateCommand();
chk.CommandText =
@$"
SELECT count(*) FROM sqlite_master WHERE type='table' AND name='{prefix}_{cd}';
";
var chkreader = chk.ExecuteReader();
SqliteDataReader chkreader = chk.ExecuteReader();
while (chkreader.Read())
{
if (chkreader.GetInt32(0) > 0)
@ -28,5 +69,196 @@ namespace TJAPlayer3
}
return _translations;
}
public static void RegisterPlay(int player, int clearStatus, int scoreRank)
{
SqliteConnection connection = GetSavesDBConnection();
SaveFile.Data saveData = TJAPlayer3.SaveFileInstances[TJAPlayer3.GetActualPlayer(player)].data;
CBestPlayRecord currentPlay = new CBestPlayRecord();
var choosenSong = TJAPlayer3.stageSongSelect.rChoosenSong;
var choosenDifficulty = TJAPlayer3.stageSongSelect.nChoosenSongDifficulty[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>() };
// 1st step: Init best play record class
{
currentPlay.ChartUniqueId = choosenSong.uniqueId.data.id;
currentPlay.ChartGenre = choosenSong.strジャンル;
currentPlay.Charter = choosenSong.strNotesDesigner[choosenDifficulty];
currentPlay.Artist = choosenSong.strサブタイトル; // There is no direct Artist tag on the .tja format, so we directly use the subtitle as a guess
currentPlay.PlayMods = ModIcons.tModsToPlayModsFlags(player);
currentPlay.ChartDifficulty = choosenDifficulty;
currentPlay.ChartLevel = choosenSong.arスコア[choosenDifficulty]..nレベル[choosenDifficulty];
currentPlay.ClearStatus = clearStatus;
currentPlay.ScoreRank = scoreRank;
currentPlay.HighScore = chartScore.nScore;
if (choosenDifficulty == (int)Difficulty.Tower) currentPlay.TowerBestFloor = CFloorManagement.LastRegisteredFloor;
if (choosenDifficulty == (int)Difficulty.Dan)
{
for (int i = 0; i < TJAPlayer3.stageSongSelect.rChoosenSong.DanSongs.Count; i++)
{
for (int j = 0; j < TJAPlayer3.stageSongSelect.rChoosenSong.DanSongs[i].Dan_C.Length; j++)
{
if (TJAPlayer3.stageSongSelect.rChoosenSong.DanSongs[i].Dan_C[j] != null)
{
int amount = TJAPlayer3.stageSongSelect.rChoosenSong.DanSongs[i].Dan_C[j].GetAmount();
danResults[j].Add(amount);
}
}
}
}
currentPlay.PlayCount = 1; // Will be directly added to the current instance if exists
currentPlay.HighScoreGoodCount = chartScore.nGreat;
currentPlay.HighScoreOkCount = chartScore.nGood;
currentPlay.HighScoreBadCount = chartScore.nMiss;
currentPlay.HighScoreMaxCombo = TJAPlayer3.stage演奏ドラム画面.actCombo.n現在のコンボ数.[player];
currentPlay.HighScoreRollCount = chartScore.nRoll;
currentPlay.HighScoreADLibCount = chartScore.nADLIB;
currentPlay.HighScoreBoomCount = chartScore.nMine;
}
// 2nd step: Overwrite the instance with best play results if exists
{
SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText =
@$"
SELECT * FROM best_plays WHERE ChartUniqueId='{currentPlay.ChartUniqueId}' AND PlayMods={currentPlay.PlayMods} and ChartDifficulty={currentPlay.ChartDifficulty};
";
SqliteDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
// Overwrite multiple variables at once if the highscore is replaced
Int64 _highscore = (Int64)reader["HighScore"];
if (_highscore > currentPlay.HighScore)
{
currentPlay.HighScore = _highscore;
currentPlay.HighScoreGoodCount = (Int64)reader["HighScoreGoodCount"];
currentPlay.HighScoreOkCount = (Int64)reader["HighScoreOkCount"];
currentPlay.HighScoreBadCount = (Int64)reader["HighScoreBadCount"];
currentPlay.HighScoreMaxCombo = (Int64)reader["HighScoreMaxCombo"];
currentPlay.HighScoreRollCount = (Int64)reader["HighScoreRollCount"];
currentPlay.HighScoreADLibCount = (Int64)reader["HighScoreADLibCount"];
currentPlay.HighScoreBoomCount = (Int64)reader["HighScoreBoomCount"];
}
currentPlay.ClearStatus = Math.Max(currentPlay.ClearStatus, (Int64)reader["ClearStatus"]);
currentPlay.ScoreRank = Math.Max(currentPlay.ScoreRank, (Int64)reader["ScoreRank"]);
if (choosenDifficulty == (int)Difficulty.Tower) currentPlay.TowerBestFloor = Math.Max(currentPlay.TowerBestFloor, (Int64)reader["TowerBestFloor"]);
if (choosenDifficulty == (int)Difficulty.Dan)
{
List<int>[] oldDanResults = new List<int>[7]
{
JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam1"]) ?? new List<int> { -1 },
JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam2"]) ?? new List<int> { -1 },
JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam3"]) ?? new List<int> { -1 },
JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam4"]) ?? new List<int> { -1 },
JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam5"]) ?? new List<int> { -1 },
JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam6"]) ?? new List<int> { -1 },
JsonConvert.DeserializeObject<List<int>>((string)reader["DanExam7"]) ?? new List<int> { -1 }
};
for (int i = 0; i < TJAPlayer3.stageSongSelect.rChoosenSong.DanSongs.Count; i++)
{
for (int j = 0; j < TJAPlayer3.stageSongSelect.rChoosenSong.DanSongs[i].Dan_C.Length; j++)
{
if (TJAPlayer3.stageSongSelect.rChoosenSong.DanSongs[i].Dan_C[j] != null)
{
int amount = danResults[j][i];
if (i < oldDanResults[j].Count)
{
int current = oldDanResults[j][i];
if (current == -1)
{
danResults[j][i] = amount;
}
else if (TJAPlayer3.stageSongSelect.rChoosenSong.DanSongs[i].Dan_C[j].GetExamRange() == Exam.Range.More)
{
danResults[j][i] = Math.Max(amount, current);
}
else if (TJAPlayer3.stageSongSelect.rChoosenSong.DanSongs[i].Dan_C[j].GetExamRange() == Exam.Range.Less)
{
danResults[j][i] = Math.Min(amount, current);
}
}
}
}
}
}
}
}
// Intermede: Dan results to Dan exams
{
if (choosenDifficulty == (int)Difficulty.Dan)
{
currentPlay.DanExam1 = danResults[0];
currentPlay.DanExam2 = danResults[1];
currentPlay.DanExam3 = danResults[2];
currentPlay.DanExam4 = danResults[3];
currentPlay.DanExam5 = danResults[4];
currentPlay.DanExam6 = danResults[5];
currentPlay.DanExam7 = danResults[6];
}
}
// 3rd step: Insert/Update to database
{
SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText = $@"
INSERT INTO best_plays(ChartUniqueId,ChartGenre,Charter,Artist,PlayMods,ChartDifficulty,ChartLevel,ClearStatus,ScoreRank,HighScore,SaveId,TowerBestFloor,DanExam1,DanExam2,DanExam3,DanExam4,DanExam5,DanExam6,DanExam7,PlayCount,HighScoreGoodCount,HighScoreOkCount,HighScoreBadCount,HighScoreMaxCombo,HighScoreRollCount,HighScoreADLibCount,HighScoreBoomCount)
VALUES(
'{currentPlay.ChartUniqueId}',
'{currentPlay.ChartGenre}',
'{currentPlay.Charter}',
'{currentPlay.Artist}',
{currentPlay.PlayMods},
{currentPlay.ChartDifficulty},
{currentPlay.ChartLevel},
{currentPlay.ClearStatus},
{currentPlay.ScoreRank},
{currentPlay.HighScore},
{saveData.SaveId},
{currentPlay.TowerBestFloor},
'{@$"[{String.Join(",", currentPlay.DanExam1.ToArray())}]"}',
'{@$"[{String.Join(",", currentPlay.DanExam2.ToArray())}]"}',
'{@$"[{String.Join(",", currentPlay.DanExam3.ToArray())}]"}',
'{@$"[{String.Join(",", currentPlay.DanExam4.ToArray())}]"}',
'{@$"[{String.Join(",", currentPlay.DanExam5.ToArray())}]"}',
'{@$"[{String.Join(",", currentPlay.DanExam6.ToArray())}]"}',
'{@$"[{String.Join(",", currentPlay.DanExam7.ToArray())}]"}',
{currentPlay.PlayCount},
{currentPlay.HighScoreGoodCount},
{currentPlay.HighScoreOkCount},
{currentPlay.HighScoreBadCount},
{currentPlay.HighScoreMaxCombo},
{currentPlay.HighScoreRollCount},
{currentPlay.HighScoreADLibCount},
{currentPlay.HighScoreBoomCount}
)
ON CONFLICT(ChartUniqueId,ChartDifficulty,PlayMods) DO UPDATE SET
PlayCount=best_plays.PlayCount+1,
ClearStatus=EXCLUDED.ClearStatus,
ScoreRank=EXCLUDED.ScoreRank,
HighScore=EXCLUDED.HighScore,
HighScoreGoodCount=EXCLUDED.HighScoreGoodCount,
HighScoreOkCount=EXCLUDED.HighScoreOkCount,
HighScoreBadCount=EXCLUDED.HighScoreBadCount,
HighScoreMaxCombo=EXCLUDED.HighScoreMaxCombo,
HighScoreRollCount=EXCLUDED.HighScoreRollCount,
HighScoreADLibCount=EXCLUDED.HighScoreADLibCount,
HighScoreBoomCount=EXCLUDED.HighScoreBoomCount,
TowerBestFloor=EXCLUDED.TowerBestFloor,
DanExam1=EXCLUDED.DanExam1,
DanExam2=EXCLUDED.DanExam2,
DanExam3=EXCLUDED.DanExam3,
DanExam4=EXCLUDED.DanExam4,
DanExam5=EXCLUDED.DanExam5,
DanExam6=EXCLUDED.DanExam6,
DanExam7=EXCLUDED.DanExam7
";
cmd.ExecuteNonQuery();
}
}
}
}

View File

@ -69,10 +69,17 @@ namespace TJAPlayer3
{
preset = ((Dictionary<string, DBSkinPreset.SkinScene>)_ps)[TJAPlayer3.stageSongSelect.rChoosenSong.strScenePreset];
}
else
else if (_ps != null
&& ((Dictionary<string, DBSkinPreset.SkinScene>)_ps).ContainsKey(""))
{
preset = ((Dictionary<string, DBSkinPreset.SkinScene>)_ps)[""];
}
else if (_ps != null)
{
var cstps = (Dictionary<string, DBSkinPreset.SkinScene>)_ps;
Random rand = new Random();
preset = cstps.ElementAt(rand.Next(0, cstps.Count)).Value;
}
return preset;
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace TJAPlayer3
{
@ -47,6 +48,8 @@ namespace TJAPlayer3
TJAPlayer3.Tx.Mod_None.Opacity = 255;
}
#region [Displayables]
static private void tDisplayHSIcon(int x, int y, int player)
{
// TO DO : Add HS x0.5 icon (_vals == 4)
@ -155,5 +158,27 @@ namespace TJAPlayer3
TJAPlayer3.Tx.Mod_None?.t2D描画(x, y);
}
#endregion
#region [Mod flags]
static public Int64 tModsToPlayModsFlags(int player)
{
byte[] _flags = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
int actual = TJAPlayer3.GetActualPlayer(player);
_flags[0] = (byte)Math.Min(255, TJAPlayer3.ConfigIni.nScrollSpeed[actual]);
_flags[1] = (byte)TJAPlayer3.ConfigIni.eSTEALTH[actual];
_flags[2] = (byte)TJAPlayer3.ConfigIni.eRandom[actual];
_flags[3] = (byte)Math.Min(255, TJAPlayer3.ConfigIni.n演奏速度);
_flags[4] = (byte)TJAPlayer3.ConfigIni.nTimingZones[actual];
_flags[5] = (byte)TJAPlayer3.ConfigIni.bJust[actual];
_flags[7] = (byte)TJAPlayer3.ConfigIni.nFunMods[actual];
return BitConverter.ToInt64(_flags, 0);
}
#endregion
}
}

View File

@ -111,10 +111,44 @@ namespace TJAPlayer3
bAddedToRecentlyPlayedSongs = false;
try
{
/*
* Notes about the difference between Replay - Save statuses and the "Assisted clear" clear status
*
* - The values for replay files are 0 if no status, while for save files they start by -1
* - The "Assisted clear" status is used on the save files, but NOT on the replay files
* - The "Assisted clear" status is also not used in the coins evaluations
*/
int[] ClearStatus_Replay = new int[5] { 0, 0, 0, 0, 0 };
int[] ScoreRank_Replay = new int[5] { 0, 0, 0, 0, 0 };
{
int[] clearStatuses =
{
-1,
-1,
-1,
-1,
-1
};
int[] scoreRanks =
{
-1,
-1,
-1,
-1,
-1
};
bool[] assistedClear =
{
(TJAPlayer3.stageSongSelect.actPlayOption.tGetModMultiplier(CActPlayOption.EBalancingType.SCORE, false, 0) < 1f),
(TJAPlayer3.stageSongSelect.actPlayOption.tGetModMultiplier(CActPlayOption.EBalancingType.SCORE, false, 1) < 1f),
(TJAPlayer3.stageSongSelect.actPlayOption.tGetModMultiplier(CActPlayOption.EBalancingType.SCORE, false, 2) < 1f),
(TJAPlayer3.stageSongSelect.actPlayOption.tGetModMultiplier(CActPlayOption.EBalancingType.SCORE, false, 3) < 1f),
(TJAPlayer3.stageSongSelect.actPlayOption.tGetModMultiplier(CActPlayOption.EBalancingType.SCORE, false, 4) < 1f)
};
{
#region [ ]
//---------------------
this.eフェードアウト完了時の戻り値 = E戻り値.;
@ -188,7 +222,11 @@ namespace TJAPlayer3
this.nクリア[p] = 2;
if (ccf.nGood == 0) this.nクリア[p] = 3;
}
}
if (assistedClear[p]) clearStatuses[p] = 0;
else clearStatuses[p] = this.nクリア[p];
}
if ((int)TJAPlayer3.stage演奏ドラム画面.actScore.Get(EInstrumentPad.DRUMS, p) < 500000)
{
@ -206,7 +244,9 @@ namespace TJAPlayer3
}
}
}
}
scoreRanks[p] = this.nスコアランク[p] - 1;
}
}
@ -339,7 +379,11 @@ namespace TJAPlayer3
if (this.st演奏記録.Drums.nGreat数 == 0)
clearValue += 2;
}
}
if (assistedClear[0]) clearStatuses[0] = (examStatus == Exam.Status.Better_Success) ? 1 : 0;
else clearStatuses[0] = clearValue + 1;
}
if (isAutoDisabled(0))
{
@ -423,6 +467,8 @@ namespace TJAPlayer3
int tmpClear = GetTowerScoreRank();
if (tmpClear != 0) clearStatuses[0] = assistedClear[0] ? 0 : tmpClear;
if (isAutoDisabled(0))
{
ini[0].stセクション[0].nクリア[0] = Math.Max(ini[0].stセクション[0].nクリア[0], tmpClear);
@ -546,7 +592,7 @@ namespace TJAPlayer3
return chara.GetEffectCoinMultiplier() * puchichara.GetEffectCoinMultiplier();
}
if (TJAPlayer3.stageSongSelect.nChoosenSongDifficulty[0] == (int)Difficulty.Tower)
if (TJAPlayer3.stageSongSelect.nChoosenSongDifficulty[0] == (int)Difficulty.Tower)
{
diffModifier = 3;
@ -563,13 +609,15 @@ namespace TJAPlayer3
#region [Clear modifier]
int clearModifier = 0;
if (this.st演奏記録.Drums.nMiss数 == 0)
{
clearModifier = (int)(5 * lengthBonus);
if (this.st演奏記録.Drums.nGreat数 == 0)
clearModifier = (int)(12 * lengthBonus);
}
if (this.st演奏記録.Drums.nMiss数 == 0)
{
clearModifier = (int)(5 * lengthBonus);
if (this.st演奏記録.Drums.nGreat数 == 0)
{
clearModifier = (int)(12 * lengthBonus);
}
}
#endregion
@ -722,6 +770,9 @@ namespace TJAPlayer3
_cs = 2;
}
}
// Unsafe function, it is the only appropriate place to call it
HDatabaseHelpers.RegisterPlay(i, clearStatuses[i], scoreRanks[i]);
if (TJAPlayer3.stageSongSelect.actPlayOption.tGetModMultiplier(CActPlayOption.EBalancingType.SCORE, false, i) == 1f)
_sf.tUpdateSongClearStatus(TJAPlayer3.stageSongSelect.rChoosenSong, _cs, TJAPlayer3.stageSongSelect.nChoosenSongDifficulty[i]);