Объявления
Поздравляем
Roman2211


Друзья, если не получается зарегистрироваться, напишите на почту vdv_forever@bk.ru.
Я оторву свою задницу от всех дел и обязательно Вас активирую! :smile10:
Добро пожаловать на геройский форум! :smile25:

Как создать плагин для HD мода

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 31 окт 2020, 18:18

Ну наконец-то все заработало. Все таки проблема была в кривой структуре из homm3.h.
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 31 окт 2020, 18:32

Теперь новая проблема с масспелами:

Код: Выделить всё
// dllmain.cpp: определяет точку входа для приложения DLL.
#define _CRT_SECURE_NO_WARNINGS

#include "stdafx.h"
#include "HotA\patcher_x86.hpp"
#include "HotA\homm3.h"

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

struct SpellDesc
{
   char* DefaultDesc;
   char* BasicDesc;
   char* AdvancedDesc;
   char* ExpertDesc;
};

SpellDesc MirthDesc =
{
   "{Радость}\n\n+1 к боевому духу всех дружественных отрядов.",
   "{Базовая Радость}\n\n+1 к боевому духу всех дружественных отрядов.",
   "{Продвинутая Радость}\n\n+2 к боевому духу всех дружественных отрядов.",
   "{Экспертная Радость}\n\n+3 к боевому духу всех дружественных отрядов."
};

SpellDesc SorrowDesc =
{
   "{Печаль}\n\n-1 к боевому духу всех вражеских отрядов.",
   "{Базовая Печаль}\n\n-1 к боевому духу всех вражеских отрядов.",
   "{Продвинутая Печаль}\n\n-2 к боевому духу всех вражеских отрядов.",
   "{Экспертная Печаль}\n\n-3 к боевому духу всех вражеских отрядов."
};

SpellDesc FortuneDesc =
{
   "{Удача}\n\n+1 к удаче всех дружественных отрядов.",
   "{Базовая Удача}\n\n+1 к удаче всех дружественных отрядов.",
   "{Продвинутая Удача}\n\n+2 к удаче всех дружественных отрядов.",
   "{Экспертная Удача}\n\n+3 к удаче всех дружественных отрядов."
};

SpellDesc MisfortuneDesc =
{
   "{Неудача}\n\n-1 к удаче всех вражеских отрядов.",
   "{Базовая Неудача}\n\n-1 к удаче всех вражеских отрядов.",
   "{Продвинутая Неудача}\n\n-2 к удаче всех вражеских отрядов.",
   "{Экспертная Неудача}\n\n-3 к удаче всех вражеских отрядов."
};

SpellDesc HasteDesc =
{
   "{Ускорение}\n\n+3 к скорости всех дружественных отрядов.",
   "{Базовое Ускорение}\n\n+3 к скорости всех дружественных отрядов.",
   "{Продвинутое Ускорение}\n\n+5 к скорости всех дружественных отрядов.",
   "{Экспертное Ускорение}\n\n+7 к скорости всех дружественных отрядов."
};

SpellDesc SlowDesc =
{
   "{Медлительность}\n\n-20% к скорости всех вражеских отрядов.",
   "{Базовая Медлительность}\n\n-20% к скорости всех вражеских отрядов.",
   "{Продвинутая Медлительность}\n\n-40% к скорости всех вражеских отрядов.",
   "{Экспертная Медлительность}\n\n-60% к скорости всех вражеских отрядов."
};

int __stdcall changeSpellDesc(LoHook* h, HookContext* c)
{
   *(SpellDesc*)(o_Spell + SPL_MIRTH)->description = MirthDesc;
   *(SpellDesc*)(o_Spell + SPL_SORROW)->description = SorrowDesc;
   *(SpellDesc*)(o_Spell + SPL_FORTUNE)->description = FortuneDesc;
   *(SpellDesc*)(o_Spell + SPL_MISFORTUNE)->description = MisfortuneDesc;
   *(SpellDesc*)(o_Spell + SPL_HASTE)->description = HasteDesc;
   *(SpellDesc*)(o_Spell + SPL_SLOW)->description = SlowDesc;

   return EXEC_DEFAULT;
}

