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


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

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

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineas239  
имя: Анатолий
Ветеран
Ветеран
 
Сообщения: 527
Зарегистрирован: 29 дек 2018, 14:17
Пол: Мужчина
Поблагодарили: 38 раз.

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

Сообщение as239 » 31 мар 2019, 18:15

Баги при генерации дорог на JC однозначно есть и они просто убивают игровой процесс.
В HoTA как раз это все исправлено.
Вот еще пример:
Изображение
По схеме шаблона деревня цитадели должна соединяться с центральным городом инферно зоны.
А она соединилась с деревней друго респа.
Что категорически исключается схемой шаблона.
А она очень простая - 5 зон, 4 игровых и 1 центр в пустыне.
В каждой игровой зоне 3-и города, один центральный с фортом и 2 деревни.
Игровые зоны должны соединяться между собой только через центральную пустынную зону.

Такое впечатление что алгоритм RMG путается и начинает вести дорогу от деревни не к центральному городу респа, а к такому же городу но на другом респе или центре.
Последний раз редактировалось as239 31 мар 2019, 18:47, всего редактировалось 1 раз.
Вернуться к началу

offlineRoseKavalier  
Мастер
Мастер
 
Сообщения: 331
Зарегистрирован: 23 сен 2017, 17:00
Пол: Не указан
Поблагодарили: 234 раз.

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

Сообщение RoseKavalier » 31 мар 2019, 18:44

@as239
There is no other code to write roads that I've seen. From point A to point B, it is also not described in Smedstad document in other ways than "connect to network".
It could potentially be improved by reworking cost algorithm to factor in different zones or to add checks for zone in RMG_Road_00548530.

@Ben80
I agree that the thought of 2->3 should work, however here code does 2 * 3 = 6 [or 20 * 3 = 60]
6 > 2 + 2 [60 > 20 + 20]
Изображение

Actually understood why, this is needed to draw roads aesthetically, lower weight gives things like this:
Изображение

Most of disassembly done for RMG_CalculateMovementCost_00547D70
Код: Выделить всё
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);
}
Последний раз редактировалось RoseKavalier 31 мар 2019, 18:52, всего редактировалось 1 раз.
Вернуться к началу

offlineBen80  
имя: Сергей
Эксперт
Эксперт
 
Сообщения: 1318
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 336 раз.

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

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

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

offlineBen80  
имя: Сергей
Эксперт
Эксперт
 
Сообщения: 1318
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 336 раз.

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

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

RoseKavalier писал(а):

Most of disassembly done for RMG_CalculateMovementCost_00547D70


Thank you very much !
Вернуться к началу

offlineas239  
имя: Анатолий
Ветеран
Ветеран
 
Сообщения: 527
Зарегистрирован: 29 дек 2018, 14:17
Пол: Мужчина
Поблагодарили: 38 раз.

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

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

Цитата:
or to add checks for zone in RMG_Road_00548530

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

offlineRoseKavalier  
Мастер
Мастер
 
Сообщения: 331
Зарегистрирован: 23 сен 2017, 17:00
Пол: Не указан
Поблагодарили: 234 раз.

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

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

@Ben80
Probably a lot of global variables and a lot of hooks or otherwise some function rewrites, no idea.

@as239
I have no idea yet, I am just thinking out loud here)
In _RMGMapitem_ there is a field that references zone (+20h) but I do not know what it is exactly.
Вернуться к началу

offlineBen80  
имя: Сергей
Эксперт
Эксперт
 
Сообщения: 1318
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 336 раз.

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

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

RoseKavalier писал(а):

In _RMGMapitem_ there is a field that references zone (+20h) but I do not know what it is exactly.


zoneId = RMGMapItem->zone << 8 >> 24
Вернуться к началу

offlineRoseKavalier  
Мастер
Мастер
 
Сообщения: 331
Зарегистрирован: 23 сен 2017, 17:00
Пол: Не указан
Поблагодарили: 234 раз.

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

Сообщение RoseKavalier » 31 мар 2019, 20:16

I wrote a very quick passability mask check function, it only checks if an object is present.
Obviously too primitive, this doesn't account for terrain, monsters, yellow tiles, etc., but I found it interesting that some progress was made on understanding RMG structures.
 
Изображение
Вернуться к началу

offlineBen80  
имя: Сергей
Эксперт
Эксперт
 
Сообщения: 1318
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 336 раз.

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

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

RoseKavalier писал(а):

I wrote a very quick passability mask check function, it only checks if an object is present.
Obviously too primitive, this doesn't account for terrain, monsters, yellow tiles, etc.,


Accounting for terrains, monsters, yellow tiles and so on are different for different specific tasks in which passability map is used. So, your passabilty map is sufficient.
Вернуться к началу

offlineBen80  
имя: Сергей
Эксперт
Эксперт
 
Сообщения: 1318
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 336 раз.

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

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

RoseKavalier писал(а):

I found it interesting that some progress was made on understanding RMG structures.


Is there way to set preferable link between zones - by shipyard, or by teleport, for example ? Or set preferable level for zone - UG or G, for example.
Вернуться к началу

Пред.След.

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

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

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