Refactor fun commands to external module

Commented out fun command implementations (noclip, godmode, freeze, unfreeze, resize) in funcommands.cs and removed their registration from RegisterCommands.cs. These commands are now intended to be provided by the new CS2-SimpleAdmin_FunCommands external module, improving modularity and maintainability.
This commit is contained in:
Dawid Bepierszcz
2025-10-19 03:12:58 +02:00
parent 2edacc2b3f
commit 78318102fe
69 changed files with 5943 additions and 1493 deletions

View File

@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CS2-SimpleAdmin_FunCommands", "CS2-SimpleAdmin_FunCommands\CS2-SimpleAdmin_FunCommands.csproj", "{72713A40-688F-401F-8211-3D28B068C791}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{72713A40-688F-401F-8211-3D28B068C791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72713A40-688F-401F-8211-3D28B068C791}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72713A40-688F-401F-8211-3D28B068C791}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72713A40-688F-401F-8211-3D28B068C791}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,237 @@
using System.Globalization;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using Microsoft.Extensions.Logging;
namespace CS2_SimpleAdmin_FunCommands;
public partial class CS2_SimpleAdmin_FunCommands
{
private void God(CCSPlayerController? caller, CCSPlayerController player)
{
if (!caller.CanTarget(player)) return;
var callerName = caller?.PlayerName ?? "Console";
// Toggle god mode for the player (like in main plugin)
if (!GodPlayers.Add(player.Slot))
{
GodPlayers.Remove(player.Slot);
}
// Show admin activity using module's own localizer with per-player language support
var activityArgs = new object[] { "CALLER", player.PlayerName };
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
{
if (Localizer != null)
{
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_god_message", callerName, false,
activityArgs);
}
else
{
_sharedApi!.ShowAdminActivity("fun_admin_god_message", callerName, false, activityArgs);
}
}
// Log command using API
_sharedApi!.LogCommand(caller,
$"css_god {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
}
private void NoClip(CCSPlayerController? caller, CCSPlayerController player)
{
if (!player.IsValid) return;
if (!caller.CanTarget(player)) return;
var callerName = caller?.PlayerName ?? "Console";
// Toggle no-clip mode using PlayerExtensions
player.Pawn.Value?.ToggleNoclip();
// Show admin activity using module's own localizer with per-player language support
var activityArgs = new object[] { "CALLER", player.PlayerName };
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
{
if (Localizer != null)
{
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_noclip_message", callerName, false,
activityArgs);
}
else
{
_sharedApi!.ShowAdminActivity("fun_admin_noclip_message", callerName, false, activityArgs);
}
}
// Log command using API
_sharedApi!.LogCommand(caller,
$"css_noclip {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
}
private void Freeze(CCSPlayerController? caller, CCSPlayerController player, int time)
{
if (!player.IsValid) return;
if (!caller.CanTarget(player)) return;
var callerName = caller?.PlayerName ?? "Console";
// Freeze player using PlayerExtensions
player.Pawn.Value?.Freeze();
// Show admin activity using module's own localizer with per-player language support
var activityArgs = new object[] { "CALLER", player.PlayerName };
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
{
if (Localizer != null)
{
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_freeze_message", callerName, false,
activityArgs);
}
else
{
_sharedApi!.ShowAdminActivity("fun_admin_freeze_message", callerName, false, activityArgs);
}
}
// Auto-unfreeze after duration
if (time > 0)
{
AddTimer(time, () => player.Pawn.Value?.Unfreeze(),
CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
// Log command using API
_sharedApi!.LogCommand(caller,
$"css_freeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {time}");
}
private void Unfreeze(CCSPlayerController? caller, CCSPlayerController player)
{
if (!player.IsValid) return;
if (!caller.CanTarget(player)) return;
var callerName = caller?.PlayerName ?? "Console";
// Unfreeze player using PlayerExtensions
player.Pawn.Value?.Unfreeze();
// Show admin activity using module's own localizer with per-player language support
var activityArgs = new object[] { "CALLER", player.PlayerName };
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
{
if (Localizer != null)
{
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_unfreeze_message", callerName, false,
activityArgs);
}
else
{
_sharedApi!.ShowAdminActivity("fun_admin_unfreeze_message", callerName, false, activityArgs);
}
}
// Log command using API
_sharedApi!.LogCommand(caller,
$"css_unfreeze {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
}
/// <summary>
/// Respawns a player and teleports them to their last death position if available.
/// This demonstrates using the GetPlayerInfo API to access player data.
/// </summary>
private void Respawn(CCSPlayerController? caller, CCSPlayerController player)
{
if (!player.IsValid) return;
if (!caller.CanTarget(player)) return;
var callerName = caller?.PlayerName ?? "Console";
// Respawn the player
player.Respawn();
// Get death position from API and teleport player to it
// BEST PRACTICE: Use API to access player data like death position
if (_sharedApi != null && player.UserId.HasValue)
{
try
{
var playerInfo = _sharedApi.GetPlayerInfo(player);
// Teleport to death position if available
if (playerInfo?.DiePosition != null && player.PlayerPawn?.Value != null)
{
player.PlayerPawn.Value.Teleport(
playerInfo.DiePosition.Position,
playerInfo.DiePosition.Angle);
}
}
catch (Exception ex)
{
Logger.LogWarning($"Failed to get player info for respawn: {ex.Message}");
}
}
// Show admin activity using module's own localizer with per-player language support
var activityArgs = new object[] { "CALLER", player.PlayerName };
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
{
if (Localizer != null)
{
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_respawn_message", callerName, false,
activityArgs);
}
else
{
_sharedApi!.ShowAdminActivity("fun_admin_respawn_message", callerName, false, activityArgs);
}
}
// Log command using API
_sharedApi!.LogCommand(caller,
$"css_respawn {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)}");
}
/// <summary>
/// Resizes a player's model to the specified scale.
/// </summary>
private void Resize(CCSPlayerController? caller, CCSPlayerController player, float size)
{
if (!player.IsValid) return;
if (!caller.CanTarget(player)) return;
var callerName = caller?.PlayerName ?? "Console";
// Resize the player
var sceneNode = player.PlayerPawn.Value!.CBodyComponent?.SceneNode;
if (sceneNode != null)
{
sceneNode.GetSkeletonInstance().Scale = size;
player.PlayerPawn.Value.AcceptInput("SetScale", null, null, size.ToString(CultureInfo.InvariantCulture));
Server.NextWorldUpdate(() =>
{
Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_CBodyComponent");
});
}
// Show admin activity using module's own localizer with per-player language support
var activityArgs = new object[] { "CALLER", player.PlayerName, size.ToString(CultureInfo.InvariantCulture) };
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
{
if (Localizer != null)
{
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_resize_message", callerName, false,
activityArgs);
}
else
{
_sharedApi!.ShowAdminActivity("fun_admin_resize_message", callerName, false, activityArgs);
}
}
// Log command using API
_sharedApi!.LogCommand(caller,
$"css_resize {(string.IsNullOrEmpty(player.PlayerName) ? player.SteamID.ToString() : player.PlayerName)} {size}");
}
}

View File