char __stdcall IsSpellSingleTarget(HiHook *h, int spell, int ssLevel)
{
   switch (spell)
   {
   case 49: case 50: case 51: case 52: case 53: case 54:
      return FALSE;
      break;
   default:
      return CALL_2(char, __fastcall, h->GetDefaultFunc(), spell, ssLevel);
   }
}

bool __stdcall changeLMSpells(HiHook* hook)
{
   bool res = CALL_0(bool, __cdecl, hook->GetDefaultFunc());

   int LMEffects[] = { 1, 1, 2, 3 };

   for (int i = 0; i < 4; ++i)
   {
      o_Spell[SPL_MIRTH].effect[i] = LMEffects[i];
      o_Spell[SPL_SORROW].effect[i] = -LMEffects[i];
      o_Spell[SPL_FORTUNE].effect[i] = LMEffects[i];
      o_Spell[SPL_MISFORTUNE].effect[i] = -LMEffects[i];
   }

   o_Spell[SPL_HASTE].effect[3] = 7;

   float SlowValueOne = 0.20f;
   float SlowValueTwo = 0.40f;
   float SlowValueThree = 0.60f;

   o_Spell[SPL_SLOW].effect[0] = (int)&SlowValueOne;
   o_Spell[SPL_SLOW].effect[1] = (int)&SlowValueOne;
   o_Spell[SPL_SLOW].effect[2] = (int)&SlowValueTwo;
   o_Spell[SPL_SLOW].effect[3] = (int)&SlowValueThree;

   // Здесь заодно можно поменять описания

   return res;
}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
   if (DLL_PROCESS_ATTACH == ul_reason_for_call)
   {
      if (!plugin_On)
      {
         plugin_On = 1;
         _P = GetPatcher();
         _PI = _P->CreateInstance("HD.Plugin.massSpell");

         _PI->WriteHiHook(0x59E360, SPLICE_, EXTENDED_, FASTCALL_, IsSpellSingleTarget);
   //      _PI->WriteHiHook(0x4EDEAF, SPLICE_, EXTENDED_, THISCALL_, changeLMSpells);
         _PI->WriteLoHook(0x59E437, changeSpellDesc);
      }
   }

   return TRUE;
}


Ваш HiHook changeLMSpells (закоментированный) крашит мод при запуске. Хук IsSpellSingleTarget от RoseKavalier отрабатывает без проблем.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: Как создать плагин для HD мода

Сообщение AlexSpl » 31 окт 2020, 18:33

Нужно будет её как-нибудь "проапгрейдить": добавить известные поля и убрать проблему с выравниваем с помощью #pragma pack(push, 1). А может быть, написать туториал по H3API. Там всё чётко.

Цитата:
Ваш HiHook changeLMSpells (закоментированный) крашит мод при запуске.

Для изменения тесктовиков используйте afterInit().
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 31 окт 2020, 18:44

AlexSpl писал(а):

Для изменения тесктовиков используйте afterInit().

Да, вы писали за afterInit(). Но мне нужен пример, что разобраться, как через него менять.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: Как создать плагин для HD мода

Сообщение AlexSpl » 31 окт 2020, 18:48

Просто копируете код из других хуков, который меняет данные из текстовиков, в тело afterInit(). Например, код из неработающего хайхука, начиная с int и до комментария.
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 31 окт 2020, 18:50

Я вот закоментил вызов лоухука, который меняет описания _PI->WriteLoHook(0x59E437, changeSpellDesc); и все равно краш.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: Как создать плагин для HD мода

Сообщение AlexSpl » 31 окт 2020, 18:54

Посмотрите функцию afterInit() из плагина с массхилом. Вот все изменения описаний и констант из текстовиков нужно вставлять туда, а старые хуки удалять. Сами структуры с описаниями должны, разумеется, быть вне тела afterInit(), т.е. глобальными.
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 31 окт 2020, 19:04

