> > > >

ReturnAmmo 1.0.0

Нет прав для скачивания
Сообщения
50
Рейтинг
63
#1
Drumanid добавил(а) новый ресурс:

ReturnAmmo - Возвращает потраченные патроны игроку после убийства или же перезарядки.

Возвращает(восстанавливает) потраченные патроны игроку после убийства или же перезарядки.
Демонстрация:


C++:
#include <sdkhooks>

public Plugin myinfo =
{
    name = "Return Ammo",
    author = "Drumanid",
    version = "1.0.0",
    url = "http://vk.com/drumanid"
};

StringMap g_hTrie;
int g_iReturnType;

public void OnPluginStart()
{
    g_hTrie = new StringMap();

    g_hTrie.SetValue("7", 30)...
Узнать больше об этом ресурсе...
 
3  
Сообщения
253
Рейтинг
290
#2
Лучше сделать массив на все пушки, избавившись от использования Trie, ещё и строковым.
 
1  
Сообщения
50
Рейтинг
63
#3
Лучше сделать массив на все пушки, избавившись от использования Trie, ещё и строковым.
Ну такое себе...

C++:
#include <sdkhooks>

public Plugin myinfo =
{
    name = "Return Ammo",
    author = "Drumanid",
    version = "1.0.0",
    url = "http://vk.com/drumanid"
};

static const int g_iWeapons[][] =
{
    {7, 30}, {16, 30}, {9, 10}, {60, 20}, {10, 25}, {13, 35}, {39, 30}, {8, 30}, {40, 10}, {17, 30}, {34, 30},
    {33, 30}, {24, 25}, {19, 50}, {26, 64}, {35, 8}, {25, 7}, {27, 5}, {29, 7}, {28, 150}, {14, 100}, {4, 20},
    {61, 12}, {32, 13}, {36, 13}, {30, 18}, {1, 7}, {2, 30}, {3, 20}, {63, 12}, {64, 8}, {23, 30}
};

/*static const int g_iWeapons[] =
{
    7, 16, 9, 60, 10, 13, 39, 8, 40, 17, 34,
    33, 24, 19, 26, 35, 25, 27, 29, 28, 14,
    4, 61, 32, 36, 30, 1, 2, 3, 63, 64, 23
};

static const int g_iAmmo[] =
{
    30, 30, 10, 20, 25, 35, 30, 30, 10, 30, 30,
    30, 25, 50, 64, 8, 7, 5, 7, 150, 100, 20,
    12, 13, 13, 18, 7, 30, 20, 12, 8, 30
};*/

int    g_iReturnType;

public void OnPluginStart()
{
    HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);

    ConVar hCvar = CreateConVar("sm_return_ammo", "2", "0 - возвращать обойму / 1 - запас / 2 - запас и обойму");
    hCvar.AddChangeHook(HookCvarTime); g_iReturnType = hCvar.IntValue;
    AutoExecConfig(true, "ReturnAmmo");
}

public void HookCvarTime(ConVar hCvar, const char[] sOldValue, const char[] sNewValue)
{
    g_iReturnType = hCvar.IntValue;
}

public void OnEntityCreated(int iEntity, const char[] sClassname)
{
    if(g_iReturnType != 0 && StrContains(sClassname, "weapon_", false) != -1) SDKHook(iEntity, SDKHook_ReloadPost, ReloadPost);
}

public void ReloadPost(int iWeapon, bool bSuccess)
{
    if(bSuccess) SetAmmo(iWeapon, false);
}

public void Event_PlayerDeath(Event hEvent, const char[] sName, bool bDbc)
{
    if(g_iReturnType != 1)
    {
        int iUserId = hEvent.GetInt("attacker");
        if(iUserId != hEvent.GetInt("userid"))
        {
            int iWeapon = GetEntPropEnt(GetClientOfUserId(iUserId), Prop_Data, "m_hActiveWeapon");
            if(iWeapon > 0) SetAmmo(iWeapon, true);
        }
    }
}

