729 zákazníckych serverů
80 TV kanálů
76003 registrovaných užívatelů
podporujeme youtubery a streamery

Skladba vlastního pluginu #1 – CS:GO VIP Plugin

Jistě každý někdy přemýšlel nad tím jak udělat svůj server originální a chtěl si složit svůj vlastní plugin. V tomto článku se vám pokusíme vysvětlit některé základní informace jak na to.
Nížší skladba pluginu je pouze výuční a nedoporučujeme ji používat na serverech.
Dejme tomu zadáme si cíle, které chceme aby měli VIP hráči. V našem případě tedy:

1. Větší HP na Spawnu
2. +5 HP za každý kill
3. VIP TAG v tabulce
4. Možnost Resetovat si Skore
5. VIP bude mít 100 Armoru na Spawnu a normální hráč 0
6. VIP bude mít Nižší Gravity
7. VIP bude mít o 0.2 větší damage

To by Zatím v Základu stačilo. Sami sobě jsme si dali cíle a teď už je jen stačí naprogramovat. Musíme však brát v úvahu, že když už programujeme vlastní pluginy tak si udržet nějakou tu originalito a né jen něco stáhnout z internetu a nebo kopírovat jiné servery. Jelikož na vás půjdeme ze začátku z lehka, stačí nám tedy obyčejný textový dokument, do kterého budeme psát kódy a pak webový compiler na adrese: http://www.sourcemod.net/compiler.php ... Celé programování spočívá v tom že my si nejdříve musíme napsat vlastní scripty a ty poté pomocí webového compileru převést na plugin. Plugin je totiž finální verze všeho našeho úsilí o změnu na serveru. Taky je důležité udržet si vlastní scripty přehlepdé a tak se v článku budu později vracet s vysvětlením co jednotlivé části znamenají.

Tak abych začal popisovat jak nejjednodušeji budeme postupovat při tvorbě našeho vlsatního prvního pluginu. Nejdůležitější částí pluginu jsou tzv. "include", to jsou soubory které mají v sobě uložené všechny částice, které různé příkazy potřebují ke správné funkčnosti. My tedy začneme s těmi základními. Do vytvořeného souboru začneme psát od začátku.
#include 
#include
#include
Další důležité jsou #define řádky, v našem případě si jimi ušetříme spoustu přepisování v pluginu při pozdějších úpravách a slouží něco jako odkazy. Například nastavíme si VIP na Rezervační Slot FLAG, pomůže nám to v tom, že když se později rozhodneme změnit FLAG, na kterým to bude nastavené > stačí nám změnit jeden řádek a né v několika desítkách pokud jsou naše výhody rozsáhlejší. Taky si nastavípe Prefix, což v našem případě bude něco jako odkaz na naše stránky, které se budou ukazovat jako začátek každé serverové zprávy v chatu. Písmenka vypadají takto: \x1-10 jsou barvy kterými se zprávy v chatu ukazují. Pak také hlavní argumenty které budeme v celém scriptu používat. A jedna z důležitýh věcí pluginu "Plugin:myinfo" , to slouží jako interní označení pluginu na serveru.
#define VIP ADMFLAG_RESERVATION
#define Prefix "\x4 \x6[GameHosting.cz] \x7"

new freevip[MAXPLAYERS+1] = 0;

public Plugin:myinfo =
{
name = "VIP Plugin",
author = "Miluska xD",
description = "VIP Plugin",
version = "1.0",
url = "www.GameHosting.cz"
}

Tak jsme si zapsali základní a potřebné Includy a Definice scripty můžeme pokročit k jednotlivým prvkům VIP Výhod. Nastavíme si tedy už na začátku, v rámci přehlednosti scriptu, nějaké ty výhody ať to máme vše pohromadě.
public SetClientSpeed(client)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", 1.2);
}
else
{
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", 1.0);
}
}
public PerformGravity(client)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
SetEntityGravity(client, 0.7);
}
else
{
SetEntityGravity(client, 1.0);
}
}
public HandleTag(client)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
CS_SetClientClanTag(client, "[VIP]");
}
}
public Action:resetscore(client, args)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
EditScore(client);
}
}
void EditScore(int client)
{
SetEntProp(client, Prop_Data, "m_iFrags", 0);
SetEntProp(client, Prop_Data, "m_iDeaths", 0);
}

V prví řadě jste si všimli slov PUBLIC a nějaký název. To slouží jako název vyvolávací části začínající a končící závorkami. Závorky jsou důležité, protože jedna chybějící nám může pěkně zavařit. Řekněme že dostatečná angličtina nám pomůže si vysvětlit, co který název znamená. Samozřejmě musíme znát i cvary které používáme a chystáme se je měnit. Například ""m_flLaggedMovementValue", jak jste pochopili slouží k změně rychlosti. Dále "SetEntityGravity" ke změně gravitace a "CS_SetClientClanTag" jak nám již napovídá ke změně TAGu v tabulce. A nyní dál do příkazů serveru do chatu a hlavní funkce scriptu. K Resetování Score se dostaneme hned níže.
public OnPluginStart()
{
RegConsoleCmd("sm_rs", resetscore);

HookEvent("round_start", round_start);
HookEvent("player_spawn", hrac_spawn);
HookEvent("player_death", hrac_zemrel);
}

