Объявления

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

RMG SOD

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

Re: RMG SOD

Сообщение as239 » 31 янв 2020, 07:24

Я думал что RMG_SetPosition_532370 устанавливает финальные координаты объекта.
Но похоже это не так.
Судя по коду она точно добавляет ссылку на объект в RMG_MapItem.objects.
А вот насчет установки координат самого объекта не уверен.
Я пытался анализировать координаты MapItem в 532473, но там часто бывают просто пустые клетки без объектов, что очень странно:
Код: Выделить всё
AddItemToList(&MapItem->objects.Ref, v18, 1u, &item);

 
Изображение


Также пытался получить финальные координаты расставленных монстров в sub_00535600(_RMGMap_ *RMG_MapItems, _RMGObject_ *NewMonster), после вызова RMG_SetPosition_532370(v2, (int)v3, NewMonster, v48, v81, 0);
 
Изображение

Ставлю хук здесь:
Код: Выделить всё
_PI->WriteLoHook(0x5358F5, test555);


И вижу следующее:
 
Изображение

 
Изображение

Тоже самое и при "c->ebp+8" т.е. это не монстр.
А где же его взять тогда?
Ничего не понимаю.
Вернуться к началу

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

Re: RMG SOD

Сообщение RoseKavalier » 31 янв 2020, 14:15

MapItem->objects is a H3Vector<int> of object ID on the current tile.

(T*)(c->esp+0x50)
There are two parts to this expression, between each set of parenthesis.
(T*) is cast type as you used correctly but (c->esp + 0x50) is a place on the stack, **not the value at that stack address**.

So what's happening is that code is saying that _RMGMonster_ is located on the stack and not allocated (e.g. _RMGMonster_ mon vs _RMGMonster_ * mon = new _RMGMonster_ ).
Instead you should be getting the data here (you can see vtable points to actual _RMGMonster_ here; vtables in homm3 tend to be in the 0x6XXXXX range)