@@ -0,0 +1,492 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Capabilities;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Utils;
using CS2_SimpleAdminApi;
using Microsoft.Extensions.Logging;
using System.Globalization;
namespace CS2_SimpleAdmin_FunCommands;
/// <summary>
/// CS2-SimpleAdmin Fun Commands Module
///
/// This module serves as a REFERENCE IMPLEMENTATION for creating CS2-SimpleAdmin modules.
/// Study this code to learn best practices for:
/// - Command registration from configuration
/// - Menu creation with SimpleAdmin API
/// - Per-player translation support
/// - Proper cleanup on module unload
/// - Code organization using partial classes
///
/// File Structure:
/// - CS2-SimpleAdmin_FunCommands.cs (this file) - Plugin initialization and registration
/// - Commands.cs - Command handlers
/// - Actions.cs - Action methods (God, NoClip, Freeze, etc.)
/// - Menus.cs - Menu creation
/// - Config.cs - Configuration with command lists
/// - lang/ - Translation files (13 languages)
///
/// See README.md for detailed explanation of all patterns demonstrated here.
/// </summary>
public partial class CS2_SimpleAdmin_FunCommands : BasePlugin, IPluginConfig<Config>
{
public Config Config { get; set; }
/// <summary>
/// BEST PRACTICE: Cache expensive operations
/// Weapons enum values don't change, so we cache them on first access
/// </summary>
private static Dictionary<int, CsItem>? _weaponsCache;
private static Dictionary<int, CsItem> GetWeaponsCache()
{
if (_weaponsCache != null) return _weaponsCache;
var weaponsArray = Enum.GetValues(typeof(CsItem));
_weaponsCache = new Dictionary<int, CsItem>();
foreach (CsItem item in weaponsArray)
{
if (item == CsItem.Tablet) continue; // Skip tablet (invalid weapon)
_weaponsCache[(int)item] = item;
}
return _weaponsCache;
}
/// <summary>
/// Track players with god mode enabled
/// HashSet for O(1) lookup performance
/// </summary>
private static readonly HashSet<int> GodPlayers = [];
/// <summary>
/// Track players with modified speed
/// Dictionary for storing speed values per player
/// </summary>
private static readonly Dictionary<CCSPlayerController, float> SpeedPlayers = [];
/// <summary>
/// Track players with modified gravity
/// Dictionary for storing gravity values per player
/// </summary>
private static readonly Dictionary<CCSPlayerController, float> GravityPlayers = [];
/// <summary>
/// BEST PRACTICE: Use capability system to get SimpleAdmin API
/// This ensures your module works even if SimpleAdmin loads after your module
/// </summary>
private ICS2_SimpleAdminApi? _sharedApi;
private readonly PluginCapability<ICS2_SimpleAdminApi> _pluginCapability = new("simpleadmin:api");
/// <summary>
/// BEST PRACTICE: Track menu registration state to prevent duplicate registrations
/// </summary>
private bool _menusRegistered = false;
// Plugin metadata
public override string ModuleName => "CS2-SimpleAdmin Fun Commands";
public override string ModuleVersion => "1.0.0";
public override string ModuleAuthor => "Your Name";
public override string ModuleDescription => "Fun commands extension for CS2-SimpleAdmin";
/// <summary>
/// BEST PRACTICE: Initialize plugin after all plugins are loaded
/// This ensures SimpleAdmin API is available
/// </summary>
public override void OnAllPluginsLoaded(bool hotReload)
{
// STEP 1: Get SimpleAdmin API using capability system
_sharedApi = _pluginCapability.Get();
if (_sharedApi == null)
{
Logger.LogError("CS2-SimpleAdmin API not found - make sure CS2-SimpleAdmin is loaded!");
Unload(false);
return;
}
// STEP 2: Register commands (can be done immediately)
RegisterFunCommands();
// STEP 3: Register menus (wait for SimpleAdmin to be ready)
// BEST PRACTICE: Use event + fallback to handle both normal load and hot reload
_sharedApi.OnSimpleAdminReady += RegisterFunMenus;
RegisterFunMenus(); // Fallback for hot reload case
// STEP 4: Start timer to maintain speed and gravity modifications
StartSpeedGravityTimer();
}
public override void Unload(bool hotReload)
{
if (_sharedApi == null) return;
// Unregister commands
if (Config.NoclipCommands.Count > 0)
{
foreach (var command in Config.NoclipCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.GodCommands.Count > 0)
{
foreach (var command in Config.GodCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.FreezeCommands.Count > 0)
{
foreach (var command in Config.FreezeCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.UnfreezeCommands.Count > 0)
{
foreach (var command in Config.UnfreezeCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.RespawnCommands.Count > 0)
{
foreach (var command in Config.RespawnCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.GiveCommands.Count > 0)
{
foreach (var command in Config.GiveCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.StripCommands.Count > 0)
{
foreach (var command in Config.StripCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.HpCommands.Count > 0)
{
foreach (var command in Config.HpCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.SpeedCommands.Count > 0)
{
foreach (var command in Config.SpeedCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.GravityCommands.Count > 0)
{
foreach (var command in Config.GravityCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.MoneyCommands.Count > 0)
{
foreach (var command in Config.MoneyCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
if (Config.ResizeCommands.Count > 0)
{
foreach (var command in Config.ResizeCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
// Unregister menus
if (Config.NoclipCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "noclip");
if (Config.GodCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "god");
if (Config.RespawnCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "respawn");
if (Config.GiveCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "give");
if (Config.StripCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "strip");
if (Config.FreezeCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "freeze");
if (Config.HpCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "hp");
if (Config.SpeedCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "speed");
if (Config.GravityCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "gravity");
if (Config.MoneyCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "money");
if (Config.ResizeCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "resize");
_sharedApi.OnSimpleAdminReady -= RegisterFunMenus;
}
private void RegisterFunCommands()
{
if (_sharedApi == null) return;
if (Config.NoclipCommands.Count > 0)
{
foreach (var command in Config.NoclipCommands)
{
_sharedApi.RegisterCommand(command, "Enable noclip", OnNoclipCommand);
}
}
if (Config.GodCommands.Count > 0)
{
foreach (var command in Config.GodCommands)
{
_sharedApi.RegisterCommand(command, "Enable god mode", OnGodCommand);
}
}
if (Config.FreezeCommands.Count > 0)
{
foreach (var command in Config.FreezeCommands)
{
_sharedApi.RegisterCommand(command, "Freeze player", OnFreezeCommand);
}
}
if (Config.UnfreezeCommands.Count > 0)
{
foreach (var command in Config.UnfreezeCommands)
{
_sharedApi.RegisterCommand(command, "Unfreeze player", OnUnfreezeCommand);
}
}
if (Config.RespawnCommands.Count > 0)
{
foreach (var command in Config.RespawnCommands)
{
_sharedApi.RegisterCommand(command, "Respawn player", OnRespawnCommand);
}
}
if (Config.GiveCommands.Count > 0)
{
foreach (var command in Config.GiveCommands)
{
_sharedApi.RegisterCommand(command, "Give weapon", OnGiveWeaponCommand);
}
}
if (Config.StripCommands.Count > 0)
{
foreach (var command in Config.StripCommands)
{
_sharedApi.RegisterCommand(command, "Strip weapons", OnStripWeaponsCommand);
}
}
if (Config.HpCommands.Count > 0)
{
foreach (var command in Config.HpCommands)
{
_sharedApi.RegisterCommand(command, "Set HP", OnSetHpCommand);
}
}
if (Config.SpeedCommands.Count > 0)
{
foreach (var command in Config.SpeedCommands)
{
_sharedApi.RegisterCommand(command, "Set speed", OnSetSpeedCommand);
}
}
if (Config.GravityCommands.Count > 0)
{
foreach (var command in Config.GravityCommands)
{
_sharedApi.RegisterCommand(command, "Set gravity", OnSetGravityCommand);
}
}
if (Config.MoneyCommands.Count > 0)
{
foreach (var command in Config.MoneyCommands)
{
_sharedApi.RegisterCommand(command, "Set money", OnSetMoneyCommand);
}
}
if (Config.ResizeCommands.Count > 0)
{
foreach (var command in Config.ResizeCommands)
{
_sharedApi.RegisterCommand(command, "Resize player", OnSetResizeCommand);
}
}
}
private void RegisterFunMenus()
{
if (_sharedApi == null || _menusRegistered) return;
try
{
_sharedApi.RegisterMenuCategory("fun", Localizer?["fun_category_name"] ?? "Fun Commands", "@css/generic");
if (Config.GodCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "god",
Localizer?["fun_menu_god"] ?? "God Mode",
CreateGodModeMenu, "@css/cheats");
if (Config.NoclipCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "noclip",
Localizer?["fun_menu_noclip"] ?? "No Clip",
CreateNoClipMenu, "@css/cheats");
if (Config.RespawnCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "respawn",
Localizer?["fun_menu_respawn"] ?? "Respawn",
CreateRespawnMenu, "@css/cheats");
if (Config.GiveCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "give",
Localizer?["fun_menu_give"] ?? "Give Weapon",
CreateGiveWeaponMenu, "@css/cheats");
if (Config.StripCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "strip",
Localizer?["fun_menu_strip"] ?? "Strip Weapons",
CreateStripWeaponsMenu, "@css/slay");
if (Config.FreezeCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "freeze",
Localizer?["fun_menu_freeze"] ?? "Freeze",
CreateFreezeMenu, "@css/slay");
if (Config.HpCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "hp",
Localizer?["fun_menu_hp"] ?? "Set HP",
CreateSetHpMenu, "@css/slay");
if (Config.SpeedCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "speed",
Localizer?["fun_menu_speed"] ?? "Set Speed",
CreateSetSpeedMenu, "@css/slay");
if (Config.GravityCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "gravity",
Localizer?["fun_menu_gravity"] ?? "Set Gravity",
CreateSetGravityMenu, "@css/slay");
if (Config.MoneyCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "money",
Localizer?["fun_menu_money"] ?? "Set Money",
CreateSetMoneyMenu, "@css/slay");
if (Config.ResizeCommands.Count > 0)
_sharedApi.RegisterMenu("fun", "resize",
Localizer?["fun_menu_resize"] ?? "Resize Player",
CreateSetResizeMenu, "@css/slay");
_menusRegistered = true;
Logger.LogInformation("Fun menus registered successfully!");
}
catch (Exception ex)
{
Logger.LogError($"Failed to register Fun menus: {ex.Message}");
}
}
public void OnConfigParsed(Config config)
{
Config = config;
}
/// <summary>
/// Starts a repeating timer to maintain speed and gravity modifications for players.
/// This ensures that speed/gravity changes persist even after respawns or round changes.
/// </summary>
private void StartSpeedGravityTimer()
{
AddTimer(0.12f, () =>
{
// Early exit if no players have modified speed or gravity
var hasSpeedPlayers = SpeedPlayers.Count > 0;
var hasGravityPlayers = GravityPlayers.Count > 0;
if (!hasSpeedPlayers && !hasGravityPlayers)
return;
if (hasSpeedPlayers)
{
// Iterate through players with modified speed
foreach (var kvp in SpeedPlayers)
{
var player = kvp.Key;
// Early validation check - avoid accessing PlayerPawn if player is invalid
if (player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected)
{
var pawn = player.PlayerPawn?.Value;
if (pawn != null && pawn.LifeState == (int)LifeState_t.LIFE_ALIVE)
{
player.SetSpeed(kvp.Value);
}
}
}
}
if (hasGravityPlayers)
{
// Iterate through players with modified gravity
foreach (var kvp in GravityPlayers)
{
var player = kvp.Key;
if (player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected)
{
var pawn = player.PlayerPawn?.Value;
if (pawn != null && pawn.LifeState == (int)LifeState_t.LIFE_ALIVE)
{
player.SetGravity(kvp.Value);
}
}
}
}
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT);
}
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CS2_SimpleAdmin_FunCommands</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Reference Include="CS2-SimpleAdminApi">
<HintPath>..\CS2-SimpleAdminApi.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.340" />
</ItemGroup>
<ItemGroup>
<None Update="lang\**\*.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,320 @@
using System.Globalization;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Entities.Constants;
namespace CS2_SimpleAdmin_FunCommands;
public partial class CS2_SimpleAdmin_FunCommands
{
// =================================
// COMMAND HANDLERS
// =================================
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/cheats")]
private void OnNoclipCommand(CCSPlayerController? caller, CommandInfo command)
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
NoClip(caller, player);
}
});
}
[RequiresPermissions("@css/cheats")]
[CommandHelper(minArgs: 1, usage: "<#userid or name>", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)]
private void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
God(caller, player);
}
});
}
[CommandHelper(1, "<#userid or name> [duration]")]
[RequiresPermissions("@css/slay")]
private void OnFreezeCommand(CCSPlayerController? caller, CommandInfo command)
{
int.TryParse(command.GetArg(2), out var time);
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Freeze(caller, player, time);
}
});
}
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/slay")]
private void OnUnfreezeCommand(CCSPlayerController? caller, CommandInfo command)
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Unfreeze(caller, player);
}
});
}
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/cheats")]
private void OnRespawnCommand(CCSPlayerController? caller, CommandInfo command)
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Respawn(caller, player);
}
});
}
[CommandHelper(2, "<#userid or name> <weapon>")]
[RequiresPermissions("@css/cheats")]
private void OnGiveWeaponCommand(CCSPlayerController? caller, CommandInfo command)
{
var weaponName = command.GetArg(2);
if (Enum.TryParse(weaponName, true, out CsItem weapon))
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
player.GiveNamedItem(weapon);
LogAndShowActivity(caller, player, "fun_admin_give_message", "css_give", weapon.ToString());
}
});
}
}
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/slay")]
private void OnStripWeaponsCommand(CCSPlayerController? caller, CommandInfo command)
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
player.RemoveWeapons();
LogAndShowActivity(caller, player, "fun_admin_strip_message", "css_strip");
}
});
}
[CommandHelper(2, "<#userid or name> <hp>")]
[RequiresPermissions("@css/slay")]
private void OnSetHpCommand(CCSPlayerController? caller, CommandInfo command)
{
if (int.TryParse(command.GetArg(2), out var hp))
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
player.SetHp(hp);
LogAndShowActivity(caller, player, "fun_admin_hp_message", "css_hp", hp.ToString());
}
});
}
}
[CommandHelper(2, "<#userid or name> <speed>")]
[RequiresPermissions("@css/slay")]
private void OnSetSpeedCommand(CCSPlayerController? caller, CommandInfo command)
{
if (float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var speed))
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
player.SetSpeed(speed);
// Track speed modification for timer
if (speed == 1f)
SpeedPlayers.Remove(player);
else
SpeedPlayers[player] = speed;
LogAndShowActivity(caller, player, "fun_admin_speed_message", "css_speed", speed.ToString(CultureInfo.InvariantCulture));
}
});
}
}
[CommandHelper(2, "<#userid or name> <gravity>")]
[RequiresPermissions("@css/slay")]
private void OnSetGravityCommand(CCSPlayerController? caller, CommandInfo command)
{
if (float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var gravity))
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
player.SetGravity(gravity);
// Track gravity modification for timer
if (gravity == 1f)
GravityPlayers.Remove(player);
else
GravityPlayers[player] = gravity;
LogAndShowActivity(caller, player, "fun_admin_gravity_message", "css_gravity", gravity.ToString(CultureInfo.InvariantCulture));
}
});
}
}
[CommandHelper(2, "<#userid or name> <money>")]
[RequiresPermissions("@css/slay")]
private void OnSetMoneyCommand(CCSPlayerController? caller, CommandInfo command)
{
if (int.TryParse(command.GetArg(2), out var money))
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, Connected: PlayerConnectedState.PlayerConnected }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
player.SetMoney(money);
LogAndShowActivity(caller, player, "fun_admin_money_message", "css_money", money.ToString());
}
});
}
}
[CommandHelper(2, "<#userid or name> <size>")]
[RequiresPermissions("@css/slay")]
private void OnSetResizeCommand(CCSPlayerController? caller, CommandInfo command)
{
if (float.TryParse(command.GetArg(2), NumberStyles.Float, CultureInfo.InvariantCulture, out var size))
{
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
Resize(caller, player, size);
}
});
}
}
// =================================
// HELPER METHOD FOR ACTIVITIES WITH INDIVIDUAL COMMAND LOGGING
// =================================
private void LogAndShowActivity(CCSPlayerController? caller, CCSPlayerController target, string messageKey, string baseCommand, params string[] extraArgs)
{
var callerName = caller?.PlayerName ?? "Console";
// Build activity args
var args = new List<object> { "CALLER", target.PlayerName };
args.AddRange(extraArgs);
// Show admin activity using module's own localizer with per-player language support
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
{
// Use module's own translations with automatic per-player language support
if (Localizer != null)
{
// This will send the message in each player's configured language
_sharedApi!.ShowAdminActivityLocalized(Localizer, messageKey, callerName, false, args.ToArray());
}
else
{
// Fallback to old method if localizer is not available
_sharedApi!.ShowAdminActivity(messageKey, callerName, false, args.ToArray());
}
}
// Build and log command using API string method
var logCommand = $"{baseCommand} {(string.IsNullOrEmpty(target.PlayerName) ? target.SteamID.ToString() : target.PlayerName)}";
if (extraArgs.Length > 0)
{
logCommand += $" {string.Join(" ", extraArgs)}";
}
_sharedApi!.LogCommand(caller, logCommand);
}
}

