Объявления

Форум о Героях Меча и Магии и King's Bounty. Если Вы любите эти игры, мы будем рады видеть Вас в наших рядах. :smile2:

Энциклопедия алгоритмов HoMM 3

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 16 мар 2019, 05:02

По поводу miPassability[mapSize][mapSize] в посте выше.

На самом деле все проще:

_MapItem_
attrib

01 - непроходимо (красная клетка)
10 - есть что-то, что надо настраивать (желтая клетка)
02 - граница к воде ???

(WoG sources) :smile1:
Вернуться к началу

offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 17 мар 2019, 20:00

Feanor

Цитата:
In TSW I made this like:
Код: Выделить всё
int __stdcall OnSpecBuildAIValueCalc(LoHook* h, HookContext* c)
{
_Town_* town = (_Town_*)c->ebx;
int building = c->edi;

if (town->type == 5 && building == 22)
{
int value = 1;
if(town->field_03)
{
value = -1;
}
else
{
//should be dynamic, but for now some big value is a fine too.
value = 9999;
}
c->return_address =  0x42B1CB;
*(int*)(c->ebp + c->edi * 4 - 0x114) = value;
return NO_EXEC_DEFAULT;
}
return EXEC_DEFAULT;
}

citrine->WriteLoHook(0x42B04D, (void*)OnSpecBuildAIValueCalc);

Вернуться к началу

offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 17 мар 2019, 20:03

Sav, старый пост на Вог форуме

Sav_data.txt
(69.49 КБ) Скачиваний: 33
Вернуться к началу

offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 31 мар 2019, 19:03