Řádky s označením: RegConsoleCmd slouží jako registrace příkazu pro vyvolání nějakého odkazu. V tomto případě vidíte "sm_rs" a "sm_shop". Tyto odkazy můžeme buď použít v console přesně tak jak je vidíme nebo pomocí chatu ovšem začátek "sm_" nahradíme lomítkem nebo vykřičníkem např.: !rs. Po napsání tohoto slova do chatu si náš VIP hráč vynuluje skóre jak cidíme výše u té rychlosti, gravity a tagu. Vidíte tam "public Action:resetscore(client, args)" přičemž Action (akce) s psaným argumentem (args) vyvolá danému VIP hráči (client) další odkaz, kterým je: "EditScore(client)" a ten o řádek níže vynuluje smrti a fragy. Místo registračních argumentů vidíte také základní systémy serverů: Spawn, Death a start kola. Začneme tedy u Spawnu a smrti protivníka.
public Action hrac_spawn(Handle event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(GetEventInt(event, "userid"));

freevip[client] = 1;

PerformGravity(client);
SetClientSpeed(client);
HandleTag(client);

if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
new health = 125;
SetEntityHealth(client,health);
SetEntProp( client, Prop_Send, "m_ArmorValue", 100, 1 );
}
else
{
new health = 100;
SetEntityHealth(client,health);
SetEntProp( client, Prop_Send, "m_ArmorValue", 0, 1 );
}

}
public Action:hrac_zemrel(Handle:event, const String:name[], bool:dontBroadcast)
{
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));

if (attacker != victim)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
new health[MAXPLAYERS+1];

health[attacker] = GetEntProp(attacker, Prop_Send, "m_iHealth");
SetEntityHealth(attacker, health[attacker] + 5);

}
}
}

Ve Spawnu už konečně vidíte na začátku odkazy ke spuštění speedu, gravity a tagu. Tyto odkazy se aktualizují při každém respawnutí hráče, aby nám nedocházelo k bugům. Dále rovnou si nastavíme naší další výhodu VIP hráčům, a tím je HP (život) a Armor (brnění) na začátku kola a NEVIP hráčům pro jistotu nastavíme také vlastní hodnoty. Také jste si už po několikáté všimli řádku "freegold[client]" na který už konečně přichází řada. Tímto si právě pro nás ve Spawnu hráče nastavíme pro různé víkendové eventy dle našeho přání zda povolíme naším hráčům VIP zdarma. A když se zakoukáme do řádku "if (GetUserFlagBits(client) & VIP || freevip[client] == 1) " tak vidíme: if = začínající podmínka se závorkou s nějakým argumentem, opak je ELSE a dále & a || to znamená: & = Rovná se neboli související... || = NEBO (v našem případě buď VIP Flag NEBO freevip) ... a později se střetnete i s && = A, zároveň. VIP hráčům tedy nastavíme health ("new health" každé nové číselné či slovní gesto musíme zapsat jako new) na 125 a obyčejným hráčům na 100. Dále cvar pro Armor používáme "m_ArmorValue" a nastavíme pro VIP 100 a obyčejným hráčům 0.

Další akce je: "Action:hrac_zemrel" co se tedy vlastně stane když hráč Zemře? Chtěli jsme výhodu pro VIP +5 HP za kill použijeme tedy stejný příkaz "SetEntityHealth" a útočníkovi (attacker) zapíšeme argument + 5 (+5 HP) při každém zabití. Určitě ste si také všimli že nyní je u "new health" použité: MAXPLAYERS+1 ... To proto že S "MAXPLAYERS+1" (new health[MAXPLAYERS+1]) je pouze pro jednoho clienta zatímco BEZ "MAXPLAYERS+1" (new health) určujeme celoserverově pro všechny.

A nakonec v poslední řadě základní registrací máme start kola. Do něj nepoužijeme momentálně nic než náš první ServerCommand. Ten se odesílá přímo do konzole serveru a my použijeme zrovna Reload Admins, to znamená že každé kolo se načtou nově přidané VIP a admini pokud je během kola (mapy) přidáme, nemusí tak čekat do konce mapy.

public Action round_start(Handle event, const char[] name, bool dontBroadcast)
{
ServerCommand("sm_reloadadmins");
}

A jedny z posledních výhod máme při napojení na server napsat zprávu do chatu, že se napojuje VIP hráč + aktivace většího damage.