Я понял.

Код: Выделить всё
// dllmain.cpp: определяет точку входа для приложения DLL.
#define _CRT_SECURE_NO_WARNINGS

#include "stdafx.h"
#include "HotA\patcher_x86.hpp"
#include "HotA\homm3.h"

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

struct SpellDesc
{
   char* DefaultDesc;
   char* BasicDesc;
   char* AdvancedDesc;
   char* ExpertDesc;
};

SpellDesc MirthDesc =
{
   "{Радость}\n\n+1 к боевому духу всех дружественных отрядов.",
   "{Базовая Радость}\n\n+1 к боевому духу всех дружественных отрядов.",
   "{Продвинутая Радость}\n\n+2 к боевому духу всех дружественных отрядов.",
   "{Экспертная Радость}\n\n+3 к боевому духу всех дружественных отрядов."
};

SpellDesc SorrowDesc =
{
   "{Печаль}\n\n-1 к боевому духу всех вражеских отрядов.",
   "{Базовая Печаль}\n\n-1 к боевому духу всех вражеских отрядов.",
   "{Продвинутая Печаль}\n\n-2 к боевому духу всех вражеских отрядов.",
   "{Экспертная Печаль}\n\n-3 к боевому духу всех вражеских отрядов."
};

SpellDesc FortuneDesc =
{
   "{Удача}\n\n+1 к удаче всех дружественных отрядов.",
   "{Базовая Удача}\n\n+1 к удаче всех дружественных отрядов.",
   "{Продвинутая Удача}\n\n+2 к удаче всех дружественных отрядов.",
   "{Экспертная Удача}\n\n+3 к удаче всех дружественных отрядов."
};

SpellDesc MisfortuneDesc =
{
   "{Неудача}\n\n-1 к удаче всех вражеских отрядов.",
   "{Базовая Неудача}\n\n-1 к удаче всех вражеских отрядов.",
   "{Продвинутая Неудача}\n\n-2 к удаче всех вражеских отрядов.",
   "{Экспертная Неудача}\n\n-3 к удаче всех вражеских отрядов."
};

SpellDesc HasteDesc =
{
   "{Ускорение}\n\n+3 к скорости всех дружественных отрядов.",
   "{Базовое Ускорение}\n\n+3 к скорости всех дружественных отрядов.",
   "{Продвинутое Ускорение}\n\n+5 к скорости всех дружественных отрядов.",
   "{Экспертное Ускорение}\n\n+7 к скорости всех дружественных отрядов."
};

SpellDesc SlowDesc =
{
   "{Медлительность}\n\n-20% к скорости всех вражеских отрядов.",
   "{Базовая Медлительность}\n\n-20% к скорости всех вражеских отрядов.",
   "{Продвинутая Медлительность}\n\n-40% к скорости всех вражеских отрядов.",
   "{Экспертная Медлительность}\n\n-60% к скорости всех вражеских отрядов."
};

char __stdcall IsSpellSingleTarget(HiHook *h, int spell, int ssLevel)
{
   switch (spell)
   {
   case 49: case 50: case 51: case 52: case 53: case 54:
      return FALSE;
      break;
   default:
      return CALL_2(char, __fastcall, h->GetDefaultFunc(), spell, ssLevel);
   }
}