RK
Код: Выделить всё
        int __thiscall RMG_CalculateMovementCost_00547D70(_RMGStruct_ *this, int x, int y, int z)
        {
          _RMGStruct_ *v4; // ebx
          _RMGMapItem_ *v5; // eax
          int z_; // esi
          _RMGMapItem_ *mitems; // edi
          int v8; // eax
          _RMGMapItem_ *mi; // eax
          unsigned int v10; // ecx
          _RMGObjectPrototype_ *v11; // ecx
          int objectType; // edx
          _ItemSettings_ *itemSet; // eax
          int v14; // eax
          unsigned int v15; // ecx
          _RMGObject_ **v16; // eax
          _RMGObject_ *v17; // eax
          int *v18; // eax
          int v19; // ecx
          int v20; // edi
          _RMGMapItem_ *v21; // eax
          int v22; // ecx
          int v23; // ST20_4
          int v24; // edx
          RMG_Point *v25; // eax
          int v26; // esi
          int v27; // ecx
          int v28; // ecx
          int v29; // ecx
          int v30; // edx
          int v31; // esi
          int subtype_; // edx
          unsigned int v33; // ecx
          _RMGObject_ **v34; // eax
          _RMGObject_ *v35; // eax
          RMG_Point *v36; // eax
          int x_2; // ecx
          int movCost; // edi
          _RMGMapItem_ *v39; // eax
          int v40; // ecx
          int v41; // edx
          RMG_Point *v42; // eax
          int cost_2; // esi
          int v44; // ecx
          int v45; // ecx
          int v46; // ecx
          int v47; // edx
          int v48; // esi
          int v49; // eax
          _RMGMapItem_ *v50; // edx
          _RMGMapItem_ *v51; // eax
          int v52; // edx
          int v53; // edi
          int *v54; // eax
          int v55; // ecx
          int v56; // esi
          int v57; // edx
          int v58; // edx
          int v59; // esi
          int v60; // eax
          _BOOL1 noDirectionAvailable; // zf
          POINT *offset; // ecx
          LONG xOffset; // eax
          LONG yOffset; // edx
          int _x; // ecx
          int _y; // edx
          int height; // eax
          _RMGMapItem_ *mi_offset; // eax
          int landType; // ecx
          unsigned int v70; // edx
          _ItemSettings_ *itSet; // ecx
          signed int costToMove; // edi
          int v73; // ecx
          unsigned __int16 prevMovementCost; // dx
          int xStart; // edx
          int v76; // edi
          RMG_Point *v77; // eax
          int newCostToMove_WORD; // esi
          int y_2; // ecx
          int zStart; // edx
          int v81; // ecx
          int currentPoint; // edx
          int v83; // esi
          int v84; // edx
          RMG_Point v86; // [esp+18h] [ebp-B0h]
          int v87; // [esp+24h] [ebp-A4h]
          int v88; // [esp+2Ch] [ebp-9Ch]
          RMG_Point a3; // [esp+30h] [ebp-98h]
          RMG_Point pt2; // [esp+3Ch] [ebp-8Ch]
          int yStart; // [esp+4Ch] [ebp-7Ch]
          int z_1; // [esp+50h] [ebp-78h]
          RMG_Point v93; // [esp+54h] [ebp-74h]
          int v94; // [esp+60h] [ebp-68h]
          int v95; // [esp+64h] [ebp-64h]
          int v96; // [esp+68h] [ebp-60h]
          RMG_Point pt1; // [esp+6Ch] [ebp-5Ch]
          int movementCost; // [esp+78h] [ebp-50h]
          _Memory_ minimumCost; // [esp+7Ch] [ebp-4Ch]
          int x_2_; // [esp+8Ch] [ebp-3Ch]
          int y_2_; // [esp+90h] [ebp-38h]
          int z_2; // [esp+94h] [ebp-34h]
          Vector_RMG_Point minimumPath; // [esp+98h] [ebp-30h]
          _BOOL1 hasRoad; // [esp+ABh] [ebp-1Dh]
          int direction; // [esp+ACh] [ebp-1Ch]
          int newCostToMove_2; // [esp+B0h] [ebp-18h]
          int subtype; // [esp+B4h] [ebp-14h]
          int newCostToMove; // [esp+B8h] [ebp-10h]
          int v109; // [esp+C4h] [ebp-4h]

          v4 = this;
          minimumPath.field_0 = HIBYTE(z);
          minimumPath.first = 0;
          minimumPath.end = 0;
          minimumPath.capacity = 0;
          v109 = 0;
          minimumCost.Ref = HIBYTE(z);
          minimumCost.First = 0;
          minimumCost.Last = 0;
          minimumCost.All = 0;
          LOBYTE(v109) = 1;
          Vector_Add_Size12_0054CAC0(&minimumPath, 0, (RMG_Point *)&x);
          subtype = 0;
          VectorCopy_0051B3A0((_Vector_ *)&minimumCost, 0, (int)&subtype);
          v5 = &v4->MapItems[x + v4->SizeX * (y + z * v4->SizeY)];
          v5->movementCost &= 0xFFFF0000;               // null out movement cost from starting point
          v5 = (_RMGMapItem_ *)((char *)v5 + 16);       // reference to coordinates of map item
          v5->objects.field_0 = -1;                     // x , previous tile = (-1, -1, -1)
          v5->objects.first = (_RMGObject_ **)-1;       // y
          v5->objects.end = (_RMGObject_ **)-1;         // z
          while ( minimumPath.first && minimumPath.end - minimumPath.first )
          {
            x = minimumPath.end[-1].x;
            y = minimumPath.end[-1].y;
            z = minimumPath.end[-1].z;
            DeleteEntry(&minimumCost, minimumCost.Last - 1);// remove last entry, cheapest
            VectorRemovePoint_0054CCE0(&minimumPath, minimumPath.end - 1);// remove last entry, cheapest
            z_ = z;
            mitems = v4->MapItems;
            v8 = x + v4->SizeX * (y + z * v4->SizeY);
            direction = 8;                              // no road?
            mi = &mitems[v8];                           // current tile
            movementCost = mi->movementCost & 0xFFFF;
            v10 = mi->PackedGroundData2;
            hasRoad = (mi->PackedGroundData & 0x3C000000) != 0;
            if ( (v10 >> 22) & 1 )                      // has object
            {
              v11 = (*mi->objects.first)->PrototypeRef->Prototype;
              objectType = v11->Type;
              itemSet = &MapObjectGlbSettings[v11->Type];
              if ( !itemSet->exitTop && !itemSet->mayBeGuarded )
                direction = 5;                          // try below, from botLeft
              switch ( objectType )
              {
                case MONOLITH_ONE_WAY_ENTRANCE:
                case MONOLITH_ONE_WAY_EXIT:
                  v14 = v11->Subtype;
                  v15 = 0;
                  subtype = v14;
                  while ( 1 )
                  {
                    v16 = v4->monolithsOneWay.first;
                    newCostToMove = v15;
                    if ( !v16 || v15 >= (_DWORD)((char *)v4->monolithsOneWay.end - (char *)v16) >> 2 )
                      break;
                    v17 = v16[v15];
                    if ( v17->PrototypeRef->Prototype->Subtype == subtype )
                    {
                      v18 = &v17->X;
                      v19 = *v18;
                      x_2_ = v19;
                      y_2_ = v18[1];
                      z_2 = v18[2];
                      v20 = movementCost + 50;
                      v21 = &v4->MapItems[v19 + v4->SizeX * (y_2_ + z_2 * v4->SizeY)];
                      v22 = v21->movementCost;
                      if ( (unsigned __int16)v21->movementCost > (unsigned int)(movementCost + 50) )
                      {
                        v23 = z_;
                        v24 = y;
                        v25 = &v21->prevTile;
                        v26 = v22 ^ (unsigned __int16)(v20 ^ v22);
                        v27 = x;
                        v25[1].x = v26;
                        v25->x = v27;
                        v28 = y_2_;
                        newCostToMove_2 = v20;
                        v25->y = v24;
                        a3.y = v28;
                        v29 = 0;
                        v25->z = v23;
                        a3.x = x_2_;
                        a3.z = z_2;
                        if ( minimumPath.first )
                          v30 = minimumPath.end - minimumPath.first;
                        else
                          v30 = 0;
                        while ( 1 )
                        {
                          v31 = (v30 + v29) >> 1;
                          if ( v29 >= v30 )
                            break;
                          if ( v20 >= minimumCost.First[v31] )
                            v30 = (v30 + v29) >> 1;
                          else
                            v29 = v31 + 1;
                        }
                        Vector_Add_Size12_0054CAC0(&minimumPath, &minimumPath.first[v31], &a3);
                        VectorCopy_0051B3A0((_Vector_ *)&minimumCost, (DWORD *)&minimumCost.First[v31], (int)&newCostToMove_2);
                        z_ = z;
                      }
                    }
                    v15 = newCostToMove + 1;
                  }
                  break;
                case MONOLITH_TWO_WAY:
                  subtype_ = v11->Subtype;
                  v33 = 0;
                  subtype = subtype_;
                  while ( 1 )
                  {
                    v34 = v4->monolithsTwoWay.first;
                    newCostToMove = v33;
                    if ( !v34 || v33 >= (_DWORD)((char *)v4->monolithsTwoWay.end - (char *)v34) >> 2 )
                      break;
                    v35 = v34[v33];
                    if ( v35->PrototypeRef->Prototype->Subtype == subtype )
                    {
                      v36 = (RMG_Point *)&v35->X;
                      x_2 = v36->x;
                      x_2_ = x_2;
                      y_2_ = v36->y;
                      z_2 = v36->z;
                      movCost = movementCost + 50;
                      v39 = &v4->MapItems[x_2 + v4->SizeX * (y_2_ + z_2 * v4->SizeY)];
                      v40 = v39->movementCost;
                      if ( (unsigned __int16)v39->movementCost > (unsigned int)(movementCost + 50) )
                      {
                        v88 = z_;
                        v87 = x;
                        v41 = y;
                        v42 = &v39->prevTile;
                        cost_2 = v40 ^ (unsigned __int16)(movCost ^ v40);
                        v44 = x;
                        v42[1].x = cost_2;              // movement cost
                        v42->x = v44;
                        v45 = y_2_;
                        newCostToMove_2 = movCost;
                        v42->y = v41;
                        v86.y = v45;
                        v46 = 0;
                        v42->z = v88;
                        v86.x = x_2_;
                        v86.z = z_2;
                        if ( minimumPath.first )
                          v47 = minimumPath.end - minimumPath.first;
                        else
                          v47 = 0;
                        while ( 1 )
                        {
                          v48 = (v47 + v46) >> 1;
                          if ( v46 >= v47 )
                            break;
                          if ( movCost >= minimumCost.First[v48] )
                            v47 = (v47 + v46) >> 1;
                          else
                            v46 = v48 + 1;
                        }
                        Vector_Add_Size12_0054CAC0(&minimumPath, &minimumPath.first[v48], &v86);
                        VectorCopy_0051B3A0((_Vector_ *)&minimumCost, (DWORD *)&minimumCost.First[v48], (int)&newCostToMove_2);
                        z_ = z;
                      }
                    }
                    v33 = newCostToMove + 1;
                  }
                  break;
                case SUBTERRANEAN_GATE:
                  v49 = x + v4->SizeX * (y + (1 - z) * v4->SizeY);
                  x_2_ = x;
                  v50 = v4->MapItems;
                  y_2_ = y;
                  z_2 = z;
                  v51 = &v50[v49];
                  newCostToMove = movementCost + 1;
                  v52 = v51->movementCost;
                  if ( movementCost + 1 < (unsigned int)(unsigned __int16)v51->movementCost )
                  {
                    v96 = z;
                    v94 = x;
                    v95 = y;
                    v53 = newCostToMove;
                    v54 = &v51->prevTile.x;
                    v93.z = 1 - z;
                    v55 = 0;
                    v56 = v52 ^ (unsigned __int16)(newCostToMove ^ v52);
                    v57 = x;
                    v54[3] = v56;
                    *v54 = v57;
                    newCostToMove_2 = v53;
                    v54[1] = v95;
                    v54[2] = v96;
                    v93.x = x_2_;
                    v93.y = y_2_;
                    if ( minimumPath.first )
                      v58 = minimumPath.end - minimumPath.first;
                    else
                      v58 = 0;
                    while ( 1 )
                    {
                      v59 = (v58 + v55) >> 1;
                      if ( v55 >= v58 )
                        break;
                      if ( v53 >= minimumCost.First[v59] )
                        v58 = (v58 + v55) >> 1;
                      else
                        v55 = v59 + 1;
                    }
                    Vector_Add_Size12_0054CAC0(&minimumPath, &minimumPath.first[v59], &v93);
                    VectorCopy_0051B3A0((_Vector_ *)&minimumCost, (DWORD *)&minimumCost.First[v59], (int)&newCostToMove_2);
                    z_ = z;
                  }
                  break;
                default:
                  break;
              }
            }
            v60 = direction - 1;
            noDirectionAvailable = direction-- == 0;
            if ( !noDirectionAvailable )
            {                                           //
                                                        // 0x69CE10 Order to check nearby tiles
                                                        //  Y\X  -1  0  1
                                                        //      ###########
                                                        //  -1  # 3  2  1 #
                                                        //   0  # 4  *  0 #
                                                        //   1  # 5  6  7 #
                                                        //      ###########


              offset = (POINT *)(8 * v60 + 0x69CE10);
              subtype = 8 * v60 + 0x69CE10;             // variable is reused, here is a POINT*
              do
              {
                xOffset = offset->x;
                yOffset = offset->y;
                pt1.z = z_;
                _x = xOffset + x;
                _y = y + yOffset;
                pt1.x = xOffset + x;
                pt1.y = _y;
                if ( xOffset + x >= 0 && _x < v4->SizeX && _y >= 0 )
                {
                  height = v4->SizeY;
                  if ( _y < height )
                  {
                    mi_offset = &v4->MapItems[_x + v4->SizeX * (_y + z_ * height)];
                    landType = mi_offset->PackedGroundData & 0x3F;
                    if ( landType != WATER_terrain )
                    {
                      v70 = mi_offset->PackedGroundData2;
                      if ( v70 & 0x2000000 )            // blocked access ?
                      {
                        if ( landType != ROCK_terrain )
                        {
                          if ( !((v70 >> 22) & 1)       // has object
                            || ((itSet = &MapObjectGlbSettings[(*mi_offset->objects.first)->PrototypeRef->Prototype->Type],
                                 !itSet->canEnter)
                             || itSet->mayBeGuarded)
                            && (itSet->exitTop || itSet->mayBeGuarded || direction <= 0 || direction >= 4) )
                          {
                            if ( !hasRoad || (costToMove = 2, !(mi_offset->PackedGroundData & 0x3C000000)) )// if road, cost is 2, 10% of no road
                              costToMove = 20;          // base cost
                            if ( direction & 1 )        // diagonal movement
                              costToMove *= 3;          // skewed against diagonal movement to draw nice looking roads
                            v73 = mi_offset->movementCost;
                            prevMovementCost = mi_offset->movementCost;
                            newCostToMove = movementCost + costToMove;
                            if ( movementCost + costToMove < (unsigned int)prevMovementCost )
                            {
                              xStart = x;
                              yStart = y;
                              v76 = newCostToMove;
                              z_1 = z_;
                              v77 = &mi_offset->prevTile;
                              newCostToMove_2 = newCostToMove;
                              newCostToMove_WORD = v73 ^ (unsigned __int16)(newCostToMove ^ v73);
                              y_2 = y;
                              v77[1].x = newCostToMove_WORD;// movement cost
                              v77->x = xStart;
                              zStart = z_1;
                              v77->y = y_2;
                              pt2.y = pt1.y;
                              v81 = 0;
                              v77->z = zStart;
                              pt2.x = pt1.x;
                              pt2.z = pt1.z;
                              if ( minimumPath.first )
                                currentPoint = minimumPath.end - minimumPath.first;
                              else
                                currentPoint = 0;
                              while ( 1 )
                              {
                                v83 = (currentPoint + v81) >> 1;// check if new cost calculated is cheaper than previous
                                if ( v81 >= currentPoint )
                                  break;
                                if ( v76 >= minimumCost.First[v83] )
                                  currentPoint = (currentPoint + v81) >> 1;
                                else
                                  v81 = v83 + 1;
                              }
                              AddVector_Size12(&minimumPath, &minimumPath.first[v83], 1u, &pt2);// update cheapest path
                              AddVector_Size4(&minimumCost, &minimumCost.First[v83], 1, &newCostToMove_2);// update cheapest cost
                              z_ = z;
                            }
                          }
                        }
                      }
                    }
                  }
                }
                v84 = direction;
                offset = (POINT *)(subtype - 8);
                --direction;
                subtype -= 8;
              }
              while ( v84 );
            }
          }
          delete(minimumCost.First);
          minimumCost.First = 0;
          minimumCost.Last = 0;
          minimumCost.All = 0;
          return delete(minimumPath.first);
        }