View File

@@ -0,0 +1,21 @@
using CounterStrikeSharp.API.Core;
namespace CS2_SimpleAdmin_FunCommands;
public class Config : IBasePluginConfig
{
public int Version { get; set; } = 1;
public List<string> NoclipCommands { get; set; } = ["css_noclip"];
public List<string> GodCommands { get; set; } = ["css_god"];
public List<string> FreezeCommands { get; set; } = ["css_freeze"];
public List<string> UnfreezeCommands { get; set; } = ["css_unfreeze"];
public List<string> RespawnCommands { get; set; } = ["css_respawn"];
public List<string> GiveCommands { get; set; } = ["css_give"];
public List<string> StripCommands { get; set; } = ["css_strip"];
public List<string> HpCommands { get; set; } = ["css_hp"];
public List<string> SpeedCommands { get; set; } = ["css_speed"];
public List<string> GravityCommands { get; set; } = ["css_gravity"];
public List<string> MoneyCommands { get; set; } = ["css_money"];
public List<string> ResizeCommands { get; set; } = ["css_resize"];
}

View File

@@ -0,0 +1,69 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
namespace CS2_SimpleAdmin_FunCommands;
public partial class CS2_SimpleAdmin_FunCommands
{
[GameEventHandler]
public HookResult OnPlayerHurt(EventPlayerHurt @event, GameEventInfo info)
{
var player = @event.Userid;
if (player == null || !player.IsValid) return HookResult.Continue;
// Check if player has god mode (similar to main plugin)
if (!GodPlayers.Contains(player.Slot)) return HookResult.Continue;
// Cancel damage
@event.DmgHealth = 0;
@event.DmgArmor = 0;
// Reset health to full
if (player.PlayerPawn?.Value == null) return HookResult.Continue;
player.PlayerPawn.Value.Health = player.PlayerPawn.Value.MaxHealth;
Utilities.SetStateChanged(player.PlayerPawn.Value, "CBaseEntity", "m_iHealth");
return HookResult.Continue;
}
[GameEventHandler]
public HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info)
{
var player = @event.Userid;
if (player == null || !player.IsValid) return HookResult.Continue;
// Remove player from god mode, speed, and gravity tracking on death
GodPlayers.Remove(player.Slot);
SpeedPlayers.Remove(player);
GravityPlayers.Remove(player);
return HookResult.Continue;
}
[GameEventHandler]
public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
{
// Clear all fun command modifications at round start
GodPlayers.Clear();
SpeedPlayers.Clear();
GravityPlayers.Clear();
return HookResult.Continue;
}
[GameEventHandler]
public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info)
{
var player = @event.Userid;
if (player == null || !player.IsValid) return HookResult.Continue;
// Clean up player from all tracking when they disconnect
GodPlayers.Remove(player.Slot);
SpeedPlayers.Remove(player);
GravityPlayers.Remove(player);
return HookResult.Continue;
}
}

View File

@@ -0,0 +1,391 @@
using System.Globalization;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
namespace CS2_SimpleAdmin_FunCommands;
/// <summary>
/// Menu creation methods for Fun Commands module.
/// This file demonstrates different menu patterns using SimpleAdmin API.
/// </summary>
public partial class CS2_SimpleAdmin_FunCommands
{
// =================================
// SIMPLE PLAYER SELECTION MENUS
// =================================
// Pattern: Direct player selection with immediate action
// Use CreateMenuWithPlayers when you just need to select a player and execute an action
/// <summary>
/// Creates a simple player selection menu for god mode.
/// PATTERN: CreateMenuWithPlayers with method reference
/// </summary>
private object CreateGodModeMenu(CCSPlayerController admin)
{
return _sharedApi!.CreateMenuWithPlayers(
Localizer?["fun_menu_god"] ?? "God Mode", // Menu title from translation
"fun", // Category ID (for back button navigation)
admin, // Admin opening the menu
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player), // Filter: only alive, targetable players
God); // Action to execute (method reference)
}
private object CreateNoClipMenu(CCSPlayerController admin)
{
return _sharedApi!.CreateMenuWithPlayers(
Localizer?["fun_menu_noclip"] ?? "No Clip",
"fun",
admin,
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player),
NoClip);
}
/// <summary>
/// Creates a player selection menu for respawn command.
/// PATTERN: CreateMenuWithPlayers with method reference
/// </summary>
private object CreateRespawnMenu(CCSPlayerController admin)
{
return _sharedApi!.CreateMenuWithPlayers(
Localizer?["fun_menu_respawn"] ?? "Respawn", // Menu title from translation
"fun", // Category ID
admin, // Admin
admin.CanTarget, // Filter: only targetable players (no LifeState check - can respawn dead players)
Respawn); // Use the Respawn method which includes death position teleport
}
// =================================
// NESTED MENUS - PLAYER → VALUE SELECTION
// =================================
// Pattern: First select player, then select a value/option for that player
// Use CreateMenuWithBack + AddSubMenu for multi-level menus
/// <summary>
/// Creates a nested menu: Player selection → Weapon selection.
/// PATTERN: CreateMenuWithBack + foreach + AddSubMenu
/// </summary>
private object CreateGiveWeaponMenu(CCSPlayerController admin)
{
var menu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_give"] ?? "Give Weapon",
"fun",
admin);
var players = _sharedApi.GetValidPlayers().Where(p =>
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
foreach (var player in players)
{
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
// AddSubMenu automatically adds a "Back" button to the submenu
// The lambda receives 'p' but we use captured 'player' variable (closure)
_sharedApi.AddSubMenu(menu, playerName, p => CreateWeaponSelectionMenu(admin, player));
}
return menu;
}
/// <summary>
/// Creates weapon selection submenu for a specific player.
/// PATTERN: CreateMenuWithBack + foreach + AddMenuOption
/// </summary>
private object CreateWeaponSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
{
var weaponMenu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_give_player", target.PlayerName] ?? $"Give Weapon: {target.PlayerName}",
"fun",
admin);
// Loop through cached weapons (performance optimization)
foreach (var weapon in GetWeaponsCache())
{
// AddMenuOption for each selectable option
// IMPORTANT: Always validate target.IsValid before executing action
_sharedApi.AddMenuOption(weaponMenu, weapon.Value.ToString(), _ =>
{
if (target.IsValid) // Player might disconnect before selection
{
target.GiveNamedItem(weapon.Value);
LogAndShowActivity(admin, target, "fun_admin_give_message", $"css_give", weapon.Value.ToString());
}
});
}
return weaponMenu;
}
private object CreateStripWeaponsMenu(CCSPlayerController admin)
{
return _sharedApi!.CreateMenuWithPlayers(
Localizer?["fun_menu_strip"] ?? "Strip Weapons",
"fun",
admin,
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player),
(adminPlayer, targetPlayer) =>
{
targetPlayer.RemoveWeapons();
LogAndShowActivity(adminPlayer, targetPlayer, "fun_admin_strip_message", "css_strip");
});
}
private object CreateFreezeMenu(CCSPlayerController admin)
{
return _sharedApi!.CreateMenuWithPlayers(
Localizer?["fun_menu_freeze"] ?? "Freeze",
"fun",
admin,
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player),
(adminPlayer, targetPlayer) => { Freeze(adminPlayer, targetPlayer, -1); });
}
/// <summary>
/// Creates a nested menu for setting player HP with predefined values.
/// PATTERN: Same as Give Weapon (player selection → value selection)
/// This is a common pattern you'll use frequently!
/// </summary>
private object CreateSetHpMenu(CCSPlayerController admin)
{
var menu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_hp"] ?? "Set HP",
"fun",
admin);
var players = _sharedApi.GetValidPlayers().Where(p =>
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
foreach (var player in players)
{
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
_sharedApi.AddSubMenu(menu, playerName, p => CreateHpSelectionMenu(admin, player));
}
return menu;
}
/// <summary>
/// Creates HP value selection submenu.
/// TIP: Use arrays for predefined values - easy to modify and maintain
/// </summary>
private object CreateHpSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
{
var hpSelectionMenu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_hp_player", target.PlayerName] ?? $"Set HP: {target.PlayerName}",
"fun",
admin);
// Predefined HP values - easy to customize
var hpValues = new[] { 1, 10, 25, 50, 100, 200, 500, 999 };
foreach (var hp in hpValues)
{
_sharedApi.AddMenuOption(hpSelectionMenu,
Localizer?["fun_menu_hp_value", hp] ?? $"{hp} HP",
_ =>
{
if (target.IsValid)
{
target.SetHp(hp);
LogAndShowActivity(admin, target, "fun_admin_hp_message", "css_hp", hp.ToString());
}
});
}
return hpSelectionMenu;
}
private object CreateSetSpeedMenu(CCSPlayerController admin)
{
var menu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_speed"] ?? "Set Speed",
"fun",
admin);
var players = _sharedApi.GetValidPlayers().Where(p =>
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
foreach (var player in players)
{
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
_sharedApi.AddSubMenu(menu, playerName, p => CreateSpeedSelectionMenu(admin, player));
}
return menu;
}
/// <summary>
/// Creates speed value selection submenu.
/// TIP: Use tuples (value, display) when you need different internal value vs display text
/// Example: (0.5f, "0.5") - float value for code, string for display
/// </summary>
private object CreateSpeedSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
{
var speedSelectionMenu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_speed_player", target.PlayerName] ?? $"Set Speed: {target.PlayerName}",
"fun",
admin);
// Tuple pattern: (actualValue, displayText)
// Useful when display text differs from actual value
var speedValues = new[]
{
(0.1f, "0.1"), (0.25f, "0.25"), (0.5f, "0.5"), (0.75f, "0.75"), (1f, "1"), (2f, "2"), (3f, "3"), (4f, "4")
};
foreach (var (speed, display) in speedValues)
{
_sharedApi.AddMenuOption(speedSelectionMenu,
Localizer?["fun_menu_speed_value", display] ?? $"Speed {display}",
_ =>
{
if (target.IsValid)
{
target.SetSpeed(speed);
// Track speed modification for timer
if (speed == 1f)
SpeedPlayers.Remove(target);
else
SpeedPlayers[target] = speed;
LogAndShowActivity(admin, target, "fun_admin_speed_message", "css_speed", speed.ToString(CultureInfo.InvariantCulture));
}
});
}
return speedSelectionMenu;
}
private object CreateSetGravityMenu(CCSPlayerController admin)
{
var menu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_gravity"] ?? "Set Gravity",
"fun",
admin);
var players = _sharedApi.GetValidPlayers().Where(p =>
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
foreach (var player in players)
{
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
_sharedApi.AddSubMenu(menu, playerName, p => CreateGravitySelectionMenu(admin, player));
}
return menu;
}
private object CreateGravitySelectionMenu(CCSPlayerController admin, CCSPlayerController target)
{
var gravitySelectionMenu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_gravity_player", target.PlayerName] ?? $"Set Gravity: {target.PlayerName}",
"fun",
admin);
var gravityValues = new[]
{ (0.1f, "0.1"), (0.25f, "0.25"), (0.5f, "0.5"), (0.75f, "0.75"), (1f, "1"), (2f, "2") };
foreach (var (gravity, display) in gravityValues)
{
_sharedApi.AddMenuOption(gravitySelectionMenu,
Localizer?["fun_menu_gravity_value", display] ?? $"Gravity {display}",
_ =>
{
if (target.IsValid)
{
target.SetGravity(Convert.ToSingle(gravity, CultureInfo.InvariantCulture));
// Track gravity modification for timer
if (gravity == 1f)
GravityPlayers.Remove(target);
else
GravityPlayers[target] = gravity;
LogAndShowActivity(admin, target, "fun_admin_gravity_message", "css_gravity", gravity.ToString(CultureInfo.InvariantCulture));
}
});
}
return gravitySelectionMenu;
}
private object CreateSetMoneyMenu(CCSPlayerController admin)
{
var menu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_money"] ?? "Set Money",
"fun",
admin);
var players = _sharedApi.GetValidPlayers().Where(p => admin.CanTarget(p));
foreach (var player in players)
{
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
_sharedApi.AddSubMenu(menu, playerName, p => CreateMoneySelectionMenu(admin, player));
}
return menu;
}
private object CreateMoneySelectionMenu(CCSPlayerController admin, CCSPlayerController target)
{
var moneySelectionMenu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_money_player", target.PlayerName] ?? $"Set Money: {target.PlayerName}",
"fun",
admin);
var moneyValues = new[] { 0, 1000, 2500, 5000, 10000, 16000 };
foreach (var money in moneyValues)
{
_sharedApi.AddMenuOption(moneySelectionMenu,
Localizer?["fun_menu_money_value", money] ?? $"${money}",
_ =>
{
if (target.IsValid)
{
target.SetMoney(money);
LogAndShowActivity(admin, target, "fun_admin_money_message", "css_money", money.ToString());
}
});
}
return moneySelectionMenu;
}
private object CreateSetResizeMenu(CCSPlayerController admin)
{
var menu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_resize"] ?? "Resize Player",
"fun",
admin);
var players = _sharedApi.GetValidPlayers().Where(p =>
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
foreach (var player in players)
{
var playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName;
_sharedApi.AddSubMenu(menu, playerName, p => CreateResizeSelectionMenu(admin, player));
}
return menu;
}
private object CreateResizeSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
{
var resizeSelectionMenu = _sharedApi!.CreateMenuWithBack(
Localizer?["fun_menu_resize_player", target.PlayerName] ?? $"Resize: {target.PlayerName}",
"fun",
admin);
var resizeValues = new[]
{ (0.5f, "0.5"), (0.75f, "0.75"), (1f, "1"), (1.25f, "1.25"), (1.5f, "1.5"), (2f, "2"), (3f, "3") };
foreach (var (resize, display) in resizeValues)
{
_sharedApi.AddMenuOption(resizeSelectionMenu,
Localizer?["fun_menu_resize_value", display] ?? $"Size {display}",
_ =>
{
if (target.IsValid)
{
Resize(admin, target, resize);
}
});
}
return resizeSelectionMenu;
}
}