public OnClientPutInServer(client)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
PrintToChatAll("%s VIP Hrac %N Se Pripojuje na Server",Prefix, client);
}
SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
}

public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype)
{
if (GetUserFlagBits(attacker) & VIP || freevip[attacker] == 1)
{
damage *= 1.2;
return Plugin_Changed;
}
return Plugin_Continue;
}

Tak a pro vysvětlení "OnClientPutInServer" je automatická základní předvolba inslude SOURCEMOD což znamená že jí dále nemusíme nikam zapisovat a znamená to tedy odkaz vyvolaný připojením hráče na server. Jako první tedy server zjistí zdali je připojující hráč VIP, pokud ano odešle všem na serveru do chatu Zpravu. Vysvětlivka argumentů: %s je odkaz na náš Prefix a tedy místo toho se ukáže náš DEFINE [GameHosting.cz] VIP Hrac %N je odkaz na jméno připojujícího se clienta a zbytek zprávy v uvozovkách. Za¨uvozovkami vidíme právě ty odkazy a POZOR ZÁLEŽÍ na jejich pořadí!!! Záměna odkazů nemusí třeba vůbec fungovat protože každé %s %i %N má svůj účel. %s = slovo , %i = číslo, %N = jméno.

Další a poslední naše chtěná výhoda je tedy odkazující na Action zvětšení damage. I tady tedy server zhodnotí zda je útočník VIP a pomocí našeho vzorce zvětšuje damage protivníka.

Náš finální plugin by tedy mohl vypadat po kupě nějak takto:

#include 
#include
#include

#define VIP ADMFLAG_RESERVATION
#define Prefix "\x4 \x6[GameHosting.eu] \x7";

new freevip[MAXPLAYERS+1] = 0;

public SetClientSpeed(client)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", 1.2);
}
else
{
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", 1.0);
}
}
PerformGravity(client)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
SetEntityGravity(client, 0.7);
}
else
{
SetEntityGravity(client, 1.0);
}
}
HandleTag(client)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
CS_SetClientClanTag(client, "[VIP]");
}
}

public Plugin:myinfo =
{
name = "VIP Plugin",
author = "Miluska xD",
description = "VIP Plugin",
version = "1.0",
url = "www.GameHosting.cz"
}

public OnPluginStart()
{
RegConsoleCmd("sm_rs", resetscore);
HookEvent("round_start", round_start);
HookEvent("player_spawn", hrac_spawn);
HookEvent("player_death", hrac_zemrel);
}

public Action:resetscore(client, args)
{
if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
EditScore(client);
}
}

void EditScore(int client)
{
SetEntProp(client, Prop_Data, "m_iFrags", 0);
SetEntProp(client, Prop_Data, "m_iDeaths", 0);
}

public Action hrac_spawn(Handle event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(GetEventInt(event, "userid"));

freevip[client] = 1;

PerformGravity(client);
SetClientSpeed(client);
HandleTag(client);

if (GetUserFlagBits(client) & VIP || freevip[client] == 1)
{
new health = 125;
SetEntityHealth(client,health);
SetEntProp( client, Prop_Send, "m_ArmorValue", 100, 1 );
}
else
{
new health = 100;
SetEntityHealth(client,health);
SetEntProp( client, Prop_Send, "m_ArmorValue", 0, 1 );
}

}
public Action:hrac_zemrel(Handle:event, const String:name[], bool:dontBroadcast)
{
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));

if (attacker != victim)
{
if (GetUserFlagBits(attacker) & VIP || freevip[attacker] == 1)
{
new health[MAXPLAYERS+1];

health[attacker] = GetEntProp(attacker, Prop_Send, "m_iHealth");
SetEntityHealth(attacker, health[attacker] + 5);
}
}
}

public Action round_start(Handle event, const char[] name, bool dontBroadcast)
{
ServerCommand("sm_reloadadmins");
}

public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype)
{
if (GetUserFlagBits(attacker) & VIP || freevip[attacker] == 1)
{
damage *= 1.2;
return Plugin_Changed;
}
return Plugin_Continue;
}

public OnClientPutInServer(client)
{
SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
}

Tak jsme si ukázali velice jednoduchý příklad jak by mohlo vypadat pár VIP výhod, samozřejmě jich jde vymyslet i naprogramovat několik desítek a to skiny, oznámení na server, reklamy, bez reklam atd atd atd ale to kdybychom to tu měli vše udělat tak máme plugin o několika tisících řádkách. My však pouze v návodu uvádíme příklad jak by toto mohlo vymapat a pomoci něco přiučit. A v příštím článku se už můžeme podívat na trochu náročnější plugin nebo jak si vlastně zřídit svůj vlastní herní portál nebo pomoci s instalací programu přímo do vašeho PC abychom nemuseli stále používat náročně Web Compiler.

10.03.2016

Komentáře