diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 16183431..84610102 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -629,6 +629,24 @@ namespace TShockAPI { HelpText = GetString("Shows the server's rules.") }); + add(new Command(ShowDeath, "death") + { + HelpText = GetString("Shows your number of deaths."), + AllowServer = false + }); + add(new Command(ShowPVPDeath, "pvpdeath") + { + HelpText = GetString("Shows your number of PVP deaths."), + AllowServer = false + }); + add(new Command(ShowAllDeath, "alldeath") + { + HelpText = GetString("Shows the number of deaths for all online players."), + }); + add(new Command(ShowAllPVPDeath, "allpvpdeath") + { + HelpText = GetString("Shows the number of PVP deaths for all online players."), + }); TShockCommands = new ReadOnlyCollection(tshockCommands); } @@ -5815,6 +5833,49 @@ namespace TShockAPI return; } + private static void ShowDeath(CommandArgs args) + { + args.Player.SendErrorMessage(GetString($"*You were slain {args.Player.DeathsPVE} times.")); + } + + private static void ShowPVPDeath(CommandArgs args) + { + args.Player.SendErrorMessage(GetString($"*You were slain by other players {args.Player.DeathsPVP} times.")); + } + + private static void ShowAllDeath(CommandArgs args) + { + if (TShock.Utils.GetActivePlayerCount() == 0) + { + args.Player.SendErrorMessage(GetString("There are currently no players online.")); + return; + } + + var deathsRank = TShock.Players + .Where(p => p is { Active: true }) + .OrderByDescending(x => x.DeathsPVE) + .Select(x => $"*{x.Name} was slain {x.DeathsPVE} times."); + + args.Player.SendErrorMessage(string.Join('\n',deathsRank)); + } + + private static void ShowAllPVPDeath(CommandArgs args) + { + if (TShock.Utils.GetActivePlayerCount() == 0) + { + args.Player.SendErrorMessage(GetString("There are currently no players online.")); + return; + } + + var deathsRank = TShock.Players + .Where(p => p is { Active: true }) + .OrderByDescending(x => x.DeathsPVP) + .Select(x => $"*{x.Name} was slain by other players {x.DeathsPVP} times."); + + args.Player.SendErrorMessage(string.Join('\n',deathsRank)); + } + + #endregion General Commands #region Game Commands diff --git a/TShockAPI/DB/CharacterManager.cs b/TShockAPI/DB/CharacterManager.cs index 3b9890a3..db14488c 100644 --- a/TShockAPI/DB/CharacterManager.cs +++ b/TShockAPI/DB/CharacterManager.cs @@ -69,7 +69,9 @@ namespace TShockAPI.DB new SqlColumn("usedGummyWorm", MySqlDbType.Int32), new SqlColumn("usedAmbrosia", MySqlDbType.Int32), new SqlColumn("unlockedSuperCart", MySqlDbType.Int32), - new SqlColumn("enabledSuperCart", MySqlDbType.Int32) + new SqlColumn("enabledSuperCart", MySqlDbType.Int32), + new SqlColumn("deathsPVE", MySqlDbType.Int32), + new SqlColumn("deathsPVP", MySqlDbType.Int32) ); SqlTableCreator creator = new(db, db.GetSqlQueryBuilder()); @@ -132,6 +134,8 @@ namespace TShockAPI.DB playerData.usedAmbrosia = reader.Get("usedAmbrosia"); playerData.unlockedSuperCart = reader.Get("unlockedSuperCart"); playerData.enabledSuperCart = reader.Get("enabledSuperCart"); + playerData.deathsPVE = reader.Get("deathsPVE"); + playerData.deathsPVP = reader.Get("deathsPVP"); return playerData; } } @@ -200,8 +204,8 @@ namespace TShockAPI.DB try { database.Query( - "INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted, usingBiomeTorches, happyFunTorchTime, unlockedBiomeTorches, currentLoadoutIndex,ateArtisanBread, usedAegisCrystal, usedAegisFruit, usedArcaneCrystal, usedGalaxyPearl, usedGummyWorm, usedAmbrosia, unlockedSuperCart, enabledSuperCart) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29, @30, @31, @32, @33);", - player.Account.ID, playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, string.Join("~", playerData.inventory), playerData.extraSlot, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.skinVariant, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor),TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisibleAccessory), TShock.Utils.EncodeColor(player.TPlayer.skinColor),TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.UsingBiomeTorches ? 1 : 0, player.TPlayer.happyFunTorchTime ? 1 : 0, player.TPlayer.unlockedBiomeTorches ? 1 : 0, player.TPlayer.CurrentLoadoutIndex, player.TPlayer.ateArtisanBread ? 1 : 0, player.TPlayer.usedAegisCrystal ? 1 : 0, player.TPlayer.usedAegisFruit ? 1 : 0, player.TPlayer.usedArcaneCrystal ? 1 : 0, player.TPlayer.usedGalaxyPearl ? 1 : 0, player.TPlayer.usedGummyWorm ? 1 : 0, player.TPlayer.usedAmbrosia ? 1 : 0, player.TPlayer.unlockedSuperCart ? 1 : 0, player.TPlayer.enabledSuperCart ? 1 : 0); + "INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted, usingBiomeTorches, happyFunTorchTime, unlockedBiomeTorches, currentLoadoutIndex,ateArtisanBread, usedAegisCrystal, usedAegisFruit, usedArcaneCrystal, usedGalaxyPearl, usedGummyWorm, usedAmbrosia, unlockedSuperCart, enabledSuperCart, deathsPVE, deathsPVP) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29, @30, @31, @32, @33, @34, @35);", + player.Account.ID, playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, string.Join("~", playerData.inventory), playerData.extraSlot, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.skinVariant, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor),TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisibleAccessory), TShock.Utils.EncodeColor(player.TPlayer.skinColor),TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.UsingBiomeTorches ? 1 : 0, player.TPlayer.happyFunTorchTime ? 1 : 0, player.TPlayer.unlockedBiomeTorches ? 1 : 0, player.TPlayer.CurrentLoadoutIndex, player.TPlayer.ateArtisanBread ? 1 : 0, player.TPlayer.usedAegisCrystal ? 1 : 0, player.TPlayer.usedAegisFruit ? 1 : 0, player.TPlayer.usedArcaneCrystal ? 1 : 0, player.TPlayer.usedGalaxyPearl ? 1 : 0, player.TPlayer.usedGummyWorm ? 1 : 0, player.TPlayer.usedAmbrosia ? 1 : 0, player.TPlayer.unlockedSuperCart ? 1 : 0, player.TPlayer.enabledSuperCart ? 1 : 0, player.sscDeathsPVE, player.sscDeathsPVP); return true; } catch (Exception ex) @@ -214,8 +218,8 @@ namespace TShockAPI.DB try { database.Query( - "UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7, hair = @8, hairDye = @9, hairColor = @10, pantsColor = @11, shirtColor = @12, underShirtColor = @13, shoeColor = @14, hideVisuals = @15, skinColor = @16, eyeColor = @17, questsCompleted = @18, skinVariant = @19, extraSlot = @20, usingBiomeTorches = @21, happyFunTorchTime = @22, unlockedBiomeTorches = @23, currentLoadoutIndex = @24, ateArtisanBread = @25, usedAegisCrystal = @26, usedAegisFruit = @27, usedArcaneCrystal = @28, usedGalaxyPearl = @29, usedGummyWorm = @30, usedAmbrosia = @31, unlockedSuperCart = @32, enabledSuperCart = @33 WHERE Account = @5;", - playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, string.Join("~", playerData.inventory), player.Account.ID, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor), TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisibleAccessory), TShock.Utils.EncodeColor(player.TPlayer.skinColor), TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.skinVariant, player.TPlayer.extraAccessory ? 1 : 0, player.TPlayer.UsingBiomeTorches ? 1 : 0, player.TPlayer.happyFunTorchTime ? 1 : 0, player.TPlayer.unlockedBiomeTorches ? 1 : 0, player.TPlayer.CurrentLoadoutIndex, player.TPlayer.ateArtisanBread ? 1 : 0, player.TPlayer.usedAegisCrystal ? 1 : 0, player.TPlayer.usedAegisFruit ? 1 : 0, player.TPlayer.usedArcaneCrystal ? 1 : 0, player.TPlayer.usedGalaxyPearl ? 1 : 0, player.TPlayer.usedGummyWorm ? 1 : 0, player.TPlayer.usedAmbrosia ? 1 : 0, player.TPlayer.unlockedSuperCart ? 1 : 0, player.TPlayer.enabledSuperCart ? 1 : 0); + "UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7, hair = @8, hairDye = @9, hairColor = @10, pantsColor = @11, shirtColor = @12, underShirtColor = @13, shoeColor = @14, hideVisuals = @15, skinColor = @16, eyeColor = @17, questsCompleted = @18, skinVariant = @19, extraSlot = @20, usingBiomeTorches = @21, happyFunTorchTime = @22, unlockedBiomeTorches = @23, currentLoadoutIndex = @24, ateArtisanBread = @25, usedAegisCrystal = @26, usedAegisFruit = @27, usedArcaneCrystal = @28, usedGalaxyPearl = @29, usedGummyWorm = @30, usedAmbrosia = @31, unlockedSuperCart = @32, enabledSuperCart = @33, deathsPVE = @34, deathsPVP = @35 WHERE Account = @5;", + playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, string.Join("~", playerData.inventory), player.Account.ID, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor), TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisibleAccessory), TShock.Utils.EncodeColor(player.TPlayer.skinColor), TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.skinVariant, player.TPlayer.extraAccessory ? 1 : 0, player.TPlayer.UsingBiomeTorches ? 1 : 0, player.TPlayer.happyFunTorchTime ? 1 : 0, player.TPlayer.unlockedBiomeTorches ? 1 : 0, player.TPlayer.CurrentLoadoutIndex, player.TPlayer.ateArtisanBread ? 1 : 0, player.TPlayer.usedAegisCrystal ? 1 : 0, player.TPlayer.usedAegisFruit ? 1 : 0, player.TPlayer.usedArcaneCrystal ? 1 : 0, player.TPlayer.usedGalaxyPearl ? 1 : 0, player.TPlayer.usedGummyWorm ? 1 : 0, player.TPlayer.usedAmbrosia ? 1 : 0, player.TPlayer.unlockedSuperCart ? 1 : 0, player.TPlayer.enabledSuperCart ? 1 : 0, player.sscDeathsPVE, player.sscDeathsPVP); return true; } catch (Exception ex) @@ -270,7 +274,7 @@ namespace TShockAPI.DB try { database.Query( - "INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted, usingBiomeTorches, happyFunTorchTime, unlockedBiomeTorches, currentLoadoutIndex, ateArtisanBread, usedAegisCrystal, usedAegisFruit, usedArcaneCrystal, usedGalaxyPearl, usedGummyWorm, usedAmbrosia, unlockedSuperCart, enabledSuperCart) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29, @30, @31, @32, @33);", + "INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted, usingBiomeTorches, happyFunTorchTime, unlockedBiomeTorches, currentLoadoutIndex, ateArtisanBread, usedAegisCrystal, usedAegisFruit, usedArcaneCrystal, usedGalaxyPearl, usedGummyWorm, usedAmbrosia, unlockedSuperCart, enabledSuperCart) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29, @30, @31, @32, @33, @34, @35);", player.Account.ID, playerData.health, playerData.maxHealth, @@ -304,7 +308,10 @@ namespace TShockAPI.DB playerData.usedGummyWorm, playerData.usedAmbrosia, playerData.unlockedSuperCart, - playerData.enabledSuperCart); + playerData.enabledSuperCart, + playerData.deathsPVE, + playerData.deathsPVP + ); return true; } catch (Exception ex) @@ -317,7 +324,7 @@ namespace TShockAPI.DB try { database.Query( - "UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7, hair = @8, hairDye = @9, hairColor = @10, pantsColor = @11, shirtColor = @12, underShirtColor = @13, shoeColor = @14, hideVisuals = @15, skinColor = @16, eyeColor = @17, questsCompleted = @18, skinVariant = @19, extraSlot = @20, usingBiomeTorches = @21, happyFunTorchTime = @22, unlockedBiomeTorches = @23, currentLoadoutIndex = @24, ateArtisanBread = @25, usedAegisCrystal = @26, usedAegisFruit = @27, usedArcaneCrystal = @28, usedGalaxyPearl = @29, usedGummyWorm = @30, usedAmbrosia = @31, unlockedSuperCart = @32, enabledSuperCart = @33 WHERE Account = @5;", + "UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7, hair = @8, hairDye = @9, hairColor = @10, pantsColor = @11, shirtColor = @12, underShirtColor = @13, shoeColor = @14, hideVisuals = @15, skinColor = @16, eyeColor = @17, questsCompleted = @18, skinVariant = @19, extraSlot = @20, usingBiomeTorches = @21, happyFunTorchTime = @22, unlockedBiomeTorches = @23, currentLoadoutIndex = @24, ateArtisanBread = @25, usedAegisCrystal = @26, usedAegisFruit = @27, usedArcaneCrystal = @28, usedGalaxyPearl = @29, usedGummyWorm = @30, usedAmbrosia = @31, unlockedSuperCart = @32, enabledSuperCart = @33, deathsPVE = @34, deathsPVP = @35 WHERE Account = @5;", playerData.health, playerData.maxHealth, playerData.mana, @@ -351,7 +358,10 @@ namespace TShockAPI.DB playerData.usedGummyWorm, playerData.usedAmbrosia, playerData.unlockedSuperCart, - playerData.enabledSuperCart); + playerData.enabledSuperCart, + playerData.deathsPVE, + playerData.deathsPVP + ); return true; } catch (Exception ex) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 6ed0f8de..fc6cebfe 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -4325,6 +4325,15 @@ namespace TShockAPI args.Player.Dead = true; args.Player.RespawnTimer = TShock.Config.Settings.RespawnSeconds; + if (Main.ServerSideCharacter && !args.Player.HasPermission(Permissions.bypassssc)) + { + if (pvp) + { + args.Player.sscDeathsPVP++; + } + args.Player.sscDeathsPVE++; + } + foreach (NPC npc in Main.npc) { if (npc.active && (npc.boss || npc.type == 13 || npc.type == 14 || npc.type == 15) && diff --git a/TShockAPI/PlayerData.cs b/TShockAPI/PlayerData.cs index c84da7c7..1ab5430f 100644 --- a/TShockAPI/PlayerData.cs +++ b/TShockAPI/PlayerData.cs @@ -63,6 +63,8 @@ namespace TShockAPI public int usedAmbrosia; public int unlockedSuperCart; public int enabledSuperCart; + public int deathsPVE; + public int deathsPVP; /// /// Sets the default values for the inventory. @@ -152,6 +154,8 @@ namespace TShockAPI this.usedAmbrosia = player.TPlayer.usedAmbrosia ? 1 : 0; this.unlockedSuperCart = player.TPlayer.unlockedSuperCart ? 1 : 0; this.enabledSuperCart = player.TPlayer.enabledSuperCart ? 1 : 0; + this.deathsPVE = player.TPlayer.numberOfDeathsPVE; + this.deathsPVP = player.TPlayer.numberOfDeathsPVP; Item[] inventory = player.TPlayer.inventory; Item[] armor = player.TPlayer.armor; @@ -293,6 +297,8 @@ namespace TShockAPI player.TPlayer.usedAmbrosia = this.usedAmbrosia == 1; player.TPlayer.unlockedSuperCart = this.unlockedSuperCart == 1; player.TPlayer.enabledSuperCart = this.enabledSuperCart == 1; + player.sscDeathsPVE = this.deathsPVE; + player.sscDeathsPVP = this.deathsPVP; if (extraSlot != null) player.TPlayer.extraAccessory = extraSlot.Value == 1 ? true : false; diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index af009fa7..52d319fc 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -401,6 +401,48 @@ namespace TShockAPI /// Determines if the player has finished the handshake (Sent all necessary packets for connection, such as Request World Data, Spawn Player, etc). A normal client would do all of this no problem. public bool FinishedHandshake = false; + /// Server-side character's recorded death count. + public int sscDeathsPVE = 0; + + /// Server-side character's recorded PVP death count. + public int sscDeathsPVP = 0; + + /// + /// Gets the player's total death count. + /// If server-side characters are enabled and player doesn't have bypass permission, + /// returns the server-stored value; otherwise returns the client's value. + /// + public int DeathsPVE + { + get + { + if (Main.ServerSideCharacter && !this.HasPermission(Permissions.bypassssc)) + { + return sscDeathsPVE; + } + + return this.TPlayer.numberOfDeathsPVE; + } + } + + /// + /// Gets the player's total PVP death count. + /// If server-side characters are enabled and player doesn't have bypass permission, + /// returns the server-stored value; otherwise returns the client's value. + /// + public int DeathsPVP + { + get + { + if (Main.ServerSideCharacter && !this.HasPermission(Permissions.bypassssc)) + { + return sscDeathsPVP; + } + + return this.TPlayer.numberOfDeathsPVP; + } + } + /// Checks to see if active throttling is happening on events by Bouncer. Rejects repeated events by malicious clients in a short window. /// If the player is currently being throttled by Bouncer, or not. public bool IsBouncerThrottled()