bool __stdcall changeLMSpells(HiHook* hook)
{
   bool res = CALL_0(bool, __cdecl, hook->GetDefaultFunc());

   int LMEffects[] = { 1, 1, 2, 3 };

   for (int i = 0; i < 4; ++i)
   {
      o_Spell[SPL_MIRTH].effect[i] = LMEffects[i];
      o_Spell[SPL_SORROW].effect[i] = -LMEffects[i];
      o_Spell[SPL_FORTUNE].effect[i] = LMEffects[i];
      o_Spell[SPL_MISFORTUNE].effect[i] = -LMEffects[i];
   }

   o_Spell[SPL_HASTE].effect[3] = 7;

   float SlowValueOne = 0.20f;
   float SlowValueTwo = 0.40f;
   float SlowValueThree = 0.60f;

   o_Spell[SPL_SLOW].effect[0] = (int)&SlowValueOne;
   o_Spell[SPL_SLOW].effect[1] = (int)&SlowValueOne;
   o_Spell[SPL_SLOW].effect[2] = (int)&SlowValueTwo;
   o_Spell[SPL_SLOW].effect[3] = (int)&SlowValueThree;

   *(SpellDesc*)(o_Spell + SPL_MIRTH)->description = MirthDesc;
   *(SpellDesc*)(o_Spell + SPL_SORROW)->description = SorrowDesc;
   *(SpellDesc*)(o_Spell + SPL_FORTUNE)->description = FortuneDesc;
   *(SpellDesc*)(o_Spell + SPL_MISFORTUNE)->description = MisfortuneDesc;
   *(SpellDesc*)(o_Spell + SPL_HASTE)->description = HasteDesc;
   *(SpellDesc*)(o_Spell + SPL_SLOW)->description = SlowDesc;

   // Здесь заодно можно поменять описания

   return res;
}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
   if (DLL_PROCESS_ATTACH == ul_reason_for_call)
   {
      if (!plugin_On)
      {
         plugin_On = 1;
         _P = GetPatcher();
         _PI = _P->CreateInstance("HD.Plugin.massSpell");

         _PI->WriteHiHook(0x59E360, SPLICE_, EXTENDED_, FASTCALL_, IsSpellSingleTarget);
         _PI->WriteHiHook(0x4EDEAF, SPLICE_, EXTENDED_, THISCALL_, changeLMSpells);
      }
   }

   return TRUE;
}


Но, увы, все равно краш...
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: Как создать плагин для HD мода

Сообщение AlexSpl » 31 окт 2020, 19:13

А где лоухук afterInit()? :smile5:
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 31 окт 2020, 19:22

Есть лоухук теперь:

Код: Выделить всё
// dllmain.cpp: определяет точку входа для приложения DLL.
#define _CRT_SECURE_NO_WARNINGS

#include "stdafx.h"
#include "HotA\patcher_x86.hpp"
#include "HotA\homm3.h"

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

struct SpellDesc
{
   char* DefaultDesc;
   char* BasicDesc;
   char* AdvancedDesc;
   char* ExpertDesc;
};

SpellDesc MirthDesc =
{
   "{Радость}\n\n+1 к боевому духу всех дружественных отрядов.",
   "{Базовая Радость}\n\n+1 к боевому духу всех дружественных отрядов.",
   "{Продвинутая Радость}\n\n+2 к боевому духу всех дружественных отрядов.",
   "{Экспертная Радость}\n\n+3 к боевому духу всех дружественных отрядов."
};

SpellDesc SorrowDesc =
{
   "{Печаль}\n\n-1 к боевому духу всех вражеских отрядов.",
   "{Базовая Печаль}\n\n-1 к боевому духу всех вражеских отрядов.",
   "{Продвинутая Печаль}\n\n-2 к боевому духу всех вражеских отрядов.",
   "{Экспертная Печаль}\n\n-3 к боевому духу всех вражеских отрядов."
};

SpellDesc FortuneDesc =
{
   "{Удача}\n\n+1 к удаче всех дружественных отрядов.",
   "{Базовая Удача}\n\n+1 к удаче всех дружественных отрядов.",
   "{Продвинутая Удача}\n\n+2 к удаче всех дружественных отрядов.",
   "{Экспертная Удача}\n\n+3 к удаче всех дружественных отрядов."
};

SpellDesc MisfortuneDesc =
{
   "{Неудача}\n\n-1 к удаче всех вражеских отрядов.",
   "{Базовая Неудача}\n\n-1 к удаче всех вражеских отрядов.",
   "{Продвинутая Неудача}\n\n-2 к удаче всех вражеских отрядов.",
   "{Экспертная Неудача}\n\n-3 к удаче всех вражеских отрядов."
};

