diff --git a/Cache.cs b/Cache.cs index 5b12105..8a0c34c 100644 --- a/Cache.cs +++ b/Cache.cs @@ -22,7 +22,10 @@ public ModelCacheManager(IStorage storage) { this.storage = storage; } public void ResyncCache() { - cache = storage.GetAllPlayerModel(); + var data = storage.GetAllPlayerModel(); + if (data != null) { + cache = data; + } } public void SetAllTModels(string tmodel, bool permissionBypass) { diff --git a/Config.cs b/Config.cs index 1d26291..92c1c38 100644 --- a/Config.cs +++ b/Config.cs @@ -29,10 +29,14 @@ public class ModelConfig : BasePluginConfig [JsonPropertyName("MySQL_Table")] public string MySQLTable { get; set; } = "playermodelchanger"; [JsonPropertyName("ModelForBots")] public BotsConfig ModelForBots { get; set; } = new BotsConfig(); + [JsonPropertyName("ModelChangeCooldownSecond")] public float ModelChangeCooldownSecond { get; set; } = 0f; + [JsonPropertyName("DisableInstantUpdate")] public bool DisableInstantUpdate { get; set; } = false; + [JsonPropertyName("DisableThirdPersonPreview")] public bool DisableThirdPersonPreview { get; set; } = false; [JsonPropertyName("DisablePrecache")] public bool DisablePrecache { get; set; } = false; [JsonPropertyName("DisableRandomModel")] public bool DisableRandomModel { get; set; } = false; [JsonPropertyName("DisableAutoCheck")] public bool DisableAutoCheck { get; set; } = false; [JsonPropertyName("DisablePlayerSelection")] public bool DisablePlayerSelection { get; set; } = false; [JsonPropertyName("AutoResyncCache")] public bool AutoResyncCache { get; set; } = false; + [JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 1; } diff --git a/PlayerModelChanger.cs b/PlayerModelChanger.cs index 1ccf170..699572a 100644 --- a/PlayerModelChanger.cs +++ b/PlayerModelChanger.cs @@ -8,12 +8,14 @@ using Service; using System.Drawing; using CounterStrikeSharp.API.Modules.Config; +using System.Reflection; +using System.Text.Json; namespace PlayerModelChanger; public partial class PlayerModelChanger : BasePlugin, IPluginConfig { public override string ModuleName => "Player Model Changer"; - public override string ModuleVersion => "1.6.1"; + public override string ModuleVersion => "1.7.0"; public override string ModuleAuthor => "samyyc"; public required ModelConfig Config { get; set; } @@ -21,10 +23,13 @@ public partial class PlayerModelChanger : BasePlugin, IPluginConfig public required DefaultModelManager DefaultModelManager { get;set; } + public static PlayerModelChanger? INSTANCE; + public bool Enable = true; public override void Load(bool hotReload) { + INSTANCE = this; IStorage? Storage = null; switch (Config.StorageType) { case "sqlite": @@ -43,7 +48,7 @@ public override void Load(bool hotReload) RegisterListener(PrecacheResource); } RegisterEventHandler(OnPlayerSpawnEvent); - RegisterListener(() => Unload(true)); + RegisterListener(OnTick); Console.WriteLine($"Player Model Changer loaded {Service.GetModelCount()} model(s) successfully."); } @@ -56,10 +61,12 @@ private void PrecacheResource(ResourceManifest manifest) { } } - public override void Unload(bool hotReload) - { - RemoveListener(PrecacheResource); - DeregisterEventHandler(OnPlayerSpawnEvent); + public override void Unload(bool hotReload) { + INSTANCE = null; + RemoveListener(PrecacheResource); + RemoveListener(OnTick); + DeregisterEventHandler(OnPlayerSpawnEvent); + Console.WriteLine("Player Model Changer unloaded successfully."); } public void ReloadConfig() { @@ -113,10 +120,13 @@ public void OnConfigParsed(ModelConfig config) Config = config; } + public void OnTick() { + ThirdPerson.UpdateCamera(); + } + // from https://github.com/Challengermode/cm-cs2-defaultskins/ [GameEventHandler] public HookResult OnPlayerSpawnEvent(EventPlayerSpawn @event, GameEventInfo info) { - if (!Enable) { return HookResult.Continue; } @@ -148,7 +158,7 @@ public HookResult OnPlayerSpawnEvent(EventPlayerSpawn @event, GameEventInfo info } var botmodel = Service.GetModel(modelindex); if (botmodel != null) { - SetModelNextServerFrame(player.Pawn.Value, botmodel.path, botmodel.disableleg); + SetModelNextServerFrame(player, botmodel.path, botmodel.disableleg); } else { Server.NextFrame(() => { var originalRender = player.Pawn.Value.Render; @@ -190,7 +200,7 @@ public HookResult OnPlayerSpawnEvent(EventPlayerSpawn @event, GameEventInfo info var model = Service.GetPlayerNowTeamModel(player); if (model != null) { - SetModelNextServerFrame(player.PlayerPawn.Value, model.path, model.disableleg); + SetModelNextServerFrame(player, model.path, model.disableleg); } else { Server.NextFrame(() => { var originalRender = player.PlayerPawn.Value.Render; @@ -206,10 +216,10 @@ public HookResult OnPlayerSpawnEvent(EventPlayerSpawn @event, GameEventInfo info return HookResult.Continue; } - public static void SetModelNextServerFrame(CBasePlayerPawn pawn, string model, bool disableleg) + public void SetModelNextServerFrame(CCSPlayerController player, string model, bool disableleg) { - Server.NextFrame(() => - { + Server.NextFrame(() => { + var pawn = player.Pawn.Value!; pawn.SetModel(model); var originalRender = pawn.Render; pawn.Render = Color.FromArgb(disableleg ? 254 : 255, originalRender.R, originalRender.G, originalRender.B); diff --git a/README.md b/README.md index 97b9534..dea214b 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ If you like this plugin, please give a star :) ### This plugin can cause a GSLT ban, please use at your own risk. [中文教程请点这里](https://github.com/samyycX/CS2-PlayerModelChanger/blob/master/README_CN.md) - **[Before you use](#before-you-use)** +- [Feature](#feature) - [Installation Guide](#installation-guide) - [Optional Dependencies](#optional-dependencies) - [Commands](#commands) @@ -21,6 +22,16 @@ Custom model parts: ## Before you use 1. **this plugin can cause a GSLT ban, use at your own risk** +## Feature +- a model select menu (support wasd menu) +- can set different model for T and CT +- random model +- update model after change instantly +- thirdperson preview +- can set default model +- can provide special models for specified permission or player +- can disable the display of leg model + ## Installation Guide Download the plugin from latest [Release](https://github.com/samyycX/CS2-PlayerModelChanger/releases), then put it into your counterstrikesharp plugin folder. @@ -69,6 +80,7 @@ See the [Configuration](#configuration) ## Credits - Method to change model: [DefaultSkins](https://github.com/Challengermode/cm-cs2-defaultskins) by ChallengerMode +- Thirdperson preview code: [ThirdPerson-WIP](https://github.com/UgurhanK/ThirdPerson-WIP) by UgurhanK ## TODOs 1. Translation diff --git a/README_CN.md b/README_CN.md index 1a97c24..26f8018 100644 --- a/README_CN.md +++ b/README_CN.md @@ -4,6 +4,7 @@ 如果你喜欢这个插件请给个Star :) ### 此插件可能导致GSLT封禁,请自行承担风险 - **[用前须知](#用前须知)** +- [功能](#功能) - [自定义模型依赖插件](#自定义模型依赖插件) - [安装指南](#安装指南) - [命令](#命令) @@ -17,10 +18,19 @@ - [如何添加原版或创意工坊模型](#如何添加原版或创意工坊模型) - [如何把你的模型打包并上传到创意工坊](#如何把你的模型打包并上传到创意工坊) - ## 用前须知 1. **此插件可能导致GSLT封禁,请自行承担风险** +## 功能 +- 菜单式选择模型 (支持wasd菜单) +- 可为T/CT阵营设置不同模型 +- 随机模型 +- 选择后可立刻更新 +- 第三人称预览 +- 可设置默认模型 +- 可按照权限和玩家设置特殊模型 +- 可禁用腿部模型 + ## 自定义模型依赖插件 **如果你不使用自定义模型,不用安装这些插件** 1. [MultiAddonManager](https://github.com/Source2ZE/MultiAddonManager) @@ -70,7 +80,8 @@ 请看 [配置](#配置) 部分 ## 感谢 -- 替换模型的方法: [DefaultSkins](https://github.com/Challengermode/cm-cs2-defaultskins), 作者 ChallengerMode +- 替换模型的方法: [DefaultSkins](https://github.com/Challengermode/cm-cs2-defaultskins) by ChallengerMode +- 第三人称预览代码: [ThirdPerson-WIP](https://github.com/UgurhanK/ThirdPerson-WIP) by UgurhanK ## TODOs 1. 翻译 diff --git a/Service.cs b/Service.cs index 5be999e..af5f3f3 100644 --- a/Service.cs +++ b/Service.cs @@ -1,3 +1,5 @@ +using System.Drawing; +using System.Net.Http.Headers; using Config; using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; @@ -21,6 +23,9 @@ public class ModelService { private ModelCacheManager cacheManager; + + private Dictionary ModelChangeCooldown = new Dictionary(); + public ModelService(ModelConfig Config, IStorage storage, IStringLocalizer localizer, DefaultModelManager defaultModelManager) { this.config = Config; this.storage = storage; @@ -85,6 +90,12 @@ public void SetPlayerModel(ulong steamid, string? modelIndex, string side, bool storage.SetPlayerCTModel(steamid, modelIndex, permissionBypass); } ); + if (!config.DisableInstantUpdate) { + var player = Utilities.GetPlayerFromSteamId(steamid); + if (Utils.isUpdatingSameTeam(player, side)) { + Utils.RespawnPlayer(player, config.DisableThirdPersonPreview); + } + } } public void SetPlayerAllModel(ulong steamid, string? tModel, string? ctModel, bool permissionBypass) { @@ -95,6 +106,10 @@ public void SetPlayerAllModel(ulong steamid, string? tModel, string? ctModel, bo storage.SetPlayerTModel(steamid, tModel, permissionBypass).ContinueWith((_) => { storage.SetPlayerCTModel(steamid, ctModel, permissionBypass); }); + if (!config.DisableInstantUpdate) { + var player = Utilities.GetPlayerFromSteamId(steamid); + Utils.RespawnPlayer(player, config.DisableThirdPersonPreview); + } } public void SetPlayerModel(ulong steamid, string? modelIndex, CsTeam team, bool permissionBypass) { var side = team == CsTeam.Terrorist ? "t" : "ct"; @@ -176,7 +191,6 @@ public bool CheckModel(CCSPlayerController player, string side, ModelCache? mode if (modelIndex == "" || modelIndex == "@random") { return true; } - CsTeam team = side.ToLower() == "t" ? CsTeam.Terrorist : CsTeam.CounterTerrorist; var model = GetModel(modelIndex!); if (model == null) { @@ -186,6 +200,14 @@ public bool CheckModel(CCSPlayerController player, string side, ModelCache? mode } public void SetPlayerModelWithCheck(CCSPlayerController player, string modelIndex, string side) { + if (ModelChangeCooldown.ContainsKey(player.SteamID)) { + var lastTime = ModelChangeCooldown[player.SteamID]; + if (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - lastTime < (config.ModelChangeCooldownSecond*1000)) { + player.PrintToChat(localizer["command.model.cooldown"]); + return; + } + } + var isSpecial = modelIndex == "" || modelIndex == "@random"; if (modelIndex == "@default") { @@ -196,7 +218,9 @@ public void SetPlayerModelWithCheck(CCSPlayerController player, string modelInde () => SetPlayerModel(player!.AuthorizedSteamID!.SteamId64, tDefault == null ? "" : tDefault.index, side, false), () => SetPlayerModel(player!.AuthorizedSteamID!.SteamId64, ctDefault == null ? "" : ctDefault.index, side, false) ); - player.PrintToChat(localizer["command.model.success", localizer["side."+side]]); + if (config.DisableInstantUpdate || !Utils.isUpdatingSameTeam(player, side)) { + player.PrintToChat(localizer["command.model.success", localizer["side."+side]]); + } return; } @@ -221,7 +245,10 @@ public void SetPlayerModelWithCheck(CCSPlayerController player, string modelInde var steamid = player!.AuthorizedSteamID!.SteamId64; SetPlayerModel(steamid, modelIndex, side, false); - player.PrintToChat(localizer["command.model.success", localizer["side."+side]]); + ModelChangeCooldown[steamid] = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + if (config.DisableInstantUpdate || !Utils.isUpdatingSameTeam(player, side)) { + player.PrintToChat(localizer["command.model.success", localizer["side."+side]]); + } } public Model? GetPlayerModel(CCSPlayerController player, string side) { diff --git a/Storage.cs b/Storage.cs index 9d2aa7a..2d02a8b 100644 --- a/Storage.cs +++ b/Storage.cs @@ -5,7 +5,7 @@ namespace Storage; public interface IStorage { public string? GetPlayerTModel(ulong SteamID); public string? GetPlayerCTModel(ulong SteamID); - public List GetAllPlayerModel(); + public List? GetAllPlayerModel(); public Task SetPlayerTModel(ulong SteamID, string modelName, bool permissionBypass); public Task SetPlayerCTModel(ulong SteamID, string modelName, bool permissionBypass); diff --git a/ThirdPerson.cs b/ThirdPerson.cs new file mode 100644 index 0000000..3bfad1d --- /dev/null +++ b/ThirdPerson.cs @@ -0,0 +1,110 @@ +using System.Drawing; +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities.Constants; +using CounterStrikeSharp.API.Modules.Utils; + +namespace PlayerModelChanger; + +public class ThirdPerson { + + private static List cameraStatuses = new List(); + const int MAX_ROTATION_TIMES = 300; + const float DISTANCE = 80; + const float Z_DISTANCE = 80; + + class CameraStatus { + public CCSPlayerController Player { get; set; } + public CPhysicsPropMultiplayer CameraProp { get; set; } + public float Times { get; set; } + } + + public static Vector CalculatePositionInFront(CCSPlayerController player, float offSetXY, float offSetZ = 0) + { + var pawn = player.PlayerPawn.Value; + // Extract yaw angle from player's rotation QAngle + float yawAngle = pawn!.EyeAngles!.Y; + + // Convert yaw angle from degrees to radians + float yawAngleRadians = (float)(yawAngle * Math.PI / 180.0); + + // Calculate offsets in x and y directions + float offsetX = offSetXY * (float)Math.Cos(yawAngleRadians); + float offsetY = offSetXY * (float)Math.Sin(yawAngleRadians); + + // Calculate position in front of the player + var positionInFront = new Vector + { + X = pawn!.AbsOrigin!.X + offsetX, + Y = pawn!.AbsOrigin!.Y + offsetY, + Z = pawn!.AbsOrigin!.Z + offSetZ + }; + + return positionInFront; + } + + public static void UpdateCamera() { + // rotation is not working + for (int i = 0; i < cameraStatuses.Count; i++) { + var cameraStatus = cameraStatuses[i]; + var player = cameraStatus.Player; + var playerPawn = player.PlayerPawn.Value!; + + var origin = playerPawn.AbsOrigin!; + float rotationAngle = cameraStatus.Times / MAX_ROTATION_TIMES * 2 * float.Pi - float.Pi; // - float.PI = from back + // float posX = origin.X + float.Cos(rotationAngle) * DISTANCE; + // float posY = origin.Y + float.Sin(rotationAngle) * DISTANCE; + + // var cameraOrigin = new Vector(posX, posY, origin.Z + Z_DISTANCE); + + // var cameraAngle = new QAngle(Z_DISTANCE / DISTANCE, 360 * (cameraStatus.Times / MAX_ROTATION_TIMES), 0); + cameraStatus.CameraProp.Teleport(CalculatePositionInFront(player, -110, 90), playerPawn.V_angle, Vector.Zero); + if (cameraStatus.Times >= MAX_ROTATION_TIMES) { + playerPawn.CameraServices!.ViewEntity.Raw = uint.MaxValue; + Utilities.SetStateChanged(playerPawn, "CBasePlayerPawn", "m_pCameraServices"); + cameraStatus.CameraProp.Remove(); + cameraStatuses.RemoveAt(i); + } + cameraStatus.Times += 1; + + } + + } + + public static void ThirdPersonPreviewForPlayer(CCSPlayerController player) { + + for (int i = 0; i < cameraStatuses.Count; i++) { + var oldCameraStatus = cameraStatuses[i]; + var oldPlayer = oldCameraStatus.Player; + var oldPlayerPawn = oldPlayer.PlayerPawn.Value!; + oldPlayerPawn.CameraServices!.ViewEntity.Raw = uint.MaxValue; + Utilities.SetStateChanged(oldPlayerPawn, "CBasePlayerPawn", "m_pCameraServices"); + oldCameraStatus.CameraProp.Remove(); + cameraStatuses.RemoveAt(i); + break; + } + + var _cameraProp = Utilities.CreateEntityByName("prop_physics_multiplayer"); + + if (_cameraProp == null || !_cameraProp.IsValid) return; + + _cameraProp.DispatchSpawn(); + + _cameraProp.Collision.CollisionGroup = (byte)CollisionGroup.COLLISION_GROUP_NEVER; + _cameraProp.Collision.SolidFlags = 12; + _cameraProp.Collision.SolidType = SolidType_t.SOLID_VPHYSICS; + + _cameraProp.Render = Color.FromArgb(0, 255, 255,255); + var playerPawn = player.PlayerPawn.Value!; + playerPawn.CameraServices!.ViewEntity.Raw = _cameraProp.EntityHandle.Raw; + Utilities.SetStateChanged(playerPawn, "CBasePlayerPawn", "m_pCameraServices"); + + CameraStatus cameraStatus = new CameraStatus { + Player = player, + CameraProp = _cameraProp, + Times = 0 + }; + + cameraStatuses.Add(cameraStatus); + } +} \ No newline at end of file diff --git a/Utils.cs b/Utils.cs index 624fc6a..d7d76eb 100644 --- a/Utils.cs +++ b/Utils.cs @@ -1,5 +1,11 @@ +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Admin; +using CounterStrikeSharp.API.Modules.Entities.Constants; +using CounterStrikeSharp.API.Modules.Timers; +using CounterStrikeSharp.API.Modules.Utils; +using Timer = CounterStrikeSharp.API.Modules.Timers.Timer; +using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; namespace PlayerModelChanger; @@ -32,25 +38,25 @@ public static void ExecuteSide(string side, Action? whenAll, Action whenT, Actio public static bool PlayerHasPermission(CCSPlayerController player, string[] permissions, string[] permissionsOr) { foreach (string perm in permissions) { - if (perm.StartsWith("@")) { - if (!AdminManager.PlayerHasPermissions(player, new string[]{perm})) { - return false; + if (perm.StartsWith("@")) { + if (!AdminManager.PlayerHasPermissions(player, new string[]{perm})) { + return false; + } } - } else if (perm.StartsWith("#")) { - if (!AdminManager.PlayerInGroup(player, new string[]{perm})) { - return false; - } + if (!AdminManager.PlayerInGroup(player, new string[]{perm})) { + return false; + } } else { - ulong steamId; - if (!ulong.TryParse(perm, out steamId)) { - throw new FormatException($"Unknown SteamID64 format: {perm}"); - } else { - if (player.SteamID != steamId) { - return false; + ulong steamId; + if (!ulong.TryParse(perm, out steamId)) { + throw new FormatException($"Unknown SteamID64 format: {perm}"); + } else { + if (player.SteamID != steamId) { + return false; + } } - } } @@ -58,29 +64,58 @@ public static bool PlayerHasPermission(CCSPlayerController player, string[] perm foreach (string perm in permissionsOr) { if (perm.StartsWith("@")) { - if (AdminManager.PlayerHasPermissions(player, perm)) { - return true; - } + if (AdminManager.PlayerHasPermissions(player, perm)) { + return true; + } } else if (perm.StartsWith("#")) { - if (AdminManager.PlayerInGroup(player, perm)) { - return true; - } - } else { - ulong steamId; - if (!ulong.TryParse(perm, out steamId)) { - throw new FormatException($"Unknown SteamID64 format: {perm}"); + if (AdminManager.PlayerInGroup(player, perm)) { + return true; + } } else { - if (player.SteamID == steamId) { - return true; - } - } - + ulong steamId; + if (!ulong.TryParse(perm, out steamId)) { + throw new FormatException($"Unknown SteamID64 format: {perm}"); + } else { + if (player.SteamID == steamId) { + return true; + } + } } + } + return true; + } - + public static bool isUpdatingSameTeam(CCSPlayerController player, string side) { + side = side.ToLower(); + if (side == "all") { + return true; } + return (side == "t" && player.Team == CsTeam.Terrorist) || (side == "ct" && player.Team == CsTeam.CounterTerrorist); + } - return true; + public static void RespawnPlayer(CCSPlayerController player, bool disableThirdPersonPreview) { + Server.NextFrame(() => { + var playerPawn = player.PlayerPawn.Value!; + var absOrigin = new Vector(playerPawn.AbsOrigin?.X, playerPawn.AbsOrigin?.Y, playerPawn.AbsOrigin?.Z); + var absAngle = new QAngle(playerPawn.AbsRotation?.X, playerPawn.AbsRotation?.Y, playerPawn.AbsRotation?.Z); + var health = playerPawn.Health; + var armor = playerPawn.ArmorValue; + CCSPlayer_ItemServices services = new CCSPlayer_ItemServices(playerPawn.ItemServices!.Handle); + var armorHelmet = services.HasHelmet; + var defuser = services.HasDefuser; + + player.Respawn(); + playerPawn.Teleport(absOrigin, absAngle); + playerPawn.Health = health; + Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_iHealth"); + playerPawn.ArmorValue = armor; + services.HasHelmet = armorHelmet; + services.HasDefuser = defuser; + Utilities.SetStateChanged(playerPawn, "CBasePlayerPawn", "m_pItemServices"); + if (!disableThirdPersonPreview) { + ThirdPerson.ThirdPersonPreviewForPlayer(player); + } + }); } } diff --git a/bin/Debug/net8.0/PlayerModelChanger.dll b/bin/Debug/net8.0/PlayerModelChanger.dll index 2338b12..72fcbbd 100644 Binary files a/bin/Debug/net8.0/PlayerModelChanger.dll and b/bin/Debug/net8.0/PlayerModelChanger.dll differ diff --git a/bin/Debug/net8.0/PlayerModelChanger.pdb b/bin/Debug/net8.0/PlayerModelChanger.pdb index bbf174e..ec4d993 100644 Binary files a/bin/Debug/net8.0/PlayerModelChanger.pdb and b/bin/Debug/net8.0/PlayerModelChanger.pdb differ diff --git a/bin/Debug/net8.0/lang/en.json b/bin/Debug/net8.0/lang/en.json index 0db9798..6d5a301 100644 --- a/bin/Debug/net8.0/lang/en.json +++ b/bin/Debug/net8.0/lang/en.json @@ -11,6 +11,7 @@ "command.model.hint2": "Type {green}!md {default}or {green}!models {default}to select model.", "command.model.notfound": "{red}Model name {default}{0} {red}not found.", "command.model.success": "{green}Your model on {0} will be set after next spawn.", + "command.model.cooldown": "{red}You need to wait for a while before changing model.", "command.resetmodel.success": "{green}Your model will be reseted from next spawn.", "command.models": "{green}Available models ({0}): {default}", "command.modeladmin.hint": "{green}!modeladmin [all/steamid] reset [all/ct/t] {default}Reset player's model.\u2029{green}!modeladmin [all/steamid] set [all/ct/t] [model index] {default}Set player's model.\u2029{green}!modeladmin [steamid] check {default}Check if player's model is not allowed to have, if not then reset it.\u2029{green}!modeladmin reload {default}Reload the config.", @@ -21,6 +22,7 @@ "random.notavailable": "{red}No available model.", "modelmenu.title": "({0}) Current model: {1}", "modelmenu.nomodel": "{red}No available model.", + "modelmenu.forced": "{red}Your model had been forced.", "modelmenu.selectside": "Please select a side", "modelmenu.unset": "Unset", "modelmenu.random": "Random", diff --git a/bin/Debug/net8.0/lang/zh-Hans.json b/bin/Debug/net8.0/lang/zh-Hans.json index 997cc71..497abf5 100644 --- a/bin/Debug/net8.0/lang/zh-Hans.json +++ b/bin/Debug/net8.0/lang/zh-Hans.json @@ -9,8 +9,9 @@ "command.unknownside": "{red}未知的阵营: {default}{0}", "command.model.hint1": "输入 {green}!model <@random/模型名> {default} 切换模型 (@random代表随机, 推荐使用{green}!models{default})。", "command.model.hint2": "输入 {green}!md {default}或 {green}!models {default} 选择模型。", - "command.model.notfound": "{red}模型名 {default}{0} {red}不存在.", + "command.model.notfound": "{red}模型名 {default}{0} {red}不存在。", "command.model.success": "{green}您在{0}的模型会在下一次重生后生效。", + "command.model.cooldown": "{red}您需要再等待一会才可更改模型。", "command.modeladmin.hint": "{green}!modeladmin [all/steamid] reset [all/ct/t] {default}重置玩家的模型\u2029{green}!modeladmin [all/steamid] set [all/ct/t] [model index] {default}设置玩家的模型\u2029{green}!modeladmin [steamid] check {default}检查玩家是否可以使用当前使用的模型,如果不可以则重置.\u2029{green}!modeladmin reload {default}重载配置文件.", "command.modeladmin.playernotfound": "{red}未找到steamid.", "command.modeladmin.success": "{green}成功执行.", @@ -20,6 +21,7 @@ "random.notavailable": "{red}没有可用的模型。", "modelmenu.title": "({0}) 当前模型: {1}", "modelmenu.nomodel": "{red}没有可用的模型。", + "modelmenu.forced": "{red}您的模型已被强制设置,无法更改。", "modelmenu.selectside": "请选择阵营", "modelmenu.unset": "重置", "modelmenu.random": "随机", diff --git a/commands/CommandModelMenu.cs b/commands/CommandModelMenu.cs index 53ec5c0..db87978 100644 --- a/commands/CommandModelMenu.cs +++ b/commands/CommandModelMenu.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities; using Service; namespace PlayerModelChanger; @@ -37,10 +38,27 @@ private ModelMenuData GenerateModelMenuData(CCSPlayerController player) { return data; } - private TeamMenuData GenerateTeamMenuData(CCSPlayerController player) { + private TeamMenuData? GenerateTeamMenuData(CCSPlayerController player) { TeamMenuData data = new(); data.title = Localizer["modelmenu.selectside"]; - foreach(var side in new string[]{"t", "ct", "all"}) { + var sides = new List(); + + var tDefault = DefaultModelManager.GetPlayerDefaultModel(player, "t"); + var ctDefault = DefaultModelManager.GetPlayerDefaultModel(player, "ct"); + if (tDefault == null || !tDefault.force) { + sides.Add("t"); + } + if (ctDefault == null || !ctDefault.force) { + sides.Add("ct"); + } + if (sides.Count == 2) { + sides.Append("all"); + } + if (sides.Count == 0) { + return null; + } + + foreach(var side in sides) { var playerModel = Service.GetPlayerModel(player, side); string selection = playerModel != null ? $"{Localizer["side."+side]}: {playerModel.name}" : $"{Localizer["side."+side]}"; @@ -65,6 +83,10 @@ private WasdMenuManager GetWasdMenuManager() { public void OpenSelectSideMenu(CCSPlayerController player) { var modelData = GenerateModelMenuData(player); var teamData = GenerateTeamMenuData(player); + if (teamData == null) { + player.PrintToChat(Localizer["modelmenu.forced"]); + return; + } if (Config.MenuType == "interactive" || Config.MenuType == "wasd") { GetWasdMenuManager().OpenSelectSideMenu(player, teamData, modelData); } else { @@ -74,6 +96,11 @@ public void OpenSelectSideMenu(CCSPlayerController player) { public void OpenSelectModelMenu(CCSPlayerController player, string side, Model? model) { var modelData = GenerateModelMenuData(player); + var defaultModel = DefaultModelManager.GetPlayerDefaultModel(player, side); + if (defaultModel != null && defaultModel.force) { + player.PrintToChat(Localizer["modelmenu.forced"]); + return; + } if (Config.MenuType == "interactive" || Config.MenuType == "wasd") { GetWasdMenuManager().OpenSelectModelMenu(player, side, modelData); } else { diff --git a/commands/PlayerCommand.cs b/commands/PlayerCommand.cs index 15f7f51..3109473 100644 --- a/commands/PlayerCommand.cs +++ b/commands/PlayerCommand.cs @@ -1,6 +1,10 @@ +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Commands; +using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Menu; using CounterStrikeSharp.API.Modules.Utils; using Service; @@ -64,18 +68,12 @@ public void GetAllModelsCommand(CCSPlayerController? player, CommandInfo command if (Config.DisablePlayerSelection) { return; } - if (commandInfo.ArgCount == 1) { OpenSelectSideMenu(player); return; } - string side = commandInfo.GetArg(1); OpenSelectModelMenu(player, side, Service.GetPlayerModel(player, side)); - - - - } diff --git a/lang/en.json b/lang/en.json index 0db9798..6d5a301 100644 --- a/lang/en.json +++ b/lang/en.json @@ -11,6 +11,7 @@ "command.model.hint2": "Type {green}!md {default}or {green}!models {default}to select model.", "command.model.notfound": "{red}Model name {default}{0} {red}not found.", "command.model.success": "{green}Your model on {0} will be set after next spawn.", + "command.model.cooldown": "{red}You need to wait for a while before changing model.", "command.resetmodel.success": "{green}Your model will be reseted from next spawn.", "command.models": "{green}Available models ({0}): {default}", "command.modeladmin.hint": "{green}!modeladmin [all/steamid] reset [all/ct/t] {default}Reset player's model.\u2029{green}!modeladmin [all/steamid] set [all/ct/t] [model index] {default}Set player's model.\u2029{green}!modeladmin [steamid] check {default}Check if player's model is not allowed to have, if not then reset it.\u2029{green}!modeladmin reload {default}Reload the config.", @@ -21,6 +22,7 @@ "random.notavailable": "{red}No available model.", "modelmenu.title": "({0}) Current model: {1}", "modelmenu.nomodel": "{red}No available model.", + "modelmenu.forced": "{red}Your model had been forced.", "modelmenu.selectside": "Please select a side", "modelmenu.unset": "Unset", "modelmenu.random": "Random", diff --git a/lang/zh-Hans.json b/lang/zh-Hans.json index 997cc71..497abf5 100644 --- a/lang/zh-Hans.json +++ b/lang/zh-Hans.json @@ -9,8 +9,9 @@ "command.unknownside": "{red}未知的阵营: {default}{0}", "command.model.hint1": "输入 {green}!model <@random/模型名> {default} 切换模型 (@random代表随机, 推荐使用{green}!models{default})。", "command.model.hint2": "输入 {green}!md {default}或 {green}!models {default} 选择模型。", - "command.model.notfound": "{red}模型名 {default}{0} {red}不存在.", + "command.model.notfound": "{red}模型名 {default}{0} {red}不存在。", "command.model.success": "{green}您在{0}的模型会在下一次重生后生效。", + "command.model.cooldown": "{red}您需要再等待一会才可更改模型。", "command.modeladmin.hint": "{green}!modeladmin [all/steamid] reset [all/ct/t] {default}重置玩家的模型\u2029{green}!modeladmin [all/steamid] set [all/ct/t] [model index] {default}设置玩家的模型\u2029{green}!modeladmin [steamid] check {default}检查玩家是否可以使用当前使用的模型,如果不可以则重置.\u2029{green}!modeladmin reload {default}重载配置文件.", "command.modeladmin.playernotfound": "{red}未找到steamid.", "command.modeladmin.success": "{green}成功执行.", @@ -20,6 +21,7 @@ "random.notavailable": "{red}没有可用的模型。", "modelmenu.title": "({0}) 当前模型: {1}", "modelmenu.nomodel": "{red}没有可用的模型。", + "modelmenu.forced": "{red}您的模型已被强制设置,无法更改。", "modelmenu.selectside": "请选择阵营", "modelmenu.unset": "重置", "modelmenu.random": "随机", diff --git a/storage/MySQL.cs b/storage/MySQL.cs index 2336233..6c24869 100644 --- a/storage/MySQL.cs +++ b/storage/MySQL.cs @@ -57,8 +57,13 @@ SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS """); } - public List GetAllPlayerModel() { - return conn.Query($"select * from {table};").ToList(); + public List? GetAllPlayerModel() { + try { + return conn.Query($"select * from {table};").ToList(); + } catch (InvalidOperationException e) { + + } + return null; } public dynamic? GetPlayerModel(ulong SteamID, string modelfield)