View File

@@ -0,0 +1,271 @@
using System.Drawing;
using System.Globalization;
using System.Numerics;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.UserMessages;
namespace CS2_SimpleAdmin_FunCommands;
public static class PlayerExtensions
{
/// <summary>
/// Slaps the player pawn by applying optional damage and adding a random velocity knockback.
/// </summary>
/// <param name="pawn">The player pawn to slap.</param>
/// <param name="damage">The amount of damage to apply (default is 0).</param>
public static void Slap(this CBasePlayerPawn pawn, int damage = 0)
{
PerformSlap(pawn, damage);
}
/// <summary>
/// Determines if the player controller can target another player controller, respecting admin permissions and immunity.
/// </summary>
/// <param name="controller">The player controller who wants to target.</param>
/// <param name="target">The player controller being targeted.</param>
/// <returns>True if targeting is allowed, false otherwise.</returns>
public static bool CanTarget(this CCSPlayerController? controller, CCSPlayerController? target)
{
if (controller is null || target is null) return true;
if (target.IsBot) return true;
return AdminManager.CanPlayerTarget(controller, target) ||
AdminManager.CanPlayerTarget(new SteamID(controller.SteamID),
new SteamID(target.SteamID)) ||
AdminManager.GetPlayerImmunity(controller) >= AdminManager.GetPlayerImmunity(target);
}
/// <summary>
/// Checks if the controller can target a player by SteamID, considering targeting permissions and immunities.
/// </summary>
/// <param name="controller">The attacker player controller.</param>
/// <param name="steamId">The SteamID of the target player.</param>
/// <returns>True if targeting is permitted, false otherwise.</returns>
public static bool CanTarget(this CCSPlayerController? controller, SteamID steamId)
{
if (controller is null) return true;
return AdminManager.CanPlayerTarget(new SteamID(controller.SteamID), steamId) ||
AdminManager.GetPlayerImmunity(controller) >= AdminManager.GetPlayerImmunity(steamId);
}
/// <summary>
/// Sets the movement speed modifier of the player controller.
/// </summary>
/// <param name="controller">The player controller.</param>
/// <param name="speed">The speed modifier value.</param>
public static void SetSpeed(this CCSPlayerController? controller, float speed)
{
var playerPawnValue = controller?.PlayerPawn.Value;
if (playerPawnValue == null) return;
playerPawnValue.VelocityModifier = speed;
}
/// <summary>
/// Sets the gravity scale for the player controller.
/// </summary>
/// <param name="controller">The player controller.</param>
/// <param name="gravity">The gravity scale.</param>
public static void SetGravity(this CCSPlayerController? controller, float gravity)
{
var playerPawnValue = controller?.PlayerPawn.Value;
if (playerPawnValue == null) return;
playerPawnValue.ActualGravityScale = gravity;
}
/// <summary>
/// Sets the player's in-game money amount.
/// </summary>
/// <param name="controller">The player controller.</param>
/// <param name="money">The amount of money to set.</param>
public static void SetMoney(this CCSPlayerController? controller, int money)
{
var moneyServices = controller?.InGameMoneyServices;
if (moneyServices == null) return;
moneyServices.Account = money;
if (controller != null) Utilities.SetStateChanged(controller, "CCSPlayerController", "m_pInGameMoneyServices");
}
/// <summary>
/// Sets the player's health points.
/// </summary>
/// <param name="controller">The player controller.</param>
/// <param name="health">The health value, default is 100.</param>
public static void SetHp(this CCSPlayerController? controller, int health = 100)
{
if (controller == null) return;
if (health <= 0 || controller.PlayerPawn.Value == null || controller.PlayerPawn?.Value?.LifeState != (int)LifeState_t.LIFE_ALIVE) return;
controller.PlayerPawn.Value.Health = health;
if (health > 100)
{
controller.PlayerPawn.Value.MaxHealth = health;
}
Utilities.SetStateChanged(controller.PlayerPawn.Value, "CBaseEntity", "m_iHealth");
}
/// <summary>
/// Buries the player pawn by moving it down by a depth offset.
/// </summary>
/// <param name="pawn">The player pawn to bury.</param>
/// <param name="depth">The depth offset (default 10 units).</param>
public static void Bury(this CBasePlayerPawn pawn, float depth = 10f)
{
var newPos = new Vector3(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y,
pawn.AbsOrigin!.Z - depth);
var newRotation = new Vector3(pawn.AbsRotation!.X, pawn.AbsRotation.Y, pawn.AbsRotation.Z);
var newVelocity = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
pawn.Teleport(newPos, newRotation, newVelocity);
}
/// <summary>
/// Unburies the player pawn by moving it up by a depth offset.
/// </summary>
/// <param name="pawn">The player pawn to unbury.</param>
/// <param name="depth">The depth offset (default 15 units).</param>
public static void Unbury(this CBasePlayerPawn pawn, float depth = 15f)
{
var newPos = new Vector3(pawn.AbsOrigin!.X, pawn.AbsOrigin.Y,
pawn.AbsOrigin!.Z + depth);
var newRotation = new Vector3(pawn.AbsRotation!.X, pawn.AbsRotation.Y, pawn.AbsRotation.Z);
var newVelocity = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
pawn.Teleport(newPos, newRotation, newVelocity);
}
/// <summary>
/// Freezes the player pawn, disabling movement.
/// </summary>
/// <param name="pawn">The player pawn to freeze.</param>
public static void Freeze(this CBasePlayerPawn pawn)
{
pawn.MoveType = MoveType_t.MOVETYPE_INVALID;
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 11); // invalid
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
}
/// <summary>
/// Unfreezes the player pawn, enabling movement.
/// </summary>
/// <param name="pawn">The player pawn to unfreeze.</param>
public static void Unfreeze(this CBasePlayerPawn pawn)
{
pawn.MoveType = MoveType_t.MOVETYPE_WALK;
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 2); // walk
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
}
/// <summary>
/// Changes the player's color tint to specified RGBA values.
/// </summary>
/// <param name="pawn">The pawn to colorize.</param>
/// <param name="r">Red component (0-255).</param>
/// <param name="g">Green component (0-255).</param>
/// <param name="b">Blue component (0-255).</param>
/// <param name="a">Alpha (transparency) component (0-255).</param>
public static void Colorize(this CBasePlayerPawn pawn, int r = 255, int g = 255, int b = 255, int a = 255)
{
pawn.Render = Color.FromArgb(a, r, g, b);
Utilities.SetStateChanged(pawn, "CBaseModelEntity", "m_clrRender");
}
/// <summary>
/// Toggles noclip mode for the player pawn.
/// </summary>
/// <param name="pawn">The player pawn.</param>
public static void ToggleNoclip(this CBasePlayerPawn pawn)
{
if (pawn.MoveType == MoveType_t.MOVETYPE_NOCLIP)
{
pawn.MoveType = MoveType_t.MOVETYPE_WALK;
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 2); // walk
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
}
else
{
pawn.MoveType = MoveType_t.MOVETYPE_NOCLIP;
Schema.SetSchemaValue(pawn.Handle, "CBaseEntity", "m_nActualMoveType", 8); // noclip
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_MoveType");
}
}
/// <summary>
/// Teleports a player controller to the position, rotation, and velocity of another player controller.
/// </summary>
/// <param name="controller">The controller to teleport.</param>
/// <param name="target">The target controller whose position to copy.</param>
public static void TeleportPlayer(this CCSPlayerController? controller, CCSPlayerController? target)
{
if (controller?.PlayerPawn.Value == null && target?.PlayerPawn.Value == null)
return;
if (
controller?.PlayerPawn.Value is { AbsOrigin: not null, AbsRotation: not null } &&
target?.PlayerPawn.Value is { AbsOrigin: not null, AbsRotation: not null }
)
{
controller.PlayerPawn.Value.Teleport(
target.PlayerPawn.Value.AbsOrigin,
target.PlayerPawn.Value.AbsRotation,
target.PlayerPawn.Value.AbsVelocity
);
}
}
/// <summary>
/// Applies a slap effect to the given player pawn, optionally inflicting damage and adding velocity knockback.
/// </summary>
/// <param name="pawn">The player pawn to slap.</param>
/// <param name="damage">The amount of damage to deal (default is 0).</param>
private static void PerformSlap(CBasePlayerPawn pawn, int damage = 0)
{
if (pawn.LifeState != (int)LifeState_t.LIFE_ALIVE)
return;
var controller = pawn.Controller.Value?.As<CCSPlayerController>();
/* Teleport in a random direction - thank you, Mani!*/
/* Thank you AM & al!*/
var random = new Random();
var vel = new Vector3(pawn.AbsVelocity.X, pawn.AbsVelocity.Y, pawn.AbsVelocity.Z);
vel.X += (random.Next(180) + 50) * (random.Next(2) == 1 ? -1 : 1);
vel.Y += (random.Next(180) + 50) * (random.Next(2) == 1 ? -1 : 1);
vel.Z += random.Next(200) + 100;
pawn.AbsVelocity.X = vel.X;
pawn.AbsVelocity.Y = vel.Y;
pawn.AbsVelocity.Z = vel.Z;
if (controller != null && controller.IsValid)
{
var shakeMessage = UserMessage.FromPartialName("Shake");
shakeMessage.SetFloat("duration", 1);
shakeMessage.SetFloat("amplitude", 10);
shakeMessage.SetFloat("frequency", 1f);
shakeMessage.SetInt("command", 0);
shakeMessage.Recipients.Add(controller);
shakeMessage.Send();
}
if (damage <= 0)
return;
pawn.Health -= damage;
Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth");
if (pawn.Health <= 0)
pawn.CommitSuicide(true, true);
}
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "أوامر المرح",
"fun_admin_give_message": "{lightred}{0}{default} أعطى {lightred}{1}{default} {lightred}{2}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} أخذ جميع أسلحة اللاعب {lightred}{1}{default}!",
"fun_admin_hp_message": "{lightred}{0}{default} غيّر عدد نقاط الحياة لـ {lightred}{1}{default}!",
"fun_admin_speed_message": "{lightred}{0}{default} غيّر السرعة لـ {lightred}{1}{default}!",
"fun_admin_gravity_message": "{lightred}{0}{default} غيّر الجاذبية لـ {lightred}{1}{default}!",
"fun_admin_money_message": "{lightred}{0}{default} غيّر المال لـ {lightred}{1}{default}!",
"fun_admin_god_message": "{lightred}{0}{default} غيّر وضع الله لـ {lightred}{1}{default}!",
"fun_admin_noclip_message": "{lightred}{0}{default} فعّل/ألغى نمط اللا تصادم لـ {lightred}{1}{default}!",
"fun_admin_freeze_message": "{lightred}{0}{default} جمد {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} أذاب {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} أحيى {lightred}{1}{default}!",
"fun_menu_god": "وضع الله",
"fun_menu_noclip": "اللا تصادم",
"fun_menu_respawn": "إحياء",
"fun_menu_give": "إعطاء سلاح",
"fun_menu_give_player": "إعطاء سلاح: {0}",
"fun_menu_strip": "نزع الأسلحة",
"fun_menu_freeze": "تجميد",
"fun_menu_hp": "ضبط الحياة",
"fun_menu_hp_player": "ضبط الحياة: {0}",
"fun_menu_speed": "ضبط السرعة",
"fun_menu_speed_player": "ضبط السرعة: {0}",
"fun_menu_gravity": "ضبط الجاذبية",
"fun_menu_gravity_player": "ضبط الجاذبية: {0}",
"fun_menu_money": "ضبط المال",
"fun_menu_money_player": "ضبط المال: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "السرعة {0}",
"fun_menu_gravity_value": "الجاذبية {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} غيّر حجم {lightred}{1}{default} إلى {lightred}{2}{default}!",
"fun_menu_resize": "تغيير الحجم",
"fun_menu_resize_player": "تغيير الحجم: {0}",
"fun_menu_resize_value": "الحجم {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Spaß-Befehle",
"fun_admin_give_message": "{lightred}{0}{default} hat {lightred}{1}{default} ein {lightred}{2}{default} gegeben!",
"fun_admin_strip_message": "{lightred}{0}{default} hat alle Waffen von Spieler {lightred}{1}{default} entfernt!",
"fun_admin_hp_message": "{lightred}{0}{default} hat die Lebenspunkte von {lightred}{1}{default} geändert!",
"fun_admin_speed_message": "{lightred}{0}{default} hat die Geschwindigkeit von {lightred}{1}{default} geändert!",
"fun_admin_gravity_message": "{lightred}{0}{default} hat die Schwerkraft von {lightred}{1}{default} geändert!",
"fun_admin_money_message": "{lightred}{0}{default} hat das Geld von {lightred}{1}{default} geändert!",
"fun_admin_god_message": "{lightred}{0}{default} hat den Gottmodus von {lightred}{1}{default} geändert!",
"fun_admin_noclip_message": "{lightred}{0}{default} hat den Noclip-Modus für {lightred}{1}{default} aktiviert/deaktiviert!",
"fun_admin_freeze_message": "{lightred}{0}{default} hat {lightred}{1}{default} eingefroren!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} hat {lightred}{1}{default} aufgetaut!",
"fun_admin_respawn_message": "{lightred}{0}{default} hat {lightred}{1}{default} wiederbelebt!",
"fun_menu_god": "Gottmodus",
"fun_menu_noclip": "Noclip",
"fun_menu_respawn": "Wiederbeleben",
"fun_menu_give": "Waffe Geben",
"fun_menu_give_player": "Waffe Geben: {0}",
"fun_menu_strip": "Waffen Entfernen",
"fun_menu_freeze": "Einfrieren",
"fun_menu_hp": "HP Festlegen",
"fun_menu_hp_player": "HP Festlegen: {0}",
"fun_menu_speed": "Geschwindigkeit Festlegen",
"fun_menu_speed_player": "Geschwindigkeit Festlegen: {0}",
"fun_menu_gravity": "Schwerkraft Festlegen",
"fun_menu_gravity_player": "Schwerkraft Festlegen: {0}",
"fun_menu_money": "Geld Festlegen",
"fun_menu_money_player": "Geld Festlegen: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Geschwindigkeit {0}",
"fun_menu_gravity_value": "Schwerkraft {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} hat die Größe von {lightred}{1}{default} auf {lightred}{2}{default} geändert!",
"fun_menu_resize": "Größe Ändern",
"fun_menu_resize_player": "Größe Ändern: {0}",
"fun_menu_resize_value": "Größe {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Fun Commands",
"fun_admin_give_message": "{lightred}{0}{default} gave {lightred}{1}{default} a {lightred}{2}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} took all of player {lightred}{1}{default} weapons!",
"fun_admin_hp_message": "{lightred}{0}{default} changed {lightred}{1}{default} hp amount!",
"fun_admin_speed_message": "{lightred}{0}{default} changed speed for {lightred}{1}{default}!",
"fun_admin_gravity_message": "{lightred}{0}{default} changed gravity for {lightred}{1}{default}!",
"fun_admin_money_message": "{lightred}{0}{default} changed money for {lightred}{1}{default}!",
"fun_admin_god_message": "{lightred}{0}{default} changed god mode for {lightred}{1}{default}!",
"fun_admin_noclip_message": "{lightred}{0}{default} toggled noclip for {lightred}{1}{default}!",
"fun_admin_freeze_message": "{lightred}{0}{default} froze {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} unfroze {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} respawned {lightred}{1}{default}!",
"fun_menu_god": "God Mode",
"fun_menu_noclip": "No Clip",
"fun_menu_respawn": "Respawn",
"fun_menu_give": "Give Weapon",
"fun_menu_give_player": "Give Weapon: {0}",
"fun_menu_strip": "Strip Weapons",
"fun_menu_freeze": "Freeze",
"fun_menu_hp": "Set HP",
"fun_menu_hp_player": "Set HP: {0}",
"fun_menu_speed": "Set Speed",
"fun_menu_speed_player": "Set Speed: {0}",
"fun_menu_gravity": "Set Gravity",
"fun_menu_gravity_player": "Set Gravity: {0}",
"fun_menu_money": "Set Money",
"fun_menu_money_player": "Set Money: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Speed {0}",
"fun_menu_gravity_value": "Gravity {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} resized {lightred}{1}{default} to {lightred}{2}{default}!",
"fun_menu_resize": "Resize Player",
"fun_menu_resize_player": "Resize: {0}",
"fun_menu_resize_value": "Size {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Comandos Divertidos",
"fun_admin_give_message": "{lightred}{0}{default} dio {lightred}{1}{default} un {lightred}{2}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} quitó todas las armas del jugador {lightred}{1}{default}!",
"fun_admin_hp_message": "{lightred}{0}{default} cambió la cantidad de HP de {lightred}{1}{default}!",
"fun_admin_speed_message": "{lightred}{0}{default} cambió la velocidad de {lightred}{1}{default}!",
"fun_admin_gravity_message": "{lightred}{0}{default} cambió la gravedad de {lightred}{1}{default}!",
"fun_admin_money_message": "{lightred}{0}{default} cambió el dinero de {lightred}{1}{default}!",
"fun_admin_god_message": "{lightred}{0}{default} cambió el modo dios de {lightred}{1}{default}!",
"fun_admin_noclip_message": "{lightred}{0}{default} alternó noclip para {lightred}{1}{default}!",
"fun_admin_freeze_message": "{lightred}{0}{default} congeló a {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} descongeló a {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} reapareció a {lightred}{1}{default}!",
"fun_menu_god": "Modo Dios",
"fun_menu_noclip": "Noclip",
"fun_menu_respawn": "Reaparecer",
"fun_menu_give": "Dar Arma",
"fun_menu_give_player": "Dar Arma: {0}",
"fun_menu_strip": "Quitar Armas",
"fun_menu_freeze": "Congelar",
"fun_menu_hp": "Establecer HP",
"fun_menu_hp_player": "Establecer HP: {0}",
"fun_menu_speed": "Establecer Velocidad",
"fun_menu_speed_player": "Establecer Velocidad: {0}",
"fun_menu_gravity": "Establecer Gravedad",
"fun_menu_gravity_player": "Establecer Gravedad: {0}",
"fun_menu_money": "Establecer Dinero",
"fun_menu_money_player": "Establecer Dinero: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Velocidad {0}",
"fun_menu_gravity_value": "Gravedad {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} cambió el tamaño de {lightred}{1}{default} a {lightred}{2}{default}!",
"fun_menu_resize": "Cambiar Tamaño",
"fun_menu_resize_player": "Cambiar Tamaño: {0}",
"fun_menu_resize_value": "Tamaño {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "دستورات سرگرمی",
"fun_admin_give_message": "{lightred}{0}{default} {lightred}{2}{default} را به {lightred}{1}{default} داد!",
"fun_admin_strip_message": "{lightred}{0}{default} تمام سلاح‌های بازیکن {lightred}{1}{default} را گرفت!",
"fun_admin_hp_message": "{lightred}{0}{default} مقدار سلامت {lightred}{1}{default} را تغییر داد!",
"fun_admin_speed_message": "{lightred}{0}{default} سرعت {lightred}{1}{default} را تغییر داد!",
"fun_admin_gravity_message": "{lightred}{0}{default} جاذبه {lightred}{1}{default} را تغییر داد!",
"fun_admin_money_message": "{lightred}{0}{default} پول {lightred}{1}{default} را تغییر داد!",
"fun_admin_god_message": "{lightred}{0}{default} حالت خدا را برای {lightred}{1}{default} تغییر داد!",
"fun_admin_noclip_message": "{lightred}{0}{default} ناپدیدی را برای {lightred}{1}{default} فعال/غیرفعال کرد!",
"fun_admin_freeze_message": "{lightred}{0}{default} {lightred}{1}{default} را یخ‌زده کرد!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} {lightred}{1}{default} را از حالت یخ خارج کرد!",
"fun_admin_respawn_message": "{lightred}{0}{default} {lightred}{1}{default} را دوباره زنده کرد!",
"fun_menu_god": "حالت خدا",
"fun_menu_noclip": "ناپدیدی",
"fun_menu_respawn": "احیا",
"fun_menu_give": "دادن سلاح",
"fun_menu_give_player": "دادن سلاح: {0}",
"fun_menu_strip": "گرفتن سلاح‌ها",
"fun_menu_freeze": "یخ زدن",
"fun_menu_hp": "تنظیم سلامت",
"fun_menu_hp_player": "تنظیم سلامت: {0}",
"fun_menu_speed": "تنظیم سرعت",
"fun_menu_speed_player": "تنظیم سرعت: {0}",
"fun_menu_gravity": "تنظیم جاذبه",
"fun_menu_gravity_player": "تنظیم جاذبه: {0}",
"fun_menu_money": "تنظیم پول",
"fun_menu_money_player": "تنظیم پول: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "سرعت {0}",
"fun_menu_gravity_value": "جاذبه {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} اندازه {lightred}{1}{default} را به {lightred}{2}{default} تغییر داد!",
"fun_menu_resize": "تغییر اندازه",
"fun_menu_resize_player": "تغییر اندازه: {0}",
"fun_menu_resize_value": "اندازه {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Commandes Amusantes",
"fun_admin_give_message": "{lightred}{0}{default} a donné {lightred}{2}{default} à {lightred}{1}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} a retiré toutes les armes de {lightred}{1}{default}!",
"fun_admin_hp_message": "{lightred}{0}{default} a modifié la quantité de HP de {lightred}{1}{default}!",
"fun_admin_speed_message": "{lightred}{0}{default} a modifié la vitesse de {lightred}{1}{default}!",
"fun_admin_gravity_message": "{lightred}{0}{default} a modifié la gravité de {lightred}{1}{default}!",
"fun_admin_money_message": "{lightred}{0}{default} a modifié l'argent de {lightred}{1}{default}!",
"fun_admin_god_message": "{lightred}{0}{default} a modifié le mode dieu de {lightred}{1}{default}!",
"fun_admin_noclip_message": "{lightred}{0}{default} a activé/désactivé le noclip pour {lightred}{1}{default}!",
"fun_admin_freeze_message": "{lightred}{0}{default} a gelé {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} a dégivré {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} a réapparu {lightred}{1}{default}!",
"fun_menu_god": "Mode Dieu",
"fun_menu_noclip": "Noclip",
"fun_menu_respawn": "Réapparition",
"fun_menu_give": "Donner Arme",
"fun_menu_give_player": "Donner Arme: {0}",
"fun_menu_strip": "Retirer Armes",
"fun_menu_freeze": "Geler",
"fun_menu_hp": "Définir HP",
"fun_menu_hp_player": "Définir HP: {0}",
"fun_menu_speed": "Définir Vitesse",
"fun_menu_speed_player": "Définir Vitesse: {0}",
"fun_menu_gravity": "Définir Gravité",
"fun_menu_gravity_player": "Définir Gravité: {0}",
"fun_menu_money": "Définir Argent",
"fun_menu_money_player": "Définir Argent: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Vitesse {0}",
"fun_menu_gravity_value": "Gravité {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} a redimensionné {lightred}{1}{default} à {lightred}{2}{default}!",
"fun_menu_resize": "Redimensionner",
"fun_menu_resize_player": "Redimensionner: {0}",
"fun_menu_resize_value": "Taille {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Izklaidējošas Komandas",
"fun_admin_give_message": "{lightred}{0}{default} iedeva {lightred}{1}{default} {lightred}{2}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} noņēma visus {lightred}{1}{default} ieročus!",
"fun_admin_hp_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} HP daudzumu!",
"fun_admin_speed_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} ātrumu!",
"fun_admin_gravity_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} gravitāciju!",
"fun_admin_money_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} naudu!",
"fun_admin_god_message": "{lightred}{0}{default} mainīja dieva režīmu priekš {lightred}{1}{default}!",
"fun_admin_noclip_message": "{lightred}{0}{default} aktivizēja/deaktivizēja noclip priekš {lightred}{1}{default}!",
"fun_admin_freeze_message": "{lightred}{0}{default} sasaldēja {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} atkausēja {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} atdzīvināja {lightred}{1}{default}!",
"fun_menu_god": "Dieva Režīms",
"fun_menu_noclip": "Noclip",
"fun_menu_respawn": "Atdzīvināt",
"fun_menu_give": "Dot Ieroci",
"fun_menu_give_player": "Dot Ieroci: {0}",
"fun_menu_strip": "Noņemt Ieročus",
"fun_menu_freeze": "Sasaldēt",
"fun_menu_hp": "Uzstādīt HP",
"fun_menu_hp_player": "Uzstādīt HP: {0}",
"fun_menu_speed": "Uzstādīt Ātrumu",
"fun_menu_speed_player": "Uzstādīt Ātrumu: {0}",
"fun_menu_gravity": "Uzstādīt Gravitāciju",
"fun_menu_gravity_player": "Uzstādīt Gravitāciju: {0}",
"fun_menu_money": "Uzstādīt Naudu",
"fun_menu_money_player": "Uzstādīt Naudu: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Ātrums {0}",
"fun_menu_gravity_value": "Gravitācija {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} mainīja {lightred}{1}{default} izmēru uz {lightred}{2}{default}!",
"fun_menu_resize": "Mainīt Izmēru",
"fun_menu_resize_player": "Mainīt Izmēru: {0}",
"fun_menu_resize_value": "Izmērs {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Komendy Rozrywkowe",
"fun_admin_give_message": "{lightred}{0}{default} dał {lightred}{1}{default} przedmiot {lightred}{2}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} zabrał wszystkie bronie {lightred}{1}{default}!",
"fun_admin_hp_message": "{lightred}{0}{default} zmienił ilość hp dla {lightred}{1}{default}!",
"fun_admin_speed_message": "{lightred}{0}{default} zmienił prędkość dla {lightred}{1}{default}!",
"fun_admin_gravity_message": "{lightred}{0}{default} zmienił grawitację dla {lightred}{1}{default}!",
"fun_admin_money_message": "{lightred}{0}{default} zmienił pieniądze dla {lightred}{1}{default}!",
"fun_admin_god_message": "{lightred}{0}{default} zmienił tryb Boga dla {lightred}{1}{default}!",
"fun_admin_noclip_message": "{lightred}{0}{default} ustawił latanie dla {lightred}{1}{default}!",
"fun_admin_freeze_message": "{lightred}{0}{default} zamroził {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} odmroził {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} odrodził {lightred}{1}{default}!",
"fun_menu_god": "Tryb Boga",
"fun_menu_noclip": "Latanie",
"fun_menu_respawn": "Odrodzenie",
"fun_menu_give": "Daj Broń",
"fun_menu_give_player": "Daj Broń: {0}",
"fun_menu_strip": "Zabierz Bronie",
"fun_menu_freeze": "Zamrożenie",
"fun_menu_hp": "Ustaw HP",
"fun_menu_hp_player": "Ustaw HP: {0}",
"fun_menu_speed": "Ustaw Prędkość",
"fun_menu_speed_player": "Ustaw Prędkość: {0}",
"fun_menu_gravity": "Ustaw Grawitację",
"fun_menu_gravity_player": "Ustaw Grawitację: {0}",
"fun_menu_money": "Ustaw Pieniądze",
"fun_menu_money_player": "Ustaw Pieniądze: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Prędkość {0}",
"fun_menu_gravity_value": "Grawitacja {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} zmienił rozmiar {lightred}{1}{default} na {lightred}{2}{default}!",
"fun_menu_resize": "Zmień Rozmiar",
"fun_menu_resize_player": "Zmień Rozmiar: {0}",
"fun_menu_resize_value": "Rozmiar {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Comandos Divertidos",
"fun_admin_give_message": "{lightred}{0}{default} deu {lightred}{1}{default} um(a) {lightred}{2}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} tirou todas as armas de {lightred}{1}{default}!",
"fun_admin_hp_message": "{lightred}{0}{default} mudou a quantidade de HP de {lightred}{1}{default}!",
"fun_admin_speed_message": "{lightred}{0}{default} mudou a velocidade de {lightred}{1}{default}!",
"fun_admin_gravity_message": "{lightred}{0}{default} mudou a gravidade de {lightred}{1}{default}!",
"fun_admin_money_message": "{lightred}{0}{default} mudou a quantidade de dinheiro de {lightred}{1}{default}!",
"fun_admin_god_message": "{lightred}{0}{default} mudou o modo Deus de {lightred}{1}{default}!",
"fun_admin_noclip_message": "{lightred}{0}{default} ativou/desativou o noclip para {lightred}{1}{default}!",
"fun_admin_freeze_message": "{lightred}{0}{default} congelou {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} descongelou {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} reanimou {lightred}{1}{default}!",
"fun_menu_god": "Modo Deus",
"fun_menu_noclip": "Noclip",
"fun_menu_respawn": "Reanimar",
"fun_menu_give": "Dar Arma",
"fun_menu_give_player": "Dar Arma: {0}",
"fun_menu_strip": "Remover Armas",
"fun_menu_freeze": "Congelar",
"fun_menu_hp": "Definir HP",
"fun_menu_hp_player": "Definir HP: {0}",
"fun_menu_speed": "Definir Velocidade",
"fun_menu_speed_player": "Definir Velocidade: {0}",
"fun_menu_gravity": "Definir Gravidade",
"fun_menu_gravity_player": "Definir Gravidade: {0}",
"fun_menu_money": "Definir Dinheiro",
"fun_menu_money_player": "Definir Dinheiro: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Velocidade {0}",
"fun_menu_gravity_value": "Gravidade {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} redimensionou {lightred}{1}{default} para {lightred}{2}{default}!",
"fun_menu_resize": "Redimensionar",
"fun_menu_resize_player": "Redimensionar: {0}",
"fun_menu_resize_value": "Tamanho {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Comandos Divertidos",
"fun_admin_give_message": "{lightred}{0}{default} deu {lightred}{1}{default} um(a) {lightred}{2}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} tirou todas as armas de {lightred}{1}{default}!",
"fun_admin_hp_message": "{lightred}{0}{default} mudou a quantidade de HP de {lightred}{1}{default}!",
"fun_admin_speed_message": "{lightred}{0}{default} mudou a velocidade de {lightred}{1}{default}!",
"fun_admin_gravity_message": "{lightred}{0}{default} mudou a gravidade de {lightred}{1}{default}!",
"fun_admin_money_message": "{lightred}{0}{default} mudou a quantidade de dinheiro de {lightred}{1}{default}!",
"fun_admin_god_message": "{lightred}{0}{default} mudou o modo Deus de {lightred}{1}{default}!",
"fun_admin_noclip_message": "{lightred}{0}{default} ativou/desativou o noclip para {lightred}{1}{default}!",
"fun_admin_freeze_message": "{lightred}{0}{default} congelou {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} descongelou {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} reanimou {lightred}{1}{default}!",
"fun_menu_god": "Modo Deus",
"fun_menu_noclip": "Noclip",
"fun_menu_respawn": "Reanimar",
"fun_menu_give": "Dar Arma",
"fun_menu_give_player": "Dar Arma: {0}",
"fun_menu_strip": "Remover Armas",
"fun_menu_freeze": "Congelar",
"fun_menu_hp": "Definir HP",
"fun_menu_hp_player": "Definir HP: {0}",
"fun_menu_speed": "Definir Velocidade",
"fun_menu_speed_player": "Definir Velocidade: {0}",
"fun_menu_gravity": "Definir Gravidade",
"fun_menu_gravity_player": "Definir Gravidade: {0}",
"fun_menu_money": "Definir Dinheiro",
"fun_menu_money_player": "Definir Dinheiro: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Velocidade {0}",
"fun_menu_gravity_value": "Gravidade {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} redimensionou {lightred}{1}{default} para {lightred}{2}{default}!",
"fun_menu_resize": "Redimensionar",
"fun_menu_resize_player": "Redimensionar: {0}",
"fun_menu_resize_value": "Tamanho {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Развлекательные Команды",
"fun_admin_give_message": "{lightred}{0}{default} дал {lightred}{1}{default} {lightred}{2}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} забрал все оружие у {lightred}{1}{default}!",
"fun_admin_hp_message": "{lightred}{0}{default} изменил количество HP у {lightred}{1}{default}!",
"fun_admin_speed_message": "{lightred}{0}{default} изменил скорость {lightred}{1}{default}!",
"fun_admin_gravity_message": "{lightred}{0}{default} изменил гравитацию для {lightred}{1}{default}!",
"fun_admin_money_message": "{lightred}{0}{default} изменил количество денег у {lightred}{1}{default}!",
"fun_admin_god_message": "{lightred}{0}{default} изменил режим бога для {lightred}{1}{default}!",
"fun_admin_noclip_message": "{lightred}{0}{default} включил/выключил noclip для {lightred}{1}{default}!",
"fun_admin_freeze_message": "{lightred}{0}{default} заморозил {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} разморозил {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} возродил {lightred}{1}{default}!",
"fun_menu_god": "Режим Бога",
"fun_menu_noclip": "Noclip",
"fun_menu_respawn": "Возрождение",
"fun_menu_give": "Выдать Оружие",
"fun_menu_give_player": "Выдать Оружие: {0}",
"fun_menu_strip": "Забрать Оружие",
"fun_menu_freeze": "Заморозить",
"fun_menu_hp": "Установить HP",
"fun_menu_hp_player": "Установить HP: {0}",
"fun_menu_speed": "Установить Скорость",
"fun_menu_speed_player": "Установить Скорость: {0}",
"fun_menu_gravity": "Установить Гравитацию",
"fun_menu_gravity_player": "Установить Гравитацию: {0}",
"fun_menu_money": "Установить Деньги",
"fun_menu_money_player": "Установить Деньги: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Скорость {0}",
"fun_menu_gravity_value": "Гравитация {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} изменил размер {lightred}{1}{default} на {lightred}{2}{default}!",
"fun_menu_resize": "Изменить Размер",
"fun_menu_resize_player": "Изменить Размер: {0}",
"fun_menu_resize_value": "Размер {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "Eğlence Komutları",
"fun_admin_give_message": "{lightred}{0}{default} {lightred}{1}{default}'e {lightred}{2}{default} verdi!",
"fun_admin_strip_message": "{lightred}{0}{default} {lightred}{1}{default}'in tüm silahlarını aldı!",
"fun_admin_hp_message": "{lightred}{0}{default} {lightred}{1}{default}'in HP miktarını değiştirdi!",
"fun_admin_speed_message": "{lightred}{0}{default} {lightred}{1}{default}'in hızını değiştirdi!",
"fun_admin_gravity_message": "{lightred}{0}{default} {lightred}{1}{default}'in yer çekimini değiştirdi!",
"fun_admin_money_message": "{lightred}{0}{default} {lightred}{1}{default}'in parasını değiştirdi!",
"fun_admin_god_message": "{lightred}{0}{default} {lightred}{1}{default}'in tanrı modunu değiştirdi!",
"fun_admin_noclip_message": "{lightred}{0}{default} {lightred}{1}{default} için noclip'i açtı/kapatı!",
"fun_admin_freeze_message": "{lightred}{0}{default} {lightred}{1}{default}'i dondurdu!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} {lightred}{1}{default}'in dondurmasını çözdü!",
"fun_admin_respawn_message": "{lightred}{0}{default} {lightred}{1}{default}'i yeniden doğurdu!",
"fun_menu_god": "Tanrı Modu",
"fun_menu_noclip": "Noclip",
"fun_menu_respawn": "Yeniden Doğma",
"fun_menu_give": "Silah Ver",
"fun_menu_give_player": "Silah Ver: {0}",
"fun_menu_strip": "Silahları Al",
"fun_menu_freeze": "Dondur",
"fun_menu_hp": "HP Ayarla",
"fun_menu_hp_player": "HP Ayarla: {0}",
"fun_menu_speed": "Hız Ayarla",
"fun_menu_speed_player": "Hız Ayarla: {0}",
"fun_menu_gravity": "Yer Çekimi Ayarla",
"fun_menu_gravity_player": "Yer Çekimi Ayarla: {0}",
"fun_menu_money": "Para Ayarla",
"fun_menu_money_player": "Para Ayarla: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "Hız {0}",
"fun_menu_gravity_value": "Yer Çekimi {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} {lightred}{1}{default}'in boyutunu {lightred}{2}{default} olarak değiştirdi!",
"fun_menu_resize": "Boyut Değiştir",
"fun_menu_resize_player": "Boyut Değiştir: {0}",
"fun_menu_resize_value": "Boyut {0}"
}