Вернуться к началу

offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 31 мар 2019, 19:08

RK: "sub_005328A0 writes the road type to a tile during RMG"
Код: Выделить всё
_RMGMapItem_ *__thiscall RMG_WriteRoad_005328A0(PtrToRMGStructOffset *this, POINT *a2, char roadType)
{
  _RMGMapItem_ *result; // eax

  result = &this->miOffset->mapItems[a2->x + a2->y * this->miOffset->sizeX];
  result->PackedGroundData = result->PackedGroundData & ~0x3C000000 | ((roadType & 0xF) << 26);
  return result;
}
Вернуться к началу

offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 31 мар 2019, 19:08

RK: "The format of PackedGroundData is bitfield as follows"
Код: Выделить всё
        struct RMG_GroundTile
        {
           unsigned landType   : 5; // bit 1~4
           unsigned _b6      : 1;
           unsigned landSprite : 7; // bit 7 ~ 13
           unsigned _b14      : 1;
           unsigned riverType   : 3; // bit 15, 16, 17
           unsigned _b18      : 1;
           unsigned riverSprite : 7;// bit 19, 20, 21, 22, 23, 24, 25
           unsigned _b26      : 1;
           unsigned roadType   : 3; // bit 27 28 29
           unsigned _b30      : 3;
        };
