Add highscores and best times.

This commit is contained in:
SilicaAndPina 2020-12-27 15:42:01 +13:00
parent 0c99624f63
commit de9dfaf362
9 changed files with 291 additions and 37 deletions

View file

@ -91,6 +91,15 @@
"end_of_meta":"^Z",
"back_to_map":"^M",
"long_full_line":"^L",
"highscores":{
"header_meta":"^ATMini-Game Rankings^H",
"highscore_format":"<B><font color='#000088'>%GAMETITLE%</font></B> Rank: <B>#%RANKING%</B> With: %SCORE% points (%TOTALPLAYS% plays)<BR>",
"besttime_format":"<B><font color='#000088'>%GAMETITLE%</font></B> Rank: <B>#%RANKING%</B> With: %SCORE% time score (%TOTALPLAYS% plays)<BR>",
"game_highscore_header":"<B>Top 20 High Scores for %GAMETITLE%</B><BR>",
"game_highscore_format":"#%RANKING%: %SCORE% points -- %USERNAME% [played %TOTALPLAYS% times]<BR>",
"game_besttime_header":"<B>Top 20 Best Times for %GAMETITLE%</B><BR>",
"game_besttime_format":"#%RANKING% Best Time: %SCORE% sec -- %USERNAME% [played %TOTALPLAYS% times]<BR>"
},
"quest_log":{
"header_meta":"^ATYour Horse Isle Adventure Log^H",
"quest_format":"%TITLE% (%QUESTPOINTS%qp) [%DIFFICULTY%] %COMPLETION%<BR>",

View file

@ -163,6 +163,17 @@ namespace HISP.Game
public static string SellButton;
public static string SellAllButton;
// Highscore List
public static string HighscoreHeaderMeta;
public static string HighscoreFormat;
public static string BestTimeFormat;
public static string GameBestTimeFormat;
public static string GameBestTimeHeaderFormat;
public static string GameHighScoreHeaderFormat;
public static string GameHighScoreFormat;
// Shop
public static string ThingsIAmSelling;
public static string ThingsYouSellMe;
@ -229,6 +240,30 @@ namespace HISP.Game
// Click
public static string NothingInterestingHere;
public static string FormatBestTimeHeader(string gameName)
{
return GameBestTimeHeaderFormat.Replace("%GAMETITLE%", gameName);
}
public static string FormatBestTimeListEntry(int ranking, int score, string username, int totalplays)
{
return GameBestTimeFormat.Replace("%RANKING%", ranking.ToString("N0")).Replace("%SCORE%", score.ToString().Insert(score.ToString().Length - 2, ".")).Replace("%USERNAME%", username).Replace("%TOTALPLAYS%", totalplays.ToString("N0"));
}
public static string FormatHighscoreHeader(string gameName)
{
return GameHighScoreHeaderFormat.Replace("%GAMETITLE%", gameName);
}
public static string FormatHighscoreListEntry(int ranking, int score, string username, int totalplays)
{
return GameHighScoreFormat.Replace("%RANKING%", ranking.ToString("N0")).Replace("%SCORE%", score.ToString("N0")).Replace("%USERNAME%", username).Replace("%TOTALPLAYS%", totalplays.ToString("N0"));
}
public static string FormatHighscoreStat(string gameTitle, int ranking, int score, int totalplays)
{
return HighscoreFormat.Replace("%GAMETITLE%", gameTitle).Replace("%RANKING%", ranking.ToString("N0")).Replace("%SCORE%", score.ToString("N0")).Replace("%TOTALPLAYS%", totalplays.ToString("N0"));
}
public static string FormatBestTimeStat(string gameTitle, int ranking, int score, int totalplays)
{
return BestTimeFormat.Replace("%GAMETITLE%", gameTitle).Replace("%RANKING%", ranking.ToString("N0")).Replace("%SCORE%", score.ToString()).Replace("%TOTALPLAYS%", totalplays.ToString("N0"));
}
public static string FormatMoneyEarnedMessage(int money)
{
return YouEarnedMoneyFormat.Replace("%MONEY%", money.ToString("N0"));

View file

@ -225,7 +225,6 @@ namespace HISP.Game
return message;
}
public static string SelectPlayerStatFormat(int statValue)
{
int curValue = 1000;
@ -240,7 +239,55 @@ namespace HISP.Game
}
throw new Exception("A mathematically impossible error occured. please check wether the laws of physics still apply.");
}
public static string BuildTopHighscores(string gameName)
{
Highscore.HighscoreTableEntry[] scores = Database.GetTopScores(gameName, 20);
if (scores.Length <= 0)
return "No scores recorded.";
string message = "";
message += Messages.FormatHighscoreHeader(gameName);
for (int i = 0; i < scores.Length; i++)
{
message += Messages.FormatHighscoreListEntry(i+1, scores[i].Score, Database.GetUsername(scores[i].UserId), scores[i].TimesPlayed);
}
message += Messages.BackToMap;
message += Messages.MetaTerminator;
return message;
}
public static string BuildTopTimes(string gameName)
{
Highscore.HighscoreTableEntry[] scores = Database.GetTopScores(gameName, 20);
if (scores.Length <= 0)
return "No times recorded.";
string message = "";
message += Messages.FormatBestTimeHeader(gameName);
for (int i = 0; i < scores.Length; i++)
{
message += Messages.FormatBestTimeListEntry(i+1, scores[i].Score, Database.GetUsername(scores[i].UserId), scores[i].TimesPlayed);
}
message += Messages.BackToMap;
message += Messages.MetaTerminator;
return message;
}
public static string BuildMinigameRankingsForUser(User user)
{
string message = Messages.HighscoreHeaderMeta;
foreach(Highscore.HighscoreTableEntry highscore in user.Highscores.HighscoreList)
{
if (highscore.Type == "SCORE")
message += Messages.FormatHighscoreStat(highscore.GameName, Database.GetRanking(highscore.Score, highscore.GameName), highscore.Score, highscore.TimesPlayed);
else if(highscore.Type == "TIME")
message += Messages.FormatBestTimeStat(highscore.GameName, Database.GetRanking(highscore.Score, highscore.GameName), highscore.Score, highscore.TimesPlayed);
}
message += Messages.BackToMap;
message += Messages.MetaTerminator;
return message;
}
public static string BuildPrivateNotes(User user)
{
string message = "";

View file

@ -1,31 +1,106 @@
using HISP.Server;
using System.Collections.Generic;
namespace HISP.Player
{
class Highscore
{
public static bool RegisterHighscore(int playerId, string gameTitle, int score, bool time)
public class HighscoreTableEntry
{
public int UserId;
public string GameName;
public int Wins;
public int Looses;
public int TimesPlayed;
public int Score;
public string Type;
}
public HighscoreTableEntry[] HighscoreList
{
get
{
return highScoreList.ToArray();
}
}
private User baseUser;
private List<HighscoreTableEntry> highScoreList = new List<HighscoreTableEntry>();
public Highscore(User user)
{
baseUser = user;
HighscoreTableEntry[] highscores = Database.GetPlayerHighScores(user.Id);
foreach (HighscoreTableEntry highscore in highscores)
highScoreList.Add(highscore);
}
public HighscoreTableEntry GetHighscore(string gameTitle)
{
foreach (HighscoreTableEntry highscore in HighscoreList)
{
if (highscore.GameName == gameTitle)
{
return highscore;
}
}
throw new KeyNotFoundException("Highscore for " + gameTitle + " Not found.");
}
public bool HasHighscore(string gameTitle)
{
foreach(HighscoreTableEntry highscore in HighscoreList)
{
if(highscore.GameName == gameTitle)
{
return true;
}
}
return false;
}
public bool UpdateHighscore(string gameTitle, int score, bool time)
{
bool isNewScore = true;
if (!Database.PlayerHasHighscore(playerId, gameTitle))
if (!HasHighscore(gameTitle))
{
Database.AddNewHighscore(playerId, gameTitle, score, time ? "TIME" : "SCORE");
string type = time ? "TIME" : "SCORE";
Database.AddNewHighscore(baseUser.Id, gameTitle, score, type);
HighscoreTableEntry newHighscore = new HighscoreTableEntry();
newHighscore.UserId = baseUser.Id;
newHighscore.GameName = gameTitle;
newHighscore.Wins = 0;
newHighscore.Looses = 0;
newHighscore.TimesPlayed = 1;
newHighscore.Score = score;
newHighscore.Type = type;
highScoreList.Add(newHighscore);
return isNewScore;
}
else
{
int currentScore = Database.GetHighscore(playerId, gameTitle);
int currentScore = GetHighscore(gameTitle).Score;
if (score < currentScore)
{
score = currentScore;
isNewScore = false;
}
Database.UpdateHighscore(playerId, gameTitle, score);
Database.UpdateHighscore(baseUser.Id, gameTitle, score);
for(int i = 0; i < highScoreList.Count; i++)
{
if(highScoreList[i].GameName == gameTitle)
{
highScoreList[i].TimesPlayed += 1;
highScoreList[i].Score = score;
}
}
return isNewScore;
}
}
}
}

View file

@ -39,6 +39,7 @@ namespace HISP.Player
public Npc.NpcEntry LastTalkedToNpc;
public Shop LastShoppedAt;
public PlayerQuests Quests;
public Highscore Highscores;
public int FreeMinutes
{
get
@ -372,7 +373,7 @@ namespace HISP.Player
Gender = Database.GetGender(UserId);
MailBox = new Mailbox(this);
Highscores = new Highscore(this);
// Generate SecCodes

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using MySqlConnector;
using HISP.Game;
using HISP.Player;
namespace HISP.Server
{
@ -1688,22 +1689,6 @@ namespace HISP.Server
}
}
public static bool PlayerHasHighscore(int playerId, string gameTitle)
{
using (MySqlConnection db = new MySqlConnection(ConnectionString))
{
db.Open();
MySqlCommand sqlCommand = db.CreateCommand();
sqlCommand.CommandText = "SELECT COUNT(1) FROM Leaderboards WHERE playerId=@playerId AND minigame=@gameTitle";
sqlCommand.Parameters.AddWithValue("@playerId", playerId);
sqlCommand.Parameters.AddWithValue("@gameTitle", gameTitle);
sqlCommand.Prepare();
int count = Convert.ToInt32(sqlCommand.ExecuteScalar());
sqlCommand.Dispose();
return count >= 1;
}
}
public static void AddNewHighscore(int playerId, string gameTitle, int score, string type)
{
using (MySqlConnection db = new MySqlConnection(ConnectionString))
@ -1724,20 +1709,90 @@ namespace HISP.Server
}
}
public static int GetHighscore(int playerId, string gameTitle)
public static Highscore.HighscoreTableEntry[] GetPlayerHighScores(int playerId)
{
List<Highscore.HighscoreTableEntry> entires = new List<Highscore.HighscoreTableEntry>();
using (MySqlConnection db = new MySqlConnection(ConnectionString))
{
db.Open();
MySqlCommand sqlCommand = db.CreateCommand();
sqlCommand.CommandText = "SELECT score FROM Leaderboards WHERE playerId=@playerId AND minigame=@gameTitle";
sqlCommand.CommandText = "SELECT * FROM leaderboards WHERE playerId=@playerId ORDER BY score ASC";
sqlCommand.Parameters.AddWithValue("@playerId", playerId);
sqlCommand.Parameters.AddWithValue("@gameTitle", gameTitle);
sqlCommand.Prepare();
int score = Convert.ToInt32(sqlCommand.ExecuteScalar());
MySqlDataReader reader = sqlCommand.ExecuteReader();
while (reader.Read())
{
Highscore.HighscoreTableEntry highscoreEntry = new Highscore.HighscoreTableEntry();
highscoreEntry.UserId = reader.GetInt32(0);
highscoreEntry.GameName = reader.GetString(1);
highscoreEntry.Wins = reader.GetInt32(2);
highscoreEntry.Looses = reader.GetInt32(3);
highscoreEntry.TimesPlayed = reader.GetInt32(4);
highscoreEntry.Score = reader.GetInt32(5);
highscoreEntry.Type = reader.GetString(6);
entires.Add(highscoreEntry);
}
sqlCommand.Dispose();
return score;
return entires.ToArray();
}
}
public static Highscore.HighscoreTableEntry[] GetTopScores(string gameTitle, int limit)
{
List<Highscore.HighscoreTableEntry> entires = new List<Highscore.HighscoreTableEntry>();
using (MySqlConnection db = new MySqlConnection(ConnectionString))
{
db.Open();
MySqlCommand sqlCommand = db.CreateCommand();
sqlCommand.CommandText = "SELECT * FROM leaderboards WHERE minigame=@gameTitle ORDER BY score ASC LIMIT @limit";
sqlCommand.Parameters.AddWithValue("@gameTitle", gameTitle);
sqlCommand.Parameters.AddWithValue("@limit", limit);
sqlCommand.Prepare();
MySqlDataReader reader = sqlCommand.ExecuteReader();
while(reader.Read())
{
Highscore.HighscoreTableEntry highscoreEntry = new Highscore.HighscoreTableEntry();
highscoreEntry.UserId = reader.GetInt32(0);
highscoreEntry.GameName = gameTitle;
highscoreEntry.Wins = reader.GetInt32(2);
highscoreEntry.Looses = reader.GetInt32(3);
highscoreEntry.TimesPlayed = reader.GetInt32(4);
highscoreEntry.Score = reader.GetInt32(5);
highscoreEntry.Type = reader.GetString(6);
entires.Add(highscoreEntry);
}
sqlCommand.Dispose();
return entires.ToArray();
}
}
public static int GetRanking(int score, string gameTitle)
{
using (MySqlConnection db = new MySqlConnection(ConnectionString))
{
db.Open();
MySqlCommand sqlCommand = db.CreateCommand();
sqlCommand.CommandText = "SELECT DISTINCT score FROM leaderboards WHERE minigame=@gameTitle ORDER BY score ASC";
sqlCommand.Parameters.AddWithValue("@gameTitle", gameTitle);
sqlCommand.Prepare();
MySqlDataReader reader = sqlCommand.ExecuteReader();
int i = 1;
while(reader.Read())
{
if (reader.GetInt32(0) == score)
break;
i++;
}
sqlCommand.Dispose();
return i;
}
}
public static void UpdateHighscore(int playerId, string gameTitle, int score)