TL;DR; you need the data at c->esp+0x50
*(_RMGMonster_*)(c->esp+0x50)
or
(_RMGMonster_ *)PtrAt(c->esp+0x50)
or
(_RMGMonster_ *)c->local_n(0x50/4); (added to HookContext in H3API's patcher_x86)
Вернуться к началу

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

Re: RMG SOD

Сообщение as239 » 02 фев 2020, 06:52

Thanks!
(_RMGMonster_ *)PtrAt(c->esp+0x50) is works.

But it shows non-existent monsters/objects.
F.e. I have the hive at x 3 y 10 z 0.
 
Изображение


Here is the code for checking, looking at 0x546ACE after first object in the group is created and placed:
Код: Выделить всё
_LHF_(test555){
   RMG_MapItem *item1 = rmg->map.GetMapItem(3,10,0);
   RMG_Object *obj = (RMG_Object*)PtrAt(c->ebp-0xC);
   //RMG_Object *obj = (RMG_Object*)(c->esp + 0x20);
   return EXEC_DEFAULT;
}
.....
_PI->WriteLoHook(0x546ACE, test555);


RMG_MapItem *item1 shows correct information.
But RMG_Object *obj shows somethig strange, how can i get correct object at 0x546ACE?
 
Изображение


Hmm, the X object coordinate at RMG_MapItem *item1, also wrong it should be "3", not "4".
Вернуться к началу

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

Re: RMG SOD

Сообщение RoseKavalier » 02 фев 2020, 23:42

That's the correct RMG_Object.
I'd say it gets shifted later when map tries to patch holes / corrects for zone position or such.

EDIT: quick memory breakpoint shows position is being rewritten at call from 0x5407D3. There could be more such situations.
Вернуться к началу

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

Re: RMG SOD

Сообщение as239 » 09 фев 2020, 09:10

Не могу решить следующие вопросы, был бы очень благодарен любой информации.

1. Удалось полностью избавится от блоков на дорогах.
Как результат - полностью пустые дороги.
Это не самое лучшее решение, нужно чтобы на них были охраны, я научился ограничивать охраны на дороге по value.
Но тут возникает следующая проблема:
 
Изображение


2. Как можно исправить "утонувшие зоны":
 
Изображение

Карта без воды, это была зона игрока, но она взяла и утонула и теперь вместо 8 игроков - 7.

3. Как можно исправить возможность обхода охран между зонами?
 
Изображение
Вернуться к началу

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

Re: RMG SOD

Сообщение as239 » 15 фев 2020, 08:26

Пытаюсь сдвинуть артефакт(x 4, y 10) с дороги на клетку выше x 4, y 9.
 
Изображение

Никак не получается. Если работать через RMGMapItems_00531D80_CantPlace, то исчезает и улик, и артефакт и охрана.
Отладчиком анализировал RMG_SetPosition_532370, вызываемую из RMG_SetTreasureObjectsGroup_546A10, ничего похожего на реальные координаты объектов, там нет.
Да и с типами, там какая-то беда. 53240E, я дизассемблировал так:
Код: Выделить всё
MapItem = &v6->mapItems[v12 + v13 * (v10 + z * v6->mapHeight)];

Но в отладчике видно, что это не MapItem, т.к. id зоны всегда 255 и прочие поля тоже не похожи.

В 0x5407D3 я вижу реальные координаты объектов, но я не понимаю, как и откуда вызывается эта функция, и что точно она делает, кроме добавления объекта в RMG_MapItem->objects.
Мне нужно найти место в коде, где генератор решает на какую координату поставить объект.
В этом примере, он ставит улик на (x 3 y 10), дальше артефакт он может поставить на любую смежную клетку, но он ставит его на (x 4 y 10), вот в это место мне нужно попасть, чтобы проверить если на тайле есть дорога, то ставим объект на другую смежную клетку.

Пытался найти это место, через анализ изменения данных RMG_MapItem, но еще больше запутался.

Ставлю хук здесь 0x54A0FE, перед вызовом RMG_GenerateObjects_00547850:
Код: Выделить всё
_PI->WriteLoHook(0x54A0FE, WriteInVector);//5358F5? 546ACE 0x546C92 5358F5

Запоминаю все тайлы имеющие объекты.
На этом этапе, это могут быть только - города, шахты, ресурсы возле шахт, монолиты, и охраны шахт, монолитов:
_
Код: Выделить всё
LHF_(WriteInVector){

   MapItemsHasObject.clear();
   for (int x = 0; x < MapSize; x++)
   {
      for (int y = 0; y < MapSize; y++)
      {
         RMG_MapItem *xyz = rmg->map.GetMapItem(x, y, 0);
         if (!xyz->tileData.hasObject) continue;
         auto it = std::find(MapItemsHasObject.begin(), MapItemsHasObject.end(), xyz);
         if (it != MapItemsHasObject.end()) continue;
         MapItemsHasObject.push_back(xyz);

      }
   }

   return EXEC_DEFAULT;
}


Ставлю хук 0x547872, практически в самое начало RMG_GenerateObjects_00547850:
Код: Выделить всё
_PI->WriteLoHook(0x547872, GetTreasuryBlock);//0x546C81 0x546AD3 0x546A88 0x546A20 0x546BD6 0x5478CE

Анализирую изменившиеся тайлы так:
Код: Выделить всё
_LHF_(GetTreasuryBlock){

   for (int x = 0; x < MapSize; x++)
   {
      for (int y = 0; y < MapSize; y++)
      {
         RMG_MapItem *xyz = rmg->map.GetMapItem(x, y, 0);
         if (!xyz->tileData.hasObject) continue;
         auto it = std::find(MapItemsHasObject.begin(), MapItemsHasObject.end(), xyz);
         if (it != MapItemsHasObject.end()) continue;

         TreasuryBlock.push_back(xyz);
         MapItemsHasObject.push_back(xyz);

      }
   }

   for (size_t i = 0; i < TreasuryBlock.size(); ++i){
      RMG_MapItem *xyz = TreasuryBlock[i];
      H3Coordinates Coord = GetCoord(xyz);
   }
   TreasuryBlock.clear();
   return EXEC_DEFAULT;
}

Ставлю точку остановы в VS, и в первом же вызове, вижу что TreasuryBlock уже содержит все объекты, расставленные на карте, как такое может быть?
Вернуться к началу

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

Re: RMG SOD

Сообщение as239 » 26 фев 2020, 15:46

Кое-что стало понятнее. Судя по всему:
В самом начале RMG_GenerateObjects_00547850(_RMGStruct_ *RMG_Struct, _RMGGenZone_ *GenZone)
Создается чистая мини карта 16х16.
Код: Выделить всё
RMG_MapItems_Ctor(&a3, 16, 16, 1);

Дальше созданный первый объект ставится в середину этой карты, с учетом его ширины/высоты.
Код: Выделить всё
  middleX = (unsigned int)(RMG_Map->SizeX + Prototype->Width) >> 1;
  middleY = (unsigned int)(RMG_Map->SizeY + Prototype->Height) >> 1;
  AddItemToList(&RMG_Map[1].SizeY, (void **)RMG_Map[2].VMT, 1u, (void **)&item);
  RMG_SetPosition_532370(RMG_Map, value, (_RMGObject_ *)item, middleX, middleY, 0);

Кстати теперь понятно почему здесь z=0.
Потом добавляются остальные объекты в группе.

И затем в недрах RMG_005475C0(RMG_Main2, &a3, GenZone, TotalDensity).
Происходит перенос объектов с карты 16х16, на реальную карту.
Вот с этим мне нужно будет детально разобраться, чтобы наконец-то научиться управлять размещением объектов на карте.

Частично разобранная RMG_MonsterGenerationObjectGuards_00546BD0:
Код: Выделить всё
char __thiscall RMG_MonsterGenerationObjectGuards_00546BD0(_RMGStruct_ *this, _RMGGenZone_ *zone, _RMGMapItems_ *RMGMap, int a4, int minValue, int maxValue)
{
  _RMGMapItem_ *MapItem; // ebx
  _RMGMapItems_ *RMGMapItems2; // esi
  int MapItemsCount; // edi
  int SizeXSizeY; // edx
  _RMGMapItem_ *MapItem2; // ebx
  int MapItem2Count; // edi
  int TreasureGroupValue; // edx
  int TotalTreasuryGroupValue; // ebx
  char result; // al
  int ZoneMonstersStrength; // ecx
  int TotalMonstersStrength; // ecx
  int MonstersStrengthValue; // edx
  signed int v18; // edi
  int v19; // edx
  _RMGObjMonster_ *v20; // eax
  _RMGObjMonster_ *v21; // ebx
  unsigned int i; // edi
  int v23; // eax
  void (__cdecl ***v24)(signed int); // ecx
  _RMGMapItem_ *v25; // edi
  int v26; // esi
  unsigned int v27; // edi
  int v28; // eax
  _RMGMapItem_ *v29; // eax
  _RMGStruct_ *RMG_Main; // [esp+Ch] [ebp-4h]

  RMGMapItems2 = RMGMap;
  RMG_Main = this;
  PtrList_DeleteRange((int)&RMGMap[1].SizeY, RMGMap[1].Levels, RMGMap[2].VMT);
  List_8_DeleteRange(&RMGMapItems2[2].Items, (_DWORD *)RMGMapItems2[2].SizeX, (int *)RMGMapItems2[2].SizeY);
  MapItem = (_RMGMapItem_ *)RMGMap->Items;
  if ( RMGMapItems2->SizeX * RMGMapItems2->SizeY * RMGMapItems2->Levels )
  {
    MapItemsCount = RMGMap->SizeX * RMGMap->SizeY * RMGMap->Levels;
    do
    {
      RMG_RmgCell_Clear(MapItem);
      ++MapItem;
      --MapItemsCount;
    }
    while ( MapItemsCount );
  }
  MapItem2 = (_RMGMapItem_ *)RMGMap->Items;
  SizeXSizeY = RMGMap->SizeX * RMGMap->SizeY;
  LOBYTE(RMGMap[3].VMT) = 0;
  LOBYTE(RMGMap[4].VMT) = 0;
  if ( SizeXSizeY )
  {
    MapItem2Count = SizeXSizeY;
    do
    {
      RMG_RmgCell_SetupLand(MapItem2, 0, 0, 0, 0);
      ++MapItem2;
      --MapItem2Count;
    }
    while ( MapItem2Count );
  }
  if ( maxValue > minValue )
    TreasureGroupValue = minValue + rand() % (maxValue - minValue);
  else
    TreasureGroupValue = maxValue;
  TotalTreasuryGroupValue = RMG_SetTreasureObjectsGroup(RMG_Main, zone, RMGMap, a4, TreasureGroupValue);
  if ( !TotalTreasuryGroupValue )
    return 0;
  ZoneMonstersStrength = zone->ZoneInfo->MonstersStrength;
  if ( !ZoneMonstersStrength )
    goto LABEL_42;
  TotalMonstersStrength = ZoneMonstersStrength + RMG_Main->MonstersStrength - 3;
  if ( TotalMonstersStrength <= 5 )
  {
    if ( TotalMonstersStrength < 0 )
      TotalMonstersStrength = 0;
  }
  else
  {
    TotalMonstersStrength = 5;
  }
  MonstersStrengthValue = dword_00682440[TotalMonstersStrength];
  v18 = 0;
  if ( TotalTreasuryGroupValue > MonstersStrengthValue )
    v18 = dword_00682470[TotalMonstersStrength] * (TotalTreasuryGroupValue - MonstersStrengthValue) / 4;
  v19 = dword_00682458[TotalMonstersStrength];
  if ( TotalTreasuryGroupValue > v19 )
    v18 += dword_00682488[TotalMonstersStrength] * (TotalTreasuryGroupValue - v19) / 4;
  if ( v18 >= 2000
    && v18 > 0
    && (v20 = RMG_00541010_MonsterGeneration(RMG_Main, v18, (int)zone), (v21 = v20) != 0)
    && !sub_00535600((int)RMGMap, (int)v20) )
  {
    for ( i = 0; ; ++i )
    {
      v23 = RMGMap[1].Levels;
      if ( !v23 || i >= (RMGMap[2].VMT - v23) >> 2 )
        break;
      (*(void (**)(void))(**(_DWORD **)(RMGMap[1].Levels + 4 * i) + 4))();
      v24 = *(void (__cdecl ****)(signed int))(RMGMap[1].Levels + 4 * i);
      if ( v24 )
        (**v24)(1);
    }
    sub_0054C610((_Vector_ *)&RMGMap[1].SizeY);
    sub_0054C650(&RMGMap[2].Items);
    sub_00531610(RMGMap);
    LOBYTE(RMGMap[3].VMT) = 0;
    LOBYTE(RMGMap[4].VMT) = 0;
    v25 = Get_RMGMapItem_00546E80(RMGMap, 0, 0);
    if ( RMGMap->SizeX * RMGMap->SizeY )
    {
      v26 = RMGMap->SizeX * RMGMap->SizeY;
      do
      {
        RMG_RmgCell_SetupLand(v25, 0, 0, 0, 0);
        ++v25;
        --v26;
      }
      while ( v26 );
    }
    (*(void (__thiscall **)(_RMGObjMonster_ *, signed int))v21->VMT)(v21, 1);
    result = 0;
  }
  else
  {
LABEL_42:
    sub_005363D0(RMGMap);
    LOBYTE(RMGMap[4].VMT) = 1;
    v27 = 0;
    while ( 1 )
    {
      v28 = RMGMap[2].SizeX;
      if ( !v28 || v27 >= (RMGMap[2].SizeY - v28) >> 3 )
        break;
      v29 = Get_RMGMapItem_00546E80(RMGMap, *(_DWORD *)(v28 + 8 * v27), *(_DWORD *)(v28 + 8 * v27 + 4));
      ++v27;
      v29->PackedGroundData2 |= 0x800000u;
    }
    result = 1;
  }
  return result;
}


И еще частично разобранная RMG_SetTreasureObjectsGroup:
Код: Выделить всё
int __thiscall RMG_SetTreasureObjectsGroup(_RMGStruct_ *this, _RMGGenZone_ *zone, _RMGMapItems_ *RMG_Map, char a3, int value)
{
  int value2; // ebx
  _RMGObject_ *FirstGeneratedObject; // eax
  _BOOL1 v7; // sf
  unsigned __int8 v8; // of
  _RMGObjectPrototypeRef_ *PrototypeRef; // ecx
  int AccumulatedValue2; // esi
  _RMGObjectPrototype_ *Prototype; // ecx
  int middleX; // esi
  int middleY; // edi
  int BalanceValue; // ecx
  int v16; // ebx
  int v17; // edi
  _RMGObject_ *GeneratedObject; // eax
  _RMGObject_ *v19; // esi
  int v20; // [esp+18h] [ebp-14h]
  _RMGStruct_ *RMG_Main; // [esp+1Ch] [ebp-10h]
  int item; // [esp+20h] [ebp-Ch]
  int AccumulatedValue; // [esp+24h] [ebp-8h]
  int v24; // [esp+28h] [ebp-4h]

  value2 = value;
  AccumulatedValue = 0;
  v24 = 0;
  RMG_Main = this;
  while ( 1 )
  {
    FirstGeneratedObject = RMG_00546680_GenerateObject(
                             RMG_Main,
                             zone,
                             value / 4,
                             value,
                             &AccumulatedValue,
                             1,
                             1,
                             a3,
                             -1,
                             -1,
                             -1);
    if ( FirstGeneratedObject )
      break;
    v8 = __OFSUB__(v24 + 1, 3);
    v7 = v24++ - 2 < 0;
    if ( !((unsigned __int8)v7 ^ v8) )
      return 0;
  }
  PrototypeRef = (_RMGObjectPrototypeRef_ *)FirstGeneratedObject->PrototypeRef;
  item = (int)FirstGeneratedObject;
  Prototype = PrototypeRef->Prototype;
  middleX = (unsigned int)(RMG_Map->SizeX + Prototype->Width) >> 1;
  middleY = (unsigned int)(RMG_Map->SizeY + Prototype->Height) >> 1;
  AddItemToList(&RMG_Map[1].SizeY, (void **)RMG_Map[2].VMT, 1u, (void **)&item);
  RMG_SetPosition_532370(RMG_Map, value, (_RMGObject_ *)item, middleX, middleY, 0);
  AccumulatedValue2 = AccumulatedValue;
  item = AccumulatedValue;
  if ( AccumulatedValue < value )
  {
    while ( 2 )
    {
      BalanceValue = value2 - AccumulatedValue2;
      if ( value2 - AccumulatedValue2 >= 1500 || BalanceValue >= AccumulatedValue2 / 2 )
      {
        v24 = 0;
        v17 = 5 * BalanceValue / 4;
        v16 = BalanceValue / 4;
        while ( 1 )
        {
          v20 = 0;
          while ( 1 )
          {
            GeneratedObject = RMG_00546680_GenerateObject(
                                RMG_Main,
                                zone,
                                v16,
                                v17,
                                &AccumulatedValue,
                                0,
                                1,
                                a3,
                                -1,
                                -1,
                                -1);
            v19 = GeneratedObject;
            if ( GeneratedObject )
              break;
            v8 = __OFSUB__(v20 + 1, 3);
            v7 = v20++ - 2 < 0;
            if ( !((unsigned __int8)v7 ^ v8) )
              goto LABEL_20;
          }
          if ( sub_00535E60(RMG_Map, GeneratedObject) )
            break;
          (*(void (__thiscall **)(_RMGObject_ *))(v19->VMT + 4))(v19);
          (*(void (__thiscall **)(_RMGObject_ *, signed int))v19->VMT)(v19, 1);
          v8 = __OFSUB__(v24 + 1, 3);
          v7 = v24++ - 2 < 0;
          if ( !((unsigned __int8)v7 ^ v8) )
            goto LABEL_20;
        }
        if ( v19 )
        {
          item += AccumulatedValue;
          if ( item < value )
          {
            value2 = value;
            AccumulatedValue2 = item;
            continue;
          }
        }
LABEL_20:
        AccumulatedValue2 = item;
      }
      break;
    }
  }
  sub_005362E0(RMG_Map);
  return AccumulatedValue2;
}
Вернуться к началу

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

Re: RMG SOD

Сообщение as239 » 28 фев 2020, 17:03

Может кто-нибудь подсказать типы параметров, этой функции:
Код: Выделить всё
unsigned int __thiscall sub_0054D4E0(int this, int a2, unsigned int a3, int a4)
Вернуться к началу

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

Re: RMG SOD

Сообщение as239 » 03 мар 2020, 16:57

А как можно отличить горизонтальный тайл дороги от вертикального?
Единственное отличие, которое я вижу это значение "RoadSprite", но не уверен что его можно использовать.
Вернуться к началу

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

Re: RMG SOD

Сообщение as239 » 10 апр 2020, 09:29

Есть такой код 0х547197:
Код: Выделить всё
  while ( 1 )
  {
    v9 = RMGMap2[1].Levels;
    if ( !v9 || v8 >= (RMGMap2[2].VMT - v9) >> 2 )
      break;
    v10 = *(_RMGObject_ **)(v9 + 4 * v8);
    v11 = v10->Z;
    if ( RMGMapItems_00531D80_CantPlace((_RMGMapItems_ *)&RMGStruct2->MapItemsVMT,(_RMGObjectPrototypeRef_ *)v10->PrototypeRef,x + v10->X,y + v10->Y,z,ZoneID,1) )
    {
      return 0;
    }
    RMGMap2 = RMGMap;
    ++v8;
  }

Здесь в цикле обходятся все объекты из TreasuryBlok MinMap и проверяется возможность размещения их на карте.
Вопрос - как обратиться к списку/вектору этих объектов, через смещение к RMGMap2?
Вот как выглядит расположение данных:
 
Изображение


Мне сейчас пришлось сделать хук и запоминать значение объекта v10 в свой вектор.
Хотелось бы сделать это проще.
Вернуться к началу

Пред.След.

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

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

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