Вернуться к началу

offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 31 мар 2019, 19:09

RK: "In PackedGroundData2 you have another bitfield"
Код: Выделить всё
struct RMG_GroundTileData
{
   unsigned roadSprite : 7;
   unsigned _b8 : 8;
   unsigned mirrorLandV : 1;   // bit 16 [0x0000 8000]
   unsigned mirrorLandH : 1;   // bit 17 [0x0001 0000]
   unsigned mirrorRiverV : 1;   // bit 18 [0x0002 0000]
   unsigned mirrorRiverH : 1;   // bit 19 [0x0004 0000]
   unsigned mirrorRoadV : 1;   // bit 20 [0x0008 0000]
   unsigned mirrorRoadH : 1;   // bit 21 [0x0010 0000]
   unsigned shore : 1;         // bit 22 [0x0020 0000]~ unsure, written to h3m at 0x532E42
   unsigned _access : 10; // these seem to be used to determine access of tile on map
};
Вернуться к началу

offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 31 мар 2019, 19:11

RK
Код: Выделить всё
// * +14B0
H3Vector<H3Coordinates> roadTargets;


"Targets (x, y, z) that need to be connected to road network.
char __thiscall RMG_Road_00548530(_RMGStruct_ *this, int x, int y, int z, int roadType) ...
This sub reads these points to x, y, z and creates a road network from this point on."
Вернуться к началу

offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 06 апр 2019, 17:59

RK:

RMG_ToPlaceTowns_00544F40
RMG_ToPlaceMines_00546450
RMG_GenerateObjects_00547850
RMG_SetShoreMapItems_005317E0
RMG_PlaceObstacles_00537E60
RMG_PlaceRoads_00548780
RMG_WatermillRiver_00549D60
RMG_ClearSomePaths_00540AC0
Вернуться к началу

offlineBen80  
Ветеран
Ветеран
 
Сообщения: 694
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 144 раз.

Re: Энциклопедия алгоритмов HoMM 3

Сообщение Ben80 » 06 апр 2019, 18:07

RK:

RMG_MonsterGenerationMinesGuards_00545E80
RMG_MonsterGenerationZoneBlocks_00541630
RMG_MonsterGenerationGates_00542570
RMG_MonsterGenerationMonolithsBlocks_005431D0
RMG_MonsterGenerationShipyards_00541FC0

It seems like process to link 2 zones is (over simplified)
1 - try to link with regular path
2 - try to link with boat
3 - try to link with subterranean gate
4- try to link using monoliths

Изображение

Изображение
Вернуться к началу

Пред.След.

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

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

Сейчас этот форум просматривают: Yandex [bot] и гости: 1

cron