View file

@ -550,6 +550,17 @@ namespace HISP.Server
Messages.NoPitchforkMeta = gameData.messages.meta.hay_pile.no_pitchfork;
Messages.HasPitchforkMeta = gameData.messages.meta.hay_pile.pitchfork;
// Highscore
Messages.HighscoreHeaderMeta = gameData.messages.meta.highscores.header_meta;
Messages.HighscoreFormat = gameData.messages.meta.highscores.highscore_format;
Messages.BestTimeFormat = gameData.messages.meta.highscores.besttime_format;
Messages.GameHighScoreHeaderFormat = gameData.messages.meta.highscores.game_highscore_header;
Messages.GameHighScoreFormat = gameData.messages.meta.highscores.game_highscore_format;
Messages.GameBestTimeHeaderFormat = gameData.messages.meta.highscores.game_besttime_header;
Messages.GameBestTimeFormat = gameData.messages.meta.highscores.game_besttime_format;
// Sec Codes

View file

@ -158,6 +158,10 @@ namespace HISP.Server
metaPacket = PacketBuilder.CreateMetaPacket(Meta.BuildPrivateNotes(sender.LoggedinUser));
sender.SendPacket(metaPacket);
break;
case 20:
metaPacket = PacketBuilder.CreateMetaPacket(Meta.BuildMinigameRankingsForUser(sender.LoggedinUser));
sender.SendPacket(metaPacket);
break;
default:
Logger.ErrorPrint("Dynamic button #" + buttonId + " unknown...");
break;
@ -349,7 +353,7 @@ namespace HISP.Server
return;
}
bool newHighscore = Highscore.RegisterHighscore(sender.LoggedinUser.Id, gameTitle, value, time);
bool newHighscore = sender.LoggedinUser.Highscores.UpdateHighscore(gameTitle, value, time);
if (newHighscore && !time)
{
byte[] chatPacket = PacketBuilder.CreateChat(Messages.FormatHighscoreBeatenMessage(value), PacketBuilder.CHAT_BOTTOM_RIGHT);
@ -530,7 +534,21 @@ namespace HISP.Server
return;
}
}
else if(method == PacketBuilder.PLAYERINFO_HIGHSCORES_LIST)
{
string packetStr = Encoding.UTF8.GetString(packet);
string gameName = packetStr.Substring(2, packetStr.Length - 4);
byte[] metaTag = PacketBuilder.CreateMetaPacket(Meta.BuildTopHighscores(gameName));
sender.SendPacket(metaTag);
}
else if (method == PacketBuilder.PLAYERINFO_BESTTIMES_LIST)
{
string packetStr = Encoding.UTF8.GetString(packet);
string gameName = packetStr.Substring(2, packetStr.Length - 4);
byte[] metaTag = PacketBuilder.CreateMetaPacket(Meta.BuildTopTimes(gameName));
sender.SendPacket(metaTag);
}
}
public static void OnMovementPacket(GameClient sender, byte[] packet)
@ -1728,7 +1746,7 @@ namespace HISP.Server
byte[] loginMessageBytes = PacketBuilder.CreateChat(Messages.FormatLoginMessage(sender.LoggedinUser.Username), PacketBuilder.CHAT_BOTTOM_LEFT);
foreach (GameClient client in ConnectedClients)
if (client.LoggedIn)
if (!client.LoggedinUser.MuteLogins || client.LoggedinUser.MuteAll)
if (!client.LoggedinUser.MuteLogins && !client.LoggedinUser.MuteAll)
if (client.LoggedinUser.Id != userId)
client.SendPacket(loginMessageBytes);
@ -1747,7 +1765,6 @@ namespace HISP.Server
public static void OnDisconnect(GameClient sender)
{
connectedClients.Remove(sender);
Logger.DebugPrint("owoo disconnect");
if (sender.LoggedIn)
{
Database.RemoveOnlineUser(sender.LoggedinUser.Id);
@ -1755,7 +1772,7 @@ namespace HISP.Server
byte[] logoutMessageBytes = PacketBuilder.CreateChat(Messages.FormatLogoutMessage(sender.LoggedinUser.Username), PacketBuilder.CHAT_BOTTOM_LEFT);
foreach (GameClient client in ConnectedClients)
if (client.LoggedIn)
if (!client.LoggedinUser.MuteLogins)
if (!client.LoggedinUser.MuteLogins && !client.LoggedinUser.MuteAll)
if (client.LoggedinUser.Id != sender.LoggedinUser.Id)
client.SendPacket(logoutMessageBytes);
// Tell clients of diconnect (remove from chat)

View file

@ -40,18 +40,22 @@ namespace HISP.Server
public const byte PACKET_PLAYERINFO = 0x16;
public const byte PACKET_INFORMATION = 0x28;
public const byte SECCODE_QUEST = 0x32;
public const byte SECCODE_ITEM = 0x28;
public const byte SECCODE_SCORE = 0x3D;
public const byte SECCODE_TIME = 0x3E;
public const byte SECCODE_MONEY = 0x1E;
public const byte SECCODE_MONEY = 0x1E;
public const byte NPC_START_CHAT = 0x14;
public const byte NPC_CONTINUE_CHAT = 0x15;
public const byte PLAYERINFO_LEAVE = 0x16;
public const byte PLAYERINFO_UPDATE_OR_CREATE = 0x15;
public const byte PLAYERINFO_HIGHSCORES_LIST = 0x51;
public const byte PLAYERINFO_BESTTIMES_LIST = 0x52;
public const byte VIEW_PROFILE = 0x14;
public const byte SAVE_PROFILE = 0x15;