View File

@@ -0,0 +1,40 @@
{
"fun_category_name": "趣味命令",
"fun_admin_give_message": "{lightred}{0}{default} 给了 {lightred}{1}{default} {lightred}{2}{default}!",
"fun_admin_strip_message": "{lightred}{0}{default} 移除了 {lightred}{1}{default} 的所有武器!",
"fun_admin_hp_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的生命值!",
"fun_admin_speed_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的速度!",
"fun_admin_gravity_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的重力!",
"fun_admin_money_message": "{lightred}{0}{default} 修改了 {lightred}{1}{default} 的金钱!",
"fun_admin_god_message": "{lightred}{0}{default} 切换了 {lightred}{1}{default} 的上帝模式!",
"fun_admin_noclip_message": "{lightred}{0}{default} 切换了 {lightred}{1}{default} 的穿墙模式!",
"fun_admin_freeze_message": "{lightred}{0}{default} 冻结了 {lightred}{1}{default}!",
"fun_admin_unfreeze_message": "{lightred}{0}{default} 解冻了 {lightred}{1}{default}!",
"fun_admin_respawn_message": "{lightred}{0}{default} 复活了 {lightred}{1}{default}!",
"fun_menu_god": "上帝模式",
"fun_menu_noclip": "穿墙模式",
"fun_menu_respawn": "复活",
"fun_menu_give": "给予武器",
"fun_menu_give_player": "给予武器: {0}",
"fun_menu_strip": "移除武器",
"fun_menu_freeze": "冻结",
"fun_menu_hp": "设置生命值",
"fun_menu_hp_player": "设置生命值: {0}",
"fun_menu_speed": "设置速度",
"fun_menu_speed_player": "设置速度: {0}",
"fun_menu_gravity": "设置重力",
"fun_menu_gravity_player": "设置重力: {0}",
"fun_menu_money": "设置金钱",
"fun_menu_money_player": "设置金钱: {0}",
"fun_menu_hp_value": "{0} HP",
"fun_menu_speed_value": "速度 {0}",
"fun_menu_gravity_value": "重力 {0}",
"fun_menu_money_value": "${0}",
"fun_admin_resize_message": "{lightred}{0}{default} 将 {lightred}{1}{default} 的大小改为 {lightred}{2}{default}!",
"fun_menu_resize": "调整大小",
"fun_menu_resize_player": "调整大小: {0}",
"fun_menu_resize_value": "大小 {0}"
}