void SetAmmo(int iWeapon, bool bFire)
{
    int iItem = GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex");
    for(int i; i < /*sizeof(g_iWeapons)*/31; i++)
    {
        if(g_iWeapons[i][0] == iItem)
        {
            SetEntProp(iWeapon, Prop_Send, "m_iClip1", bFire ? g_iWeapons[i][1] +1:g_iWeapons[i][1]);
            break;
        }
    }
}
C++:
#include <sdkhooks>

public Plugin myinfo =
{
    name = "Return Ammo",
    author = "Drumanid",
    version = "1.0.0",
    url = "http://vk.com/drumanid"
};

static const int g_iAmmo[] =
{
    0, 7, 30, 20, 20, 0, 0, 30, 30, 10, 25,0, 0, 35, 100,
    0, 30, 30, 0, 50, 0, 0, 0, 30, 25, 7, 64, 5, 150, 7,
    18, 0, 13, 30, 30, 8, 13, 0, 0, 30, 10, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 12, 12, 8
};

int    g_iReturnType;

public void OnPluginStart()
{
    HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);

    ConVar hCvar = CreateConVar("sm_return_ammo", "2", "0 - возвращать обойму / 1 - запас / 2 - запас и обойму");
    hCvar.AddChangeHook(HookCvarTime); g_iReturnType = hCvar.IntValue;
    AutoExecConfig(true, "ReturnAmmo");
}

public void HookCvarTime(ConVar hCvar, const char[] sOldValue, const char[] sNewValue)
{
    g_iReturnType = hCvar.IntValue;
}

public void OnEntityCreated(int iEntity, const char[] sClassname)
{
    if(g_iReturnType != 0 && StrContains(sClassname, "weapon_", false) != -1) SDKHook(iEntity, SDKHook_ReloadPost, ReloadPost);
}

