/*
* =================================================================================================
* Using Direktívák (Using Directives)
* =================================================================================================
* Ezekkel a sorokkal "importáljuk" a CounterStrikeSharp API különböző részeit.
* Mindegyik egy-egy funkciócsoportért felel, pl. parancsokért, menükért, eseményekért stb.
* Így tud a kódunk hivatkozni a beépített funkciókra, mint pl. a PrintToChat vagy a AddCommand.
*/
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Menus;
using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.Utils;
// A névtér (namespace) segít a kódunkat logikailag elkülöníteni más pluginektől.
// Jó gyakorlat, ha minden plugin egyedi névtérben van.
namespace ApiBemutatoPlugin;
// A fő plugin osztályunk, ami a BasePlugin-ból öröklődik. Minden pluginnek így kell kezdődnie.
public class ApiBemutatoPlugin : BasePlugin
{
/*
* =================================================================================================
* Plugin Metaadatok (Plugin Metadata)
* =================================================================================================
* Ezek az adatok jelennek meg a szerver konzoljában, amikor a plugin betöltődik.
* Segítenek azonosítani a plugint és annak verzióját.
*/
public override string ModuleName => "CSS API Bemutató Plugin";
public override string ModuleAuthor => "*tOrNaDo";
public override string ModuleVersion => "1.0.0";
/*
* =================================================================================================
* Állapotkezelés (State Management)
* =================================================================================================
* Itt egy ún. "Dictionary"-t (szótárat) hozunk létre.
* Ebben fogjuk tárolni, hogy egy adott játékos (akit az UserID-ja azonosít)
* hány ölést szerzett az aktuális körben.
* Ez egy alapvető példa az állapotkezelésre, amikor adatokat kell tárolnunk a játékosokról.
*/
private readonly Dictionary<int, int> _playerRoundKills = new();
/*
* =================================================================================================
* Eseménykezelők (Event Handlers)
* =================================================================================================
* A [GameEventHandler] attribútummal "feliratkozunk" a játék eseményeire.
* Amikor a játékban történik valami (pl. egy játékos meghal), a CounterStrikeSharp
* meghívja az eseményhez tartozó függvényünket.
*/
/// <summary>
/// Ez a függvény minden kör elején lefut (a freeze time kezdetekor).
/// </summary>
[GameEventHandler]
public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
{
// Kiürítjük a szótárunkat, hogy az új kör tiszta lappal induljon mindenki számára.
// Ha ezt nem tennénk, az ölések száma körökön át összeadódna.
_playerRoundKills.Clear();
// Kiírunk egy üzenetet a szerver chatjébe mindenki számára.
// A ChatColors segítségével színesíthetjük a szöveget.
Server.PrintToChatAll(\$" {ChatColors.Green}Új kör kezdődött! Az ölési statisztikák nullázva.");
// A HookResult.Continue jelzi a CounterStrikeSharp-nak, hogy a többi plugin is feldolgozhatja ezt az eseményt.
return HookResult.Continue;
}
/// <summary>
/// Akkor fut le, amikor egy játékos sikeresen csatlakozott a szerverre.
/// </summary>
[GameEventHandler]
public HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
{
// Lekérjük a csatlakozott játékos objektumát az eseményből.
// A try-catch blokk biztonsági okokból van itt, ha esetleg a játékos érvénytelen lenne.
try
{
var player = @event.Userid;
// Ellenőrizzük, hogy a játékos valid-e és nem egy bot.
if (player == null || !player.IsValid || player.IsBot)
{
return HookResult.Continue;
}
// Létrehozunk egy időzítőt (Timer), ami 3 másodperc múlva futtatja le a benne lévő kódot.
// Erre azért van szükség, mert a csatlakozáskor sok üzenet jelenik meg,
// és így a miénk nem veszik el a többi között.
AddTimer(3.0f, () =>
{
// Mielőtt üzenetet küldenénk, újra ellenőrizzük, hogy a játékos még mindig valid-e.
// Lehet, hogy 3 mp alatt kilépett.
if (player.IsValid)
{
// Privát üzenetet küldünk a csatlakozott játékosnak.
player.PrintToChat(\$" {ChatColors.Gold}Üdv a szerveren, {ChatColors.Lime}{player.PlayerName}{ChatColors.Gold}!");
player.PrintToChat(\$" {ChatColors.LightBlue}Ez a szerver egy API bemutató plugint futtat.");
player.PrintToChat(\$" {ChatColors.LightBlue}Írd be a chatbe: {ChatColors.Olive}/bemutato");
}
}, TimerFlags.STOP_ON_MAPCHANGE);
}
catch (Exception e)
{
// Hiba esetén kiírjuk a konzolra a hibaüzenetet.
Console.WriteLine(\$"[ApiBemutato] Hiba OnPlayerConnectFull eseménynél: {e.Message}");
}
return HookResult.Continue;
}
/// <summary>
/// Ez a legkomplexebb eseménykezelőnk. Akkor fut le, amikor egy játékos meghal.
/// </summary>
[GameEventHandler]
public HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info)
{
// Lekérjük az áldozatot (victim) és a támadót (attacker) az esemény adataiból.
var victim = @event.Userid;
var attacker = @event.Attacker;
// Ellenőrizzük, hogy a támadó és az áldozat is érvényes játékos-e.
if (attacker == null || !attacker.IsValid || victim == null || !victim.IsValid)
{
return HookResult.Continue;
}
// Ha a támadó és az áldozat ugyanaz a személy (öngyilkosság), nem csinálunk semmit.
if (attacker == victim)
{
return HookResult.Continue;
}
// Ha a támadó egy bot, szintén nem foglalkozunk vele.
if (attacker.IsBot)
{
return HookResult.Continue;
}
// Növeljük a támadó ölési számlálóját.
// Először megnézzük, szerepel-e már a szótárban. Ha nem, hozzáadjuk 0 értékkel.
if (!_playerRoundKills.ContainsKey(attacker.UserId ?? 0))
{
_playerRoundKills.Add(attacker.UserId ?? 0, 0);
}
// Növeljük az értékét eggyel.
_playerRoundKills[attacker.UserId ?? 0]++;
// Kiolvassuk az aktuális ölési számot.
var kills = _playerRoundKills[attacker.UserId ?? 0];
// Kiírunk egy informatív üzenetet a chatbe mindenki számára.
Server.PrintToChatAll(
\$" {ChatColors.Lime}{attacker.PlayerName} {ChatColors.White}megölte {ChatColors.Red}{victim.PlayerName} {ChatColors.White}játékost" +
\$" egy {ChatColors.Gold}{@event.Weapon}{ChatColors.White} fegyverrel." +
\$" ({ChatColors.Lime}{kills}{ChatColors.White}. ölés a körben)"
);
return HookResult.Continue;
}
/*
* =================================================================================================
* Parancsok és Menük (Commands and Menus)
* =================================================================================================
* A [ConsoleCommand] attribútummal regisztrálunk egy parancsot, amit a játékosok a chatbe írhatnak.
* A css_ előtag automatikusan hozzáadódik, a chat parancsok általában / vagy ! jellel kezdődnek.
*/
/// <summary>
/// A /bemutato parancsot lekezelő függvény.
/// </summary>
[ConsoleCommand("css_bemutato", "Megnyitja az API bemutató menüt.")]
[CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] // Biztosítja, hogy csak játékosok használhassák, a szerverkonzol ne.
public void OnShowMenuCommand(CCSPlayerController? player, CommandInfo command)
{
// Ellenőrizzük, hogy a parancsot kiadó játékos érvényes-e.
if (player == null || !player.IsValid)
{
return;
}
// Létrehozunk egy új menüt, aminek címet adunk.
var menu = new ChatMenu(\$"{ChatColors.Gold}API Bemutató Menü");
// Hozzáadunk egy menüpontot. A második paraméter egy ún. lambda függvény,
// ami akkor fut le, ha a játékos ezt a pontot választja.
menu.AddMenuOption("Élet és Páncél Lekérdezése", (p, option) =>
{
// Kiírjuk a játékosnak az aktuális életpontját és páncélját.
p.PrintToChat(\$" {ChatColors.Green}Életerő: {p.Pawn.Value.Health}");
p.PrintToChat(\$" {ChatColors.Green}Páncél: {p.Pawn.Value.ArmorValue}");
});
// Újabb menüpont, ami egy HE gránátot ad a játékosnak.
menu.AddMenuOption("Adj egy HE gránátot!", (p, option) =>
{
// A GiveNamedItem függvénnyel adhatunk tárgyakat a játékosnak.
p.GiveNamedItem("weapon_hegrenade");
p.PrintToChat(\$" {ChatColors.Green}Megkaptad a HE gránátot!");
});
// Menüpont a körben szerzett ölések lekérdezésére.
menu.AddMenuOption("Kör statisztikám", (p, option) =>
{
// Kiolvassuk az adatot a szótárunkból. Ha a játékos még nem ölt, 0 lesz az érték.
_playerRoundKills.TryGetValue(p.UserId ?? 0, out int kills);
p.PrintToChat(\$" {ChatColors.LightBlue}Ebben a körben eddig {ChatColors.Lime}{kills}{ChatColors.LightBlue} ölésed van.");
});
// Menüpont szerverinformáció lekérdezésére.
menu.AddMenuOption("Szerver neve", (p, option) =>
{
// A ConVar.Find segítségével kiolvashatjuk a szerver változóit (ConVar).
// A 'sv_hostname' a szerver nevét tárolja.
var hostname = ConVar.Find("sv_hostname")?.StringValue ?? "Ismeretlen";
p.PrintToChat(\$" {ChatColors.Grey}A szerver neve: {ChatColors.Olive}{hostname}");
});
// Végül megnyitjuk a létrehozott menüt a parancsot használó játékosnak.
MenuManager.OpenChatMenu(player, menu);
}
}