View File

@@ -0,0 +1,384 @@
# CS2-SimpleAdmin Fun Commands Module
This module serves as a **reference implementation** for creating CS2-SimpleAdmin modules. It demonstrates best practices for menu creation, command registration, translation support, and API usage.
## 📚 What This Module Teaches
This module is designed to be educational and shows you how to:
1.**Register commands dynamically** from configuration
2.**Create menu categories** and menu items
3.**Use per-player translations** with `ShowAdminActivityLocalized`
4.**Handle player targeting** and validation
5.**Implement proper cleanup** on module unload
6.**Structure code** using partial classes for organization
7.**Cache data** for performance (weapons cache)
8.**Use configuration** to enable/disable features
## 🎯 Features
This module provides fun admin commands:
- **God Mode** (`css_god`) - Toggle god mode for players
- **No Clip** (`css_noclip`) - Enable no-clip mode
- **Freeze/Unfreeze** (`css_freeze`, `css_unfreeze`) - Freeze players in place
- **Respawn** (`css_respawn`) - Respawn dead players
- **Give Weapon** (`css_give`) - Give weapons to players
- **Strip Weapons** (`css_strip`) - Remove all player weapons
- **Set HP** (`css_hp`) - Set player health
- **Set Speed** (`css_speed`) - Modify player movement speed
- **Set Gravity** (`css_gravity`) - Change player gravity
- **Set Money** (`css_money`) - Set player money
## 📁 File Structure
```
CS2-SimpleAdmin_FunCommands/
├── CS2-SimpleAdmin_FunCommands.cs # Main plugin file - initialization, registration
├── Commands.cs # Command handlers
├── Actions.cs # Action methods (God, NoClip, Freeze, etc.)
├── Menus.cs # Menu creation using SimpleAdmin API
├── Config.cs # Configuration with command lists
└── lang/ # Translation files (13 languages)
├── en.json
├── pl.json
├── ru.json
└── ... (10 more languages)
```
## 🔍 Code Organization Explained
### 1. Main Plugin File (`CS2-SimpleAdmin_FunCommands.cs`)
**Key Concepts Demonstrated:**
```csharp
public partial class CS2_SimpleAdmin_FunCommands : BasePlugin, IPluginConfig<Config>
{
// ✅ BEST PRACTICE: Use capability system to get API
private ICS2_SimpleAdminApi? _sharedApi;
private readonly PluginCapability<ICS2_SimpleAdminApi> _pluginCapability = new("simpleadmin:api");
// ✅ BEST PRACTICE: Cache expensive data
private static Dictionary<int, CsItem>? _weaponsCache;
// ✅ BEST PRACTICE: Track menu registration state
private bool _menusRegistered = false;
public override void OnAllPluginsLoaded(bool hotReload)
{
// Get the API
_sharedApi = _pluginCapability.Get();
// Register commands
RegisterFunCommands();
// ✅ BEST PRACTICE: Wait for SimpleAdmin to be ready before registering menus
_sharedApi.OnSimpleAdminReady += RegisterFunMenus;
RegisterFunMenus(); // Fallback for hot reload
}
}
```
**Why partial classes?**
- Separates concerns (commands, actions, menus)
- Makes code easier to navigate
- Each file has a specific purpose
### 2. Configuration (`Config.cs`)
**Key Concept:** Command lists for flexibility
```csharp
public class Config : IBasePluginConfig
{
// ✅ BEST PRACTICE: Allow multiple command aliases
public List<string> NoclipCommands { get; set; } = ["css_noclip"];
public List<string> GodCommands { get; set; } = ["css_god"];
// ... more command lists
}
```
**Benefits:**
- Users can disable features by emptying the list
- Users can add command aliases (e.g., `["css_god", "css_godmode"]`)
- Menus only register if commands exist
### 3. Commands (`Commands.cs`)
**Key Concepts Demonstrated:**
```csharp
[CommandHelper(1, "<#userid or name>")]
[RequiresPermissions("@css/cheats")]
private void OnGodCommand(CCSPlayerController? caller, CommandInfo command)
{
// ✅ BEST PRACTICE: Use API to get targets (handles target syntax)
var targets = _sharedApi!.GetTarget(command);
if (targets == null) return;
// ✅ BEST PRACTICE: Filter for alive players
var playersToTarget = targets.Players.Where(player =>
player is { IsValid: true, IsHLTV: false, PlayerPawn.Value.LifeState: (int)LifeState_t.LIFE_ALIVE }).ToList();
// ✅ BEST PRACTICE: Check targeting permissions
playersToTarget.ForEach(player =>
{
if (caller!.CanTarget(player))
{
God(caller, player);
}
});
// ✅ BEST PRACTICE: Always log commands
_sharedApi.LogCommand(caller, command);
}
```
### 4. Actions (`Actions.cs`)
**Key Concepts Demonstrated:**
```csharp
private void God(CCSPlayerController? caller, CCSPlayerController player)
{
// Perform the action
if (!GodPlayers.Add(player.Slot))
{
GodPlayers.Remove(player.Slot);
}
// ✅ BEST PRACTICE: Use per-player language support
var activityArgs = new object[] { "CALLER", player.PlayerName };
if (caller == null || !_sharedApi!.IsAdminSilent(caller))
{
if (Localizer != null)
{
// Each player sees message in their configured language!
_sharedApi!.ShowAdminActivityLocalized(Localizer, "fun_admin_god_message", callerName, false, activityArgs);
}
}
// ✅ BEST PRACTICE: Log the action
_sharedApi!.LogCommand(caller, $"css_god {player.PlayerName}");
}
```
### 5. Menus (`Menus.cs`)
**Key Concepts Demonstrated:**
#### Simple Player Selection Menu
```csharp
private object CreateGodModeMenu(CCSPlayerController admin)
{
// ✅ BEST PRACTICE: Use CreateMenuWithPlayers for simple player selection
return _sharedApi!.CreateMenuWithPlayers("God Mode", "fun", admin,
player => player.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(player),
God); // Direct method reference
}
```
#### Nested Menu with Value Selection
```csharp
private object CreateSetHpMenu(CCSPlayerController admin)
{
// ✅ BEST PRACTICE: Use CreateMenuWithBack for menus with back button
var menu = _sharedApi!.CreateMenuWithBack("Set HP", "fun", admin);
var players = _sharedApi.GetValidPlayers().Where(p =>
p.PlayerPawn?.Value?.LifeState == (int)LifeState_t.LIFE_ALIVE && admin.CanTarget(p));
foreach (var player in players)
{
// ✅ BEST PRACTICE: AddSubMenu automatically adds back button to submenu
_sharedApi.AddSubMenu(menu, playerName, p => CreateHpSelectionMenu(admin, player));
}
return menu;
}
private object CreateHpSelectionMenu(CCSPlayerController admin, CCSPlayerController target)
{
var hpMenu = _sharedApi!.CreateMenuWithBack($"Set HP: {target.PlayerName}", "fun", admin);
var hpValues = new[] { 1, 10, 25, 50, 100, 200, 500, 999 };
foreach (var hp in hpValues)
{
// ✅ BEST PRACTICE: AddMenuOption for simple actions
_sharedApi.AddMenuOption(hpMenu, $"{hp} HP", _ =>
{
// ✅ BEST PRACTICE: Always validate before executing
if (target.IsValid)
{
target.SetHp(hp);
LogAndShowActivity(admin, target, "fun_admin_hp_message", "css_hp", hp.ToString());
}
});
}
return hpMenu;
}
```
### 6. Translations
**Key Concept:** Module-specific translations
```json
// lang/en.json
{
"fun_admin_god_message": "{lightred}{0}{default} changed god mode for {lightred}{1}{default}!",
"fun_admin_hp_message": "{lightred}{0}{default} changed {lightred}{1}{default} hp amount!"
}
```
**Why module translations?**
- Your module is independent from SimpleAdmin
- You can update translations without affecting main plugin
- Each player sees messages in their language automatically
## 🛠️ How to Use This as a Template
### Step 1: Copy the Module
```bash
cp -r CS2-SimpleAdmin_FunCommands YourModuleName
```
### Step 2: Rename Files
- Rename `.csproj` file
- Rename all `.cs` files to match your module name
- Update namespace in all files
### Step 3: Update References
- Change `namespace CS2_SimpleAdmin_FunCommands` to `namespace YourModuleName`
- Update plugin metadata (name, version, author, description)
### Step 4: Modify Config
```csharp
public class Config : IBasePluginConfig
{
public int Version { get; set; } = 1;
// Add your own command lists
public List<string> YourCommands { get; set; } = ["css_yourcommand"];
}
```
### Step 5: Add Your Commands
Look at `Commands.cs` for examples of command handlers
### Step 6: Add Your Menus
Look at `Menus.cs` for examples of menu creation
### Step 7: Add Translations
Create language files in `lang/{language}.json` (e.g., `lang/en.json`, `lang/pl.json`)
## 📖 Learning Path
If you're new to module development, study files in this order:
1. **Config.cs** - Understand configuration structure
2. **CS2-SimpleAdmin_FunCommands.cs** - See initialization and API acquisition
3. **Commands.cs** - Learn command registration and handling
4. **Actions.cs** - Understand action methods and translations
5. **Menus.cs** - Study menu creation patterns
## 🎓 Best Practices Demonstrated
### ✅ Command Registration
```csharp
// Dynamic registration based on config
if (Config.GodCommands.Count > 0)
{
foreach (var command in Config.GodCommands)
{
_sharedApi.RegisterCommand(command, "Enable god mode", OnGodCommand);
}
}
```
### ✅ Target Validation
```csharp
// Always check if player can be targeted
if (!caller.CanTarget(player)) return;
// Always validate player state
if (!player.IsValid) return;
```
### ✅ Translation Usage
```csharp
// Use module's localizer for per-player language support
if (Localizer != null)
{
_sharedApi.ShowAdminActivityLocalized(Localizer, "translation_key", callerName, false, args);
}
```
### ✅ Cleanup on Unload
```csharp
public override void Unload(bool hotReload)
{
// Unregister all commands
if (Config.GodCommands.Count > 0)
{
foreach (var command in Config.GodCommands)
{
_sharedApi.UnRegisterCommand(command);
}
}
// Unregister all menus
if (Config.GodCommands.Count > 0)
_sharedApi.UnregisterMenu("fun", "god");
// Remove event handlers
_sharedApi.OnSimpleAdminReady -= RegisterFunMenus;
}
```
### ✅ Data Caching
```csharp
// Cache expensive operations
private static Dictionary<int, CsItem> GetWeaponsCache()
{
if (_weaponsCache != null) return _weaponsCache;
var weaponsArray = Enum.GetValues(typeof(CsItem));
_weaponsCache = new Dictionary<int, CsItem>();
// ... populate cache
return _weaponsCache;
}
```
## 🔗 Related Documentation
- **[MODULE_DEVELOPMENT.md](../MODULE_DEVELOPMENT.md)** - Complete API reference
- **[TRANSLATION_EXAMPLE.md](../TRANSLATION_EXAMPLE.md)** - Translation usage guide
- **[CS2-SimpleAdminApi](../../CS2-SimpleAdminApi/)** - API interface definitions
## 💡 Tips
1. **Start Simple** - Begin with one command and one menu, then expand
2. **Test Thoroughly** - Test with multiple players, different permissions, and languages
3. **Handle Errors** - Always validate player state before actions
4. **Log Everything** - Use `_sharedApi.LogCommand()` for all admin actions
5. **Use Partial Classes** - Split your code into logical files
6. **Comment Your Code** - Especially if others will use it as reference
## 🐛 Common Mistakes to Avoid
-**Don't** forget to check `IsValid` before accessing player properties
-**Don't** skip permission checks (`CanTarget`, `RequiresPermissions`)
-**Don't** forget to unregister commands/menus in `Unload()`
-**Don't** use SimpleAdmin's translations - create your own
-**Don't** hardcode command names - use config lists instead
## 🤝 Contributing
If you improve this module or find better patterns, please contribute back so others can learn!
## 📄 License
This module is provided as-is for educational purposes. Feel free to use it as a template for your own modules.

View File

@@ -0,0 +1,7 @@
{
"sdk": {
"version": "8.0.0",
"rollForward": "latestMinor",
"allowPrerelease": false
}
}