SpellDesc HasteDesc =
{
   "{Ускорение}\n\n+3 к скорости всех дружественных отрядов.",
   "{Базовое Ускорение}\n\n+3 к скорости всех дружественных отрядов.",
   "{Продвинутое Ускорение}\n\n+5 к скорости всех дружественных отрядов.",
   "{Экспертное Ускорение}\n\n+7 к скорости всех дружественных отрядов."
};

SpellDesc SlowDesc =
{
   "{Медлительность}\n\n-20% к скорости всех вражеских отрядов.",
   "{Базовая Медлительность}\n\n-20% к скорости всех вражеских отрядов.",
   "{Продвинутая Медлительность}\n\n-40% к скорости всех вражеских отрядов.",
   "{Экспертная Медлительность}\n\n-60% к скорости всех вражеских отрядов."
};

int __stdcall afterInit(LoHook* h, HookContext* c)
{
   *(SpellDesc*)(o_Spell + SPL_MIRTH)->description = MirthDesc;
   *(SpellDesc*)(o_Spell + SPL_SORROW)->description = SorrowDesc;
   *(SpellDesc*)(o_Spell + SPL_FORTUNE)->description = FortuneDesc;
   *(SpellDesc*)(o_Spell + SPL_MISFORTUNE)->description = MisfortuneDesc;
   *(SpellDesc*)(o_Spell + SPL_HASTE)->description = HasteDesc;
   *(SpellDesc*)(o_Spell + SPL_SLOW)->description = SlowDesc;

   return EXEC_DEFAULT;
}


char __stdcall IsSpellSingleTarget(HiHook *h, int spell, int ssLevel)
{
   switch (spell)
   {
   case 49: case 50: case 51: case 52: case 53: case 54:
      return FALSE;
      break;
   default:
      return CALL_2(char, __fastcall, h->GetDefaultFunc(), spell, ssLevel);
   }
}

bool __stdcall changeLMSpells(HiHook* hook)
{
   bool res = CALL_0(bool, __cdecl, hook->GetDefaultFunc());

   int LMEffects[] = { 1, 1, 2, 3 };

   for (int i = 0; i < 4; ++i)
   {
      o_Spell[SPL_MIRTH].effect[i] = LMEffects[i];
      o_Spell[SPL_SORROW].effect[i] = -LMEffects[i];
      o_Spell[SPL_FORTUNE].effect[i] = LMEffects[i];
      o_Spell[SPL_MISFORTUNE].effect[i] = -LMEffects[i];
   }

   o_Spell[SPL_HASTE].effect[3] = 7;

   float SlowValueOne = 0.20f;
   float SlowValueTwo = 0.40f;
   float SlowValueThree = 0.60f;

   o_Spell[SPL_SLOW].effect[0] = (int)&SlowValueOne;
   o_Spell[SPL_SLOW].effect[1] = (int)&SlowValueOne;
   o_Spell[SPL_SLOW].effect[2] = (int)&SlowValueTwo;
   o_Spell[SPL_SLOW].effect[3] = (int)&SlowValueThree;



   // Здесь заодно можно поменять описания

   return res;
}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
   if (DLL_PROCESS_ATTACH == ul_reason_for_call)
   {
      if (!plugin_On)
      {
         plugin_On = 1;
         _P = GetPatcher();
         _PI = _P->CreateInstance("HD.Plugin.massSpell");

         _PI->WriteHiHook(0x59E360, SPLICE_, EXTENDED_, FASTCALL_, IsSpellSingleTarget);
         _PI->WriteHiHook(0x4EDEAF, SPLICE_, EXTENDED_, THISCALL_, changeLMSpells);
         _PI->WriteLoHook(0x59E437, afterInit);
      }
   }

   return TRUE;
}


И все равно краш... :smile29:
Вернуться к началу

Пред.След.

Вернуться в Общий раздел

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4

cron