public void ReloadPost(int iWeapon, bool bSuccess)
{
    if(bSuccess) SetEntProp(iWeapon, Prop_Send, "m_iClip1", g_iAmmo[GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex")]);
}

public void Event_PlayerDeath(Event hEvent, const char[] sName, bool bDbc)
{
    if(g_iReturnType != 1)
    {
        int iUserId = hEvent.GetInt("attacker");
        if(iUserId != hEvent.GetInt("userid"))
        {
            int iWeapon = GetEntPropEnt(GetClientOfUserId(iUserId), Prop_Data, "m_hActiveWeapon");
            if(iWeapon > 0) SetEntProp(iWeapon, Prop_Send, "m_iClip1", g_iAmmo[GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex")] +1);
        }
    }
}

/*void SetAmmo(int iWeapon, bool bFire)
{
    SetEntProp(iWeapon, Prop_Send, "m_iClip1", g_iAmmo[GetEntProp(iWeapon, Prop_Send, "m_iItemDefinitionIndex")] + (bFire ? 1:0));
}*/
 
Последнее редактирование:
 
Сообщения
253
Рейтинг
290
#4
Drumanid, массив можно было и не инициализировать, а чтоб он сам заполнился после выдачи оружия.
 
 
Сообщения
50
Рейтинг
63
#5
fl0wer, лишние проверки и хук. Я понимаю если бы значения менялись, в этом был бы смысл.
Короче, кто будет использовать пусть выбирает из трех вариантов.

C++:
#include <sdkhooks>

#define GWI(%0) GetEntProp(%0, Prop_Send, "m_iItemDefinitionIndex")

public Plugin myinfo =
{
    name = "Return Ammo",
    author = "Drumanid",
    version = "1.0.1",
    url = "http://vk.com/drumanid"
};

int g_iAmmo[64], g_iReturnType;

public void OnPluginStart()
{
    HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);

    ConVar hCvar = CreateConVar("sm_return_ammo", "2", "0 - возвращать обойму / 1 - запас / 2 - запас и обойму");
    hCvar.AddChangeHook(HookCvarTime); g_iReturnType = hCvar.IntValue;
    AutoExecConfig(true, "ReturnAmmo");
}

public void HookCvarTime(ConVar hCvar, const char[] sOldValue, const char[] sNewValue) { g_iReturnType = hCvar.IntValue; }
public void OnClientPostAdminCheck(int iClient) { SDKHook(iClient, SDKHook_WeaponEquipPost, WeaponEquipPost); }

public void WeaponEquipPost(int iClient, int iWeapon)
{
    int iNumber = GWI(iWeapon);
    if(g_iAmmo[iNumber] == 0) g_iAmmo[iNumber] = GetEntProp(iWeapon, Prop_Send, "m_iClip1");
}

public void OnEntityCreated(int iEntity, const char[] sClassname)
{
    if(g_iReturnType != 0 && StrContains(sClassname, "weapon_", false) != -1) SDKHook(iEntity, SDKHook_ReloadPost, ReloadPost);
}

public void ReloadPost(int iWeapon, bool bSuccess)
{
    if(bSuccess) SetEntProp(iWeapon, Prop_Send, "m_iClip1", g_iAmmo[GWI(iWeapon)]);
}

public void Event_PlayerDeath(Event hEvent, const char[] sName, bool bDbc)
{
    if(g_iReturnType != 1)
    {
        int iUserId = hEvent.GetInt("attacker");
        if(iUserId != hEvent.GetInt("userid"))
        {
            int iWeapon = GetEntPropEnt(GetClientOfUserId(iUserId), Prop_Data, "m_hActiveWeapon");
            if(iWeapon > 0) SetEntProp(iWeapon, Prop_Send, "m_iClip1", g_iAmmo[GWI(iWeapon)] +1);
        }
    }
}
 

Вложения

Последнее редактирование:
2  
Сообщения
73
Рейтинг
34
#6
А для reload лучше прикрутить m_iPrimaryReserveAmmoCount как по мне, с каким нить повторяющимся CreateDataTimer на проверку заполненности обоймы, ибо как то по читерски получается с m_iClip1
 
 
Сообщения
50
Рейтинг
63
#7
А для reload лучше прикрутить m_iPrimaryReserveAmmoCount как по мне, с каким нить повторяющимся CreateDataTimer на проверку заполненности обоймы, ибо как то по читерски получается с m_iClip1
Для чего, в чем читерство проявляется? Нет смысла делать проверки на запас патронов...
Лишний таймер, переменные, и конечно же проверки.

0: Будет возвращать обойму, но при перезарядке заберет патроны из запаса.

1: Не будет забирать из запаса патроны, т.к обойма уже будет иметь недостающие в себе патроны.
При этом анимация перезарядки будет и игрок не сможет стрелять пока не перезарядится.

Да, тут возможно есть читерство.
Игрок перезаряжается, резко меняет оружие и возращает обратно, то есть перезарядка была быстрее обычного
(анимация сменилась с 'перезарядки' на 'смена оружия')

2: Думаю и так понятно. Игрок убивает игрока, патроны в обойме меняются, в запасе же ничего не происходит, т.е сохраняем и то и другое.


Секундное преимущество, а оно того стоит? Либо я тебя не понял.
 
 
Сообщения
73
Рейтинг
34
#8
Я прост глянул на код увидев SDKHook_ReloadPost а в данном случае можно прервать перезарядку при возвращении запаса
 
 
Сообщения
50
Рейтинг
63
#9
в данном случае можно прервать перезарядку при возвращении запаса
Прервать анимацию можно при помощи другой анимации, сама перезарядка работает должным образом. Посмотри видео :smile3:
То о чем ты написал можно реализовать следующем образом, если прям очень хочется исправить описанный баг:
Код:
#include <sdkhooks>

#define GWI(%0) GetEntProp(%0, Prop_Send, "m_iItemDefinitionIndex") +1

public Plugin myinfo =
{
    name = "Return Ammo",
    author = "Drumanid",
    version = "1.0.2",
    url = "http://vk.com/drumanid"
};

enum {CLIP, RESERVE};
int g_iAmmo[2][64], g_iReturnType;

public void OnPluginStart()
{
    HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);

    ConVar hCvar = CreateConVar("sm_return_ammo", "2", "0 - возвращать обойму / 1 - запас / 2 - запас и обойму");
    hCvar.AddChangeHook(HookCvarTime); g_iReturnType = hCvar.IntValue;
    AutoExecConfig(true, "ReturnAmmo");
}

public void HookCvarTime(ConVar hCvar, const char[] sOldValue, const char[] sNewValue) { g_iReturnType = hCvar.IntValue; }
public void OnClientPostAdminCheck(int iClient) { SDKHook(iClient, SDKHook_WeaponEquipPost, WeaponEquipPost); }

public void WeaponEquipPost(int iClient, int iWeapon)
{
    int iNumber = GWI(iWeapon);
    if(g_iAmmo[CLIP][iNumber] == 0) g_iAmmo[CLIP][iNumber] = GetEntProp(iWeapon, Prop_Send, "m_iClip1");
    if(g_iAmmo[RESERVE][iNumber] == 0) g_iAmmo[RESERVE][iNumber] = GetEntProp(iWeapon, Prop_Send, "m_iPrimaryReserveAmmoCount");
}

public void OnEntityCreated(int iEntity, const char[] sClassname)
{
    if(g_iReturnType != 0 && StrContains(sClassname, "weapon_", false) != -1) SDKHook(iEntity, SDKHook_ReloadPost, ReloadPost);
}

public void ReloadPost(int iWeapon, bool bSuccess)
{
    if(bSuccess) CreateTimer(GetEntPropFloat(iWeapon, Prop_Send, "m_flTimeWeaponIdle") - 1.0 - GetGameTime(), ReturnAmmoTimer, EntIndexToEntRef(iWeapon), TIMER_FLAG_NO_MAPCHANGE);
}

public Action ReturnAmmoTimer(Handle hTimer, any iWeapon)
{
    if((iWeapon = EntRefToEntIndex(iWeapon)) != INVALID_ENT_REFERENCE && IsValidEntity(iWeapon) && GetEntProp(iWeapon, Prop_Data, "m_bInReload"))
    {
        SetEntProp(iWeapon, Prop_Send, "m_iPrimaryReserveAmmoCount", g_iAmmo[RESERVE][GWI(iWeapon)]);
    }

    return Plugin_Stop;
}

public void Event_PlayerDeath(Event hEvent, const char[] sName, bool bDbc)
{
    if(g_iReturnType != 1)
    {
        int iUserId = hEvent.GetInt("attacker");
        if(iUserId != hEvent.GetInt("userid"))
        {
            int iWeapon = GetEntPropEnt(GetClientOfUserId(iUserId), Prop_Data, "m_hActiveWeapon");
            if(iWeapon > 0) SetEntProp(iWeapon, Prop_Send, "m_iClip1", g_iAmmo[CLIP][GWI(iWeapon)] +1);
        }
    }
}
Но это еще не все... В CS:GO есть такой баг:

На кастомных картах патроны не всегда меняются, если оружие у игрока в руках.
На стандартных же картах все работает нормально. С чем это связано ? Я не знаю.
Как это решить? Телепортировать оружие в NULL_VECTOR / принудительно заставить выбросить оружие, либо вовсе пересоздать и возвращать оружие при помощи функции: EquipPlayerWeapon
Код:
#include <sdkhooks>

#define GWI(%0) GetEntProp(%0, Prop_Send, "m_iItemDefinitionIndex") +1

public Plugin myinfo =
{
    name = "Return Ammo",
    author = "Drumanid",
    version = "1.0.2",
    url = "http://vk.com/drumanid"
};

enum {CLIP, RESERVE};
int g_iAmmo[2][64], g_iReturnType;

public void OnPluginStart()
{
    HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);

    ConVar hCvar = CreateConVar("sm_return_ammo", "2", "0 - возвращать обойму / 1 - запас / 2 - запас и обойму");
    hCvar.AddChangeHook(HookCvarTime); g_iReturnType = hCvar.IntValue;
    AutoExecConfig(true, "ReturnAmmo");
}

public void HookCvarTime(ConVar hCvar, const char[] sOldValue, const char[] sNewValue) { g_iReturnType = hCvar.IntValue; }
public void OnClientPostAdminCheck(int iClient) { SDKHook(iClient, SDKHook_WeaponEquipPost, WeaponEquipPost); }

public void WeaponEquipPost(int iClient, int iWeapon)
{
    int iNumber = GWI(iWeapon);
    if(g_iAmmo[CLIP][iNumber] == 0) g_iAmmo[CLIP][iNumber] = GetEntProp(iWeapon, Prop_Send, "m_iClip1");
    if(g_iAmmo[RESERVE][iNumber] == 0) g_iAmmo[RESERVE][iNumber] = GetEntProp(iWeapon, Prop_Send, "m_iAmmo");
}

public void OnEntityCreated(int iEntity, const char[] sClassname)
{
    if(g_iReturnType != 0 && StrContains(sClassname, "weapon_", false) != -1) SDKHook(iEntity, SDKHook_ReloadPost, ReloadPost);
}

public void ReloadPost(int iWeapon, bool bSuccess)
{
    if(bSuccess) CreateTimer(GetEntPropFloat(iWeapon, Prop_Send, "m_flTimeWeaponIdle") - 1.0 - GetGameTime(), ReturnAmmoTimer, EntIndexToEntRef(iWeapon), TIMER_FLAG_NO_MAPCHANGE);
}

public Action ReturnAmmoTimer(Handle hTimer, any iWeapon)
{
    if((iWeapon = EntRefToEntIndex(iWeapon)) != INVALID_ENT_REFERENCE && IsValidEntity(iWeapon) && GetEntProp(iWeapon, Prop_Data, "m_bInReload"))
    {
        SetEntProp(iWeapon, Prop_Send, "m_iAmmo", g_iAmmo[RESERVE][GWI(iWeapon)]);
    }

    return Plugin_Stop;
}

public void Event_PlayerDeath(Event hEvent, const char[] sName, bool bDbc)
{
    if(g_iReturnType != 1)
    {
        int iUserId = hEvent.GetInt("attacker");
        if(iUserId != hEvent.GetInt("userid"))
        {
            int iWeapon = GetEntPropEnt(GetClientOfUserId(iUserId), Prop_Data, "m_hActiveWeapon");
            if(iWeapon > 0) SetEntProp(iWeapon, Prop_Send, "m_iClip1", g_iAmmo[CLIP][GWI(iWeapon)] +1);
        }
    }
}

Как-то так...
 
Последнее редактирование:
1  
Сообщения
73
Рейтинг
34
#10
ну я про ет и говорил, CreateDataTimer будет как по мне лучше ибо уверенность в убийстве таймера больше (ну чтоб утечек не было)
21 Окт 2018
Не пойму данный кусок
C++:
GetEntPropFloat(weapon, Prop_Send, "m_flTimeWeaponIdle") - 1.0 - GetGameTime()
И есть ли возможность узнать время перезарядки ствола?
21 Окт 2018
Хотя как я понял ет оно и есть m_flTimeWeaponIdle
 
 
Сообщения
50
Рейтинг
63
#11
ну я про ет и говорил, CreateDataTimer будет как по мне лучше ибо уверенность в убийстве таймера больше (ну чтоб утечек не было)
Через CreateDataTimer ты не передашь данные так как я передал в CreateTimer, в третьем аргументе используется 'Handle', не 'any', т.е мне надо будет создавать DataPack, чтобы передать одно значение...

Таймер будет уничтожен если правильно его 'убивать'. Я в свое время иногда тоже не понимал почему не убиваются.:thank_you:
Можно зарегистрировать глобальный Handle и записать в него таймер, а после убивать когда тебе это необходимо, но это лишнее...
Код:
void Test(int iWeapon)
{
    DataPack hPack;
    CreateDataTimer(0.0, TimerTest, hPack);
    hPack.WriteCell(iWeapon);
}

public Action TimerTest(Handle hTimer, Handle hDataPack)
{
    DataPack hPack = view_as<DataPack>(hDataPack); hPack.Reset();
    int iWeapon = hPack.ReadCell();
    if(iWeapon ...)

    return Plugin_Stop;
}
Код:
stock Handle CreateDataTimer(float interval, Timer func, Handle &datapack, int flags=0)
{
    datapack = new DataPack();
    flags |= TIMER_DATA_HNDL_CLOSE;
    return CreateTimer(interval, func, datapack, flags);
}

И есть ли возможность узнать время перезарядки ствола?
Не сказал бы что это 'время перезарядки оружия', скорее 'бездействие' :good2:
 
Последнее редактирование:
 
Сообщения
73
Рейтинг
34
#12
Ну мне только обоймы нужны были, так шо переделал так

C++:
public void OnClientPostAdminCheck(int client) {
    SDKHook(client, SDKHook_WeaponEquipPost, WeaponEquipPost);
}
public void WeaponEquipPost(int client, int weapon) {
    int iNumber = GWI(weapon);
    if(g_iAmmo[iNumber] == 0)
        g_iAmmo[iNumber] = GetEntProp(weapon, Prop_Send, "m_iPrimaryReserveAmmoCount");
}

public void OnEntityCreated(int iEntity, const char[] sClassname) {
    if(StrContains(sClassname, "weapon_", false) != -1)
        SDKHook(iEntity, SDKHook_ReloadPost, ReloadPost);
}

public void ReloadPost(int weapon, bool bSuccessful) {
    if(bSuccessful) {
        int client_ix = GetEntPropEnt(weapon, Prop_Data, "m_hOwner");
        DataPack hData;
        float time_calc = GetEntPropFloat(weapon, Prop_Send, "m_flTimeWeaponIdle") - 1.0 - GetGameTime();
        CreateDataTimer(time_calc, Timer_ShotgunReloadEnd, hData, TIMER_FLAG_NO_MAPCHANGE);
        hData.WriteCell(EntIndexToEntRef(weapon));
        hData.WriteCell(GetClientUserId(client_ix));
    }
}

public Action Timer_ShotgunReloadEnd(Handle timer, DataPack data) {
    data.Reset();
    int weapon = EntRefToEntIndex(data.ReadCell());
    int client_ix = GetClientOfUserId(data.ReadCell());

    if(IsValidEntity(weapon) && GetEntProp(weapon, Prop_Data, "m_bInReload") && IsValidClient(client_ix) && IsPlayerAlive(client_ix) && (GetUserFlagBits(client_ix) & (user_flag|ADMFLAG_ROOT)) && g_bAmmoEnabled[client_ix])
        SetEntProp(weapon, Prop_Send, "m_iPrimaryReserveAmmoCount", g_iAmmo[GWI(weapon)]);

    return Plugin_Stop;
}
 
 
Сообщения
50
Рейтинг
63
#13
SAZONISCHE, нууу хоть понятно что ты хотел сделать :derisive:
Правда я все равно не понимаю зачем тебе CreateDataTimer...

Код:
public void OnClientPostAdminCheck(int client) {
    SDKHook(client, SDKHook_WeaponEquipPost, WeaponEquipPost);
}
public void WeaponEquipPost(int client, int weapon) {
    int iNumber = GWI(weapon);
    if(g_iAmmo[iNumber] == 0)
        g_iAmmo[iNumber] = GetEntProp(weapon, Prop_Send, "m_iPrimaryReserveAmmoCount");
}

public void OnEntityCreated(int iEntity, const char[] sClassname) {
    if(StrContains(sClassname, "weapon_", false) != -1)
        SDKHook(iEntity, SDKHook_ReloadPost, ReloadPost);
}

public void ReloadPost(int weapon, bool bSuccessful) {
    if(bSuccessful) {
        CreateTimer(GetEntPropFloat(weapon, Prop_Send, "m_flTimeWeaponIdle") - 1.0 - GetGameTime(), Timer_ShotgunReloadEnd, EntIndexToEntRef(weapon), TIMER_FLAG_NO_MAPCHANGE);
    }
}

public Action Timer_ShotgunReloadEnd(Handle timer, any weapon) {
    weapon = EntRefToEntIndex(weapon);
    if(weapon != INVALID_ENT_REFERENCE && IsValidEntity(weapon) && GetEntProp(weapon, Prop_Data, "m_bInReload"))
    {
        int client_ix = GetEntPropEnt(weapon, Prop_Data, "m_hOwner");
        if(IsValidClient(client_ix) && IsPlayerAlive(client_ix) && (GetUserFlagBits(client_ix) & (user_flag|ADMFLAG_ROOT)) && g_bAmmoEnabled[client_ix])
            SetEntProp(weapon, Prop_Send, "m_iPrimaryReserveAmmoCount", g_iAmmo[GWI(weapon)]);
    }

    return Plugin_Stop;
}
 
1  

Пользователи, читающие эту тему

Сейчас на форуме нет ни одного пользователя.
> > > >