Страница 7 из 19

Re: Программа для разведки

СообщениеДобавлено: 14 май 2014, 15:06
AlexSpl
Обновлений в ближайшее время не будет.

Re: Программа для разведки

СообщениеДобавлено: 12 июн 2014, 11:24
AlexSpl
Исходники LMOracle.SkillTreeAPI.dll

 SkillTreeAPI.h
Код: Выделить всё
#include <windows.h>

#define DllExport extern "C" __declspec(dllexport)

struct THero
{
   BYTE Class;
   BYTE Level;
   BYTE TreeNumber;
   BYTE LastWisdom;
   BYTE LastMagic;
   BYTE Attack;
   BYTE Defense;
   BYTE SpellPower;
   BYTE Knowledge;
   BYTE SkillCount;
   BYTE Skill[28];
};

struct TWeights
{
   BYTE PW[4];
   BYTE PW10[4];
   BYTE SW[28];
   bool SR[28];
};

struct TSecondary
{
   BYTE Left;
   BYTE Right;
};

struct TSkillOffer
{
   BYTE Primary;
   TSecondary Secondary;
};

struct TSkillExcept
{
   BYTE Last_Wisdom;
   BYTE Last_Magic;
   BYTE Delta_Wisdom;
   BYTE Delta_Magic;
};

enum {ATTACK, DEFENSE, SPELL_POWER, KNOWLEDGE};

enum
{
   PATHFINDING, ARCHERY, LOGISTICS, SCOUTING, DIPLOMACY, NAVIGATION, LEADERSHIP,
   WISDOM, MYSTICISM, LUCK, BALLISTICS, EAGLE_EYE, NECROMANCY, ESTATES,
   FIRE_MAGIC, AIR_MAGIC, WATER_MAGIC, EARTH_MAGIC, SCHOLAR, TACTICS, ARTILLERY,
   LEARNING, OFFENSE, ARMORER, INTELLIGENCE, SORCERY, RESISTANCE, FIRST_AID, NONE
};

enum {BASIC = 1, ADVANCED, EXPERT};

const DWORD HAddr[4] = {0x0069CC88, 0x006994E8, 0x00699538, 0x006388D8};
const DWORD WAddr[4] = {0x0067EFDC, 0x0067DCEC, 0x0067DCEC, 0x0061C194};
const DWORD BB_CFAddr[3] = {0x0069AEEC, 0x0069774C, 0x0069774C};
const DWORD BB_SRAddr[3] = {0x0067AF90, 0x00679CA0, 0x00679CA0};

DllExport DWORD GetProcessID(const wchar_t *Title);
DllExport DWORD GetHStartPoint(const HANDLE hProcess, const BYTE Version);
DllExport int RAMHero(const HANDLE hProcess, const DWORD HStartPoint, const BYTE ID, void *HeroRecord);
DllExport int RAMWeights(const HANDLE hProcess, const DWORD HStartPoint, const BYTE Version, const BYTE Class, void *WeightsRecord);
DllExport int LevelUp(void *HeroRecord, const void *WeightsRecord, void *SkillOfferRecord, const BYTE Option);
DllExport bool isStandardWeights(const HANDLE hProcess, const DWORD HStartPoint, const BYTE Version, const BYTE ID);
DllExport int RAMBBWeights(const HANDLE hProcess, const DWORD HStartPoint, const BYTE Version, const BYTE Class, void *WeightsRecord);


 SkillTreeAPI.cpp
Код: Выделить всё
#include "SkillTreeAPI.h"

DWORD GetProcessID(const wchar_t *Title)
{
   HWND HWindow = FindWindow(NULL, Title);
   DWORD PID = 0;
   GetWindowThreadProcessId(HWindow, &PID);
   return PID;
}

DWORD GetHStartPoint(const HANDLE hProcess, const BYTE Version)
{
   DWORD HStartPoint = 0;
   if ( Version < 4 )
   {
      DWORD BR;
      ReadProcessMemory(hProcess, LPCVOID(HAddr[Version]), &HStartPoint, 4, &BR);
      HStartPoint += ( Version == 3 ) ? 0x21608 : 0x21620;
   }
   return HStartPoint;
}

int RAMHero(const HANDLE hProcess, const DWORD HStartPoint, const BYTE ID, void *HeroRecord)
{
   if ( ID < 156 )
   {
      DWORD BR;
      DWORD BAddr = HStartPoint + ID * 0x492;

      ReadProcessMemory(hProcess, LPCVOID(BAddr + 0x30), (BYTE *)HeroRecord, 1, &BR);
      ReadProcessMemory(hProcess, LPCVOID(BAddr + 0x55), (BYTE *)HeroRecord + 1, 1, &BR);
      
      ReadProcessMemory(hProcess, LPCVOID(BAddr + 0x8F), (BYTE *)HeroRecord + 2, 2, &BR);
      ReadProcessMemory(hProcess, LPCVOID(BAddr + 0x3F), (BYTE *)HeroRecord + 4, 1, &BR);

      ReadProcessMemory(hProcess, LPCVOID(BAddr + 0x476), (BYTE *)HeroRecord + 5, 4, &BR);
            
      ReadProcessMemory(hProcess, LPCVOID(BAddr + 0x101), (BYTE *)HeroRecord + 9, 1, &BR);
      ReadProcessMemory(hProcess, LPCVOID(BAddr + 0xC9), (BYTE *)HeroRecord + 10, 28, &BR);
   }   
   return 0;
}

bool isStandardWeights(const HANDLE hProcess, const DWORD HStartPoint, const BYTE Version, const BYTE ID)
{
   bool StandardWeights = true;
   if ( Version < 3 )
   {
      DWORD BR;
      BYTE CF, SF, HF;

      ReadProcessMemory(hProcess, LPCVOID(BB_CFAddr[Version]), &CF, 1, &BR);
      ReadProcessMemory(hProcess, LPCVOID(HStartPoint - 0x21C4), &SF, 1, &BR);
      ReadProcessMemory(hProcess, LPCVOID(HStartPoint + ID * 0x492 + 0x1A), &HF, 1, &BR);

      StandardWeights = !( (CF) && (SF == 0x0E) && (HF == 0x2D) );
   }
   return StandardWeights;
}

int RAMBBWeights(const HANDLE hProcess, const DWORD HStartPoint, const BYTE Version, const BYTE Class, void *WeightsRecord)
{
   if ( (Version < 3) && (Class < 18) )
   {
      DWORD BR;
      DWORD WeightsAddr;
      
      ReadProcessMemory(hProcess, LPCVOID(WAddr[Version]), &WeightsAddr, 4, &BR);
      ReadProcessMemory(hProcess, LPCVOID(WeightsAddr + 0x310), (BYTE *)WeightsRecord, 8, &BR);
      ReadProcessMemory(hProcess, LPCVOID(WeightsAddr + (Class << 6) + 0x18), (BYTE *)WeightsRecord + 8, 28, &BR);
      ReadProcessMemory(hProcess, LPCVOID(BB_SRAddr[Version]), (bool *)WeightsRecord + 36, 28, &BR);
   }
   return 0;
}

int RAMWeights(const HANDLE hProcess, const DWORD HStartPoint, const BYTE Version, const BYTE Class, void *WeightsRecord)
{
   if ( (Version < 4) && (Class < 18) )
   {
      DWORD BR;
      DWORD WeightsAddr;
      
      ReadProcessMemory(hProcess, LPCVOID(WAddr[Version]), &WeightsAddr, 4, &BR);
      ReadProcessMemory(hProcess, LPCVOID(WeightsAddr + (Class << 6) + 0x10), (BYTE *)WeightsRecord, 36, &BR);
      ReadProcessMemory(hProcess, LPCVOID(HStartPoint + 0x2D038), (bool *)WeightsRecord + 36, 28, &BR);
   }
   return 0;
}

int Rand(const DWORD Seed, DWORD &R)
{
   R =  0x343FD * Seed + 0x269EC3;
   return ( (R >> 0x10) & 0x7FFF );
}

BYTE GetSkillOffer(const THero *Hero, const TWeights *Weights, const BYTE Level, const TSkillExcept *SkillExcept,
   const BYTE MinLevel, const BYTE MaxLevel, const BYTE LeftSkill, DWORD &R)
{
   if ( MinLevel >= MaxLevel ) return NONE;
         
   // Wisdom
   if ( ((*SkillExcept).Last_Wisdom + (*SkillExcept).Delta_Wisdom <= Level) &&
       ((*Hero).Skill[WISDOM] >= MinLevel) && ((*Hero).Skill[WISDOM] < MaxLevel) &&
       (!(*Weights).SR[WISDOM]) && (LeftSkill != WISDOM) ) return WISDOM;

   int SUM_W = 0;
   int Q;
   
   // Magic
   if ( ((*SkillExcept).Last_Magic + (*SkillExcept).Delta_Magic <= Level) &&
      (LeftSkill != FIRE_MAGIC) && (LeftSkill != AIR_MAGIC) && (LeftSkill != WATER_MAGIC) && (LeftSkill != EARTH_MAGIC) )
   {
      for (int i = FIRE_MAGIC; i <= EARTH_MAGIC; ++i)
         if ( ((*Hero).Skill[i] >= MinLevel) && ((*Hero).Skill[i] < MaxLevel) && (!(*Weights).SR[i]) )
            (*Hero).Skill[i] ? ++SUM_W : SUM_W += (*Weights).SW[i];
      
      if ( SUM_W )
      {
         if ( SUM_W > 1 )
         {
            Q = Rand(R, R);
            ++(Q %= SUM_W);
         } else Q = 1;

         SUM_W = Q;
         for (int i = FIRE_MAGIC; i <= EARTH_MAGIC; ++i)
         {
            if ( ((*Hero).Skill[i] >= MinLevel) && ((*Hero).Skill[i] < MaxLevel) && (!(*Weights).SR[i]) )
                (*Hero).Skill[i]  ? --SUM_W : SUM_W -= (*Weights).SW[i];

            if ( SUM_W <= 0 ) return (BYTE)i;
         }
      }
   }

   // Other Skills
   SUM_W = 0;
   for (int i = PATHFINDING; i < NONE; ++i)
      if ( ((*Hero).Skill[i] >= MinLevel) && ((*Hero).Skill[i] < MaxLevel) && (i != LeftSkill) )
         if ( (!(*Weights).SR[i]) && ((*Weights).SW[i]) ) SUM_W += (*Weights).SW[i]; else
            if ( (*Hero).Skill[i] > 0 ) ++SUM_W;
   
   if ( SUM_W )
   {
      if ( SUM_W > 1 )
      {
         Q = Rand(R, R);
         ++(Q %= SUM_W);
      } else Q = 1;

      SUM_W = Q;
      for (int i = PATHFINDING; i < NONE; ++i)
      {
         if ( ((*Hero).Skill[i] >= MinLevel) && ((*Hero).Skill[i] < MaxLevel) && (i != LeftSkill) )
            if ( (!(*Weights).SR[i]) && ((*Weights).SW[i]) ) SUM_W -= (*Weights).SW[i]; else
               if ( (*Hero).Skill[i] > 0 ) --SUM_W;
            
         if ( SUM_W <= 0 ) return (BYTE)i;
      }
   }

   return NONE;
}

int LevelUp(void *HeroRecord, const void *WeightsRecord, void *SkillOfferRecord, const BYTE Option)
{
   THero *Hero;
   TWeights *Weights;
   TSkillOffer *SkillOffer;

   Hero = (THero *)HeroRecord;
   Weights = (TWeights *)WeightsRecord;
   SkillOffer = (TSkillOffer *)SkillOfferRecord;
   
   BYTE Level = (*Hero).Level;
   BYTE Slot = Option & 3;

   Slot ? Level = ++(*Hero).Level : ++Level;
         
   DWORD R = 0;
   int Q = Rand(0x343FD * Level + 0x26497 * (*Hero).TreeNumber + 0x259DF, R);
   ++(Q %= 100);
   
   // BB Campaign
   if ( Option & 4 )
   {
      int SUM = (Level > 9) ? (*Weights).PW10[ATTACK] + (*Weights).PW10[DEFENSE] : (*Weights).PW[ATTACK] + (*Weights).PW[DEFENSE];
      if ( SUM > 1 )
      {
         Q = Rand(R, R);
         ++(Q %= SUM);
      } else Q = SUM;
   }
   
   // Primary Skills
   int Primary_ID = 0;
   int W;
   do
   {
      W = ( Level > 9 ) ? (*Weights).PW10[Primary_ID] : (*Weights).PW[Primary_ID];
      if ( Q <= W ) break;
      Q -= W;
      ++Primary_ID;
   }
   while ( true );
      
   (*SkillOffer).Primary = Primary_ID;
   if ( Slot ) ++*((BYTE *)HeroRecord + 5 + Primary_ID);

   // Secondary Skills
   TSkillExcept SkillExcept = { (*Hero).LastWisdom, (*Hero).LastMagic, 6, 4 };
   BYTE MinLevel = (*Hero).SkillCount > 7;
      
   if ( ((*Hero).Class & 0x11) == 1 ) SkillExcept.Delta_Wisdom = SkillExcept.Delta_Magic = 3;
   
   BYTE LeftSkill = GetSkillOffer(Hero, Weights, Level, &SkillExcept, BASIC, EXPERT, NONE, R);
   if ( LeftSkill == NONE )
      LeftSkill = GetSkillOffer(Hero, Weights, Level, &SkillExcept, MinLevel, EXPERT, NONE, R);

   if ( LeftSkill == WISDOM ) SkillExcept.Last_Wisdom = Level;
   if ( (LeftSkill >= FIRE_MAGIC) && (LeftSkill <= EARTH_MAGIC) ) SkillExcept.Last_Magic = Level;

   BYTE RightSkill = GetSkillOffer(Hero, Weights, Level, &SkillExcept, MinLevel, BASIC, LeftSkill, R);
   if ( RightSkill == NONE )
      RightSkill = GetSkillOffer(Hero, Weights, Level, &SkillExcept, MinLevel, EXPERT, LeftSkill, R);

   if ( RightSkill == WISDOM ) SkillExcept.Last_Wisdom = Level;
   if ( (RightSkill >= FIRE_MAGIC) && (RightSkill <= EARTH_MAGIC) ) SkillExcept.Last_Magic = Level;
   
   (*SkillOffer).Secondary.Left = LeftSkill;
   (*SkillOffer).Secondary.Right = RightSkill;
      
   if ( Slot )
   {
      (*Hero).LastWisdom = SkillExcept.Last_Wisdom;
      (*Hero).LastMagic = SkillExcept.Last_Magic;
   } else return 0;

   BYTE Skill_ID = ( Slot == 1 ) ? LeftSkill : RightSkill;

   if ( Skill_ID < NONE )
   {
      if ( !(*Hero).Skill[Skill_ID] ) ++(*Hero).SkillCount;
      ++(*Hero).Skill[Skill_ID];
   }
   
   return 0;
}


А вот так можно потестить получившуюся DLL-ку:

 Test.h
Код: Выделить всё
#include <iostream>
#include <windows.h>

using namespace std;

struct THero
{
   BYTE Class;
   BYTE Level;
   BYTE TreeNumber;
   BYTE LastWisdom;
   BYTE LastMagic;
   BYTE Attack;
   BYTE Defense;
   BYTE SpellPower;
   BYTE Knowledge;
   BYTE SkillCount;
   BYTE Skill[28];
};

struct TWeights
{
   BYTE PW[4];
   BYTE PW10[4];
   BYTE SW[28];
   bool SR[28];
};

struct TSecondary
{
   BYTE Left;
   BYTE Right;
};

struct TSkillOffer
{
   BYTE Primary;
   TSecondary Secondary;
};

enum {ATTACK, DEFENSE, SPELL_POWER, KNOWLEDGE};

enum
{
   PATHFINDING, ARCHERY, LOGISTICS, SCOUTING, DIPLOMACY, NAVIGATION, LEADERSHIP,
   WISDOM, MYSTICISM, LUCK, BALLISTICS, EAGLE_EYE, NECROMANCY, ESTATES,
   FIRE_MAGIC, AIR_MAGIC, WATER_MAGIC, EARTH_MAGIC, SCHOLAR, TACTICS, ARTILLERY,
   LEARNING, OFFENSE, ARMORER, INTELLIGENCE, SORCERY, RESISTANCE, FIRST_AID, NONE
};

const char *PrimSkillName[4] = {"ATTACK", "DEFENSE", "SPELL_POWER", "KNOWLEDGE"};

const char *SecSkillName[29] =
{
   "PATHFINDING", "ARCHERY", "LOGISTICS", "SCOUTING", "DIPLOMACY", "NAVIGATION", "LEADERSHIP",
   "WISDOM", "MYSTICISM", "LUCK", "BALLISTICS", "EAGLE_EYE", "NECROMANCY", "ESTATES",
   "FIRE_MAGIC", "AIR_MAGIC", "WATER_MAGIC", "EARTH_MAGIC", "SCHOLAR", "TACTICS", "ARTILLERY",
   "LEARNING", "OFFENSE", "ARMORER", "INTELLIGENCE", "SORCERY", "RESISTANCE", "FIRST_AID", "NONE"
};


 Test.cpp
Код: Выделить всё
#include "Test.h"

int main()
{
   // Объявляем указатели на импортируемые функции.
   DWORD (*GetProcessID) (const wchar_t *Title);
   DWORD (*GetHStartPoint) (const HANDLE hProcess, const BYTE Version);
   int (*RAMHero) (const HANDLE hProcess, const DWORD HStartPoint, const BYTE ID, void *HeroRecord);
   int (*RAMWeights) (const HANDLE hProcess, const DWORD HStartPoint, const BYTE Version, const BYTE Class, void *WeightsRecord);
   int (*LevelUp) (void *HeroRecord, const void *WeightsRecord, void *SkillOfferRecord, const BYTE Option);
   
   // Специальные функции для кампании "Рождение варвара" ("Birth of a Barbarian").
   bool (*isStandardWeights) (const HANDLE hProcess, const DWORD HStartPoint, const BYTE Version, const BYTE ID);
   int (*RAMBBWeights) (const HANDLE hProcess, const DWORD HStartPoint, const BYTE Version, const BYTE Class, void *WeightsRecord);

   // Загружаем библиотеку и получаем адреса функций.
   HINSTANCE hDLL = LoadLibrary(L"LMOracle.SkillTreeAPI.dll");
   if ( !hDLL ) return -1;

   GetProcessID = (DWORD (*) (const wchar_t *)) GetProcAddress(hDLL, "GetProcessID");
   GetHStartPoint = (DWORD (*) (const HANDLE, const BYTE)) GetProcAddress(hDLL, "GetHStartPoint");
   RAMHero = (int (*) (const HANDLE, const DWORD, const BYTE, void *)) GetProcAddress(hDLL, "RAMHero");
   RAMWeights = (int (*) (const HANDLE, const DWORD, const BYTE, const BYTE, void *)) GetProcAddress(hDLL, "RAMWeights");
   LevelUp = (int (*) (void *, const void *, void *, const BYTE)) GetProcAddress(hDLL, "LevelUp");
   isStandardWeights = (bool (*) (const HANDLE, const DWORD, const BYTE, const BYTE)) GetProcAddress(hDLL, "isStandardWeights");
   RAMBBWeights = (int (*) (const HANDLE, const DWORD, const BYTE, const BYTE, void *)) GetProcAddress(hDLL, "RAMBBWeights");

   // Получаем PID и открываем процесс для чтения.
   DWORD ProcessID = GetProcessID(L"Heroes of Might and Magic III");
   HANDLE hProcess = OpenProcess(PROCESS_VM_READ, false, ProcessID);

   // Определяем адрес в памяти процесса, по которому хранятся структуры героев: GetHStartPoint(<хендл процесса>, <версия игры>), где
   // <версия игры> = 0 для 4.0 RU,
   // <версия игры> = 1 для 4.0 EN,
   // <версия игры> = 2 для 3.2 EN,
   // <версия игры> = 3 для Heroes Chronicles 1.0 RU.
   DWORD HStartPoint = GetHStartPoint(hProcess, 2);
   
   // Объявляем экземпляры структур THero, TWeights и TSkillOffer.
   THero Hero;
   TWeights Weights;
   TSkillOffer SkillOffer;
   
   // Вводим идентификатор героя ID и количество уровней MaxLevel, на которое будем прокачивать героя.
   int ID, MaxLevel, Slot;
   cout << "Enter ID and MaxLevel: ";
   cin >> ID >> MaxLevel;
   cout << endl;

   // Читаем данные из структуры героя с идентификатором ID.
   RAMHero(hProcess, HStartPoint, ID, &Hero);
   
   // Читаем веса первичных и вторичных навыков для класса, к которому принадлежит герой Hero,
   // с учётом особенностей прокачки Йога в кампании "Рождение варвара" ("Birth of a Barbarian").
   // Для этого определим флаг BBFlag, который будет равен 0 для стандартной прокачки и 4 - для исключения
   BYTE BBFlag = isStandardWeights(hProcess, HStartPoint, 2, ID) ? 0 : 4;
   BBFlag ? RAMBBWeights(hProcess, HStartPoint, 2, Hero.Class, &Weights) : RAMWeights(hProcess, HStartPoint, 2, Hero.Class, &Weights);
   
   // Тестируем
   for (int i = 0; i < MaxLevel; ++i)
   {
      // Option = 000b = 0 - получить SkillOffer, не повышая уровень героя Hero;
      // Option = 001b = 1 - получить SkillOffer и повысить уровень героя Hero, выбрав навык из левого слота;
      // Option = 010b = 2 - получить SkillOffer и повысить уровень героя Hero, выбрав навык из правого слота;
      // Option = 1xxb - получить SkillOffer c учётом особенностей кампании "Рождение варвара" ("Birth of a Barbarian"),
      // где xx = 00, 01 или 10 (см. выше).
      
      // Пример.
      // Option = 110b = 6 - получить SkillOffer c учётом особенностей кампании "Рождение варвара" ("Birth of a Barbarian")
      // и повысить уровень героя Hero, выбрав навык из правого слота.
      LevelUp(&Hero, &Weights, &SkillOffer, BBFlag); // Option = 000b OR BBFlag

      // Выводим предлагаемые при повышении уровня навыки
      cout << "SkillOffer = [" << PrimSkillName[SkillOffer.Primary] << ", ["
          << SecSkillName[SkillOffer.Secondary.Left] << ", " << SecSkillName[SkillOffer.Secondary.Right] << "]]"
          << endl << endl;

      // Выбираем навык: 1 - левый, 2 - правый
      cout << "Slot = ";
      cin >> Slot;
      cout << endl;

      LevelUp(&Hero, &Weights, &SkillOffer, Slot | BBFlag); // Option = Slot OR BBFlag
      
      // Наблюдаем за изменениями характеристик героя.
      cout << "Level = " << (int)Hero.Level << endl
          << "LastWisdom = " << (int)Hero.LastWisdom << endl
          << "LastMagic = " << (int)Hero.LastMagic << endl
          << "Primary = " << (int)Hero.Attack << " " << (int)Hero.Defense
          << " " << (int)Hero.SpellPower << " " << (int)Hero.Knowledge
          << endl << endl;
         
      cout << "SkillCount = " << (int)Hero.SkillCount << endl << "Secondary = ";
      for (int i = PATHFINDING; i < NONE; ++i) cout << (int)Hero.Skill[i] << " ";
      cout << endl << endl;
      
      system("Pause");
      cout << endl;
   }   
      
   // Закрываем хендл, выгружаем библиотеку и выходим.
   CloseHandle(hProcess);
   FreeLibrary(hDLL);
   
   return 0;
}

Re: Программа для разведки

СообщениеДобавлено: 13 окт 2014, 03:15
AlexSpl
LM Oracle 3.19.0.0 XE (x64) с поддержкой HotA 1.3.3 EN (возможно, будет работать и с русской версией; не тестировал).

MD5 (LMOracle.zip, 838,072 bytes): 92237bf3a6649d3842facf0e8f5b86cb
MD5 (LMOracle.exe, 845,824 bytes): eee34e3a355ebb5922f84a86df50134a
Ссылка действительна в течение 7 дней

Re: Программа для разведки

СообщениеДобавлено: 13 окт 2014, 10:51
VDV_forever
AlexSpl писал(а):

LM Oracle 3.19.0.0 XE (x64) с поддержкой HotA 1.3.3 EN (возможно, будет работать и с русской версией; не тестировал).

http://dropmefiles.com/HNHgo

MD5 (LMOracle.zip, 838,072 bytes): 92237bf3a6649d3842facf0e8f5b86cb
MD5 (LMOracle.exe, 845,824 bytes): eee34e3a355ebb5922f84a86df50134a
Ссылка действительна в течение 7 дней


Залил ее на справочник: LM_Oracle_3.19.0.0_XE.zip XE (x64) с поддержкой HotA 1.3.3 EN

P.S. Увеличил объем файла который можно выкладывать на форуме, до 2 МБт. ;)

Re: Программа для разведки

СообщениеДобавлено: 20 окт 2014, 23:32
AlexSpl
LM Oracle 3.19.0.1 XE

 
Исправлены ошибки в Battle Supervisor, возникшие при портировании программы на x64.

Полное дерево на Core i5-3570:

Изображение

Re: Программа для разведки

СообщениеДобавлено: 29 ноя 2014, 19:28
AlexSpl
Небольшой апдейт, связанный с обнаружением бага в x64 версии. Игроки "в теме", возможно, также оценят новую консольную команду GETR.

LM Oracle 3.19.0.2 XE (x64)

 
[ HoMM II 2.1 RU ]
1. Исправлена ошибка x64 версии, возникающая при двойном клике на MP героя в списке (не запускался LM Oracle Hero Viewer).
2. Добавлена консольная команда GETR, показывающая текущее значение "боевого" ГПСЧ.
3. Теперь консольная команда PLAYER показывает мировоззрение (Alignment) игроков.


Для работы LM Oracle Hero Viewer папка BMP должна находиться в той же директории, что и исполняемый файл.

Re: Программа для разведки

СообщениеДобавлено: 30 ноя 2014, 11:58
DeathLust
AlexSpl писал(а):

Небольшой апдейт, связанный с обнаружением бага в x64 версии. Игроки "в теме", возможно, также оценят новую консольную команду GETR.

Где-то можно почитать список всех команд? ;) х86 версия не будет обновлена?

Re: Программа для разведки

СообщениеДобавлено: 30 ноя 2014, 14:29
AlexSpl
Цитата:
Где-то можно почитать список всех команд?

Отдельного справочника по консольным командам нет. Но все они более или менее подробно описаны в документах Readme.pdf и History.txt, что находятся в папке Readme.

Цитата:
х86 версия не будет обновлена?

Скорее всего, будет. Пока неохота выпускать обе ради небольших исправлений.

Re: Программа для разведки

СообщениеДобавлено: 08 дек 2014, 18:42
AlexSpl
Работаю над новой версией 3.19.1.0 XE. Уже запилил сортировку со "стрелочками" ([A], [Z], [0], [9] напрягать стали) и добавил новую консольную команду GETCHEAT (раньше была бы полезна, теперь больше для фана). Теперь планирую объединить мелкие окошки в одно: для Героев 1 - "дерево" прокачки + информацию о герое; Mage Guild Oracle + информацию о городе/замке. Для Героев 2 - Mage Guild Oracle + наконец добавить информацию о городских постройках, здесь же будет информация о гарнизоне. Если у Вас есть идеи насчёт того, какой информации о текущей карте не хватает в LMOracle, чтобы, например, не юзать другой софт, делитесь.

Re: Программа для разведки

СообщениеДобавлено: 10 дек 2014, 05:36
DR28
AlexSpl писал(а):

Если у Вас есть идеи насчёт того, какой информации о текущей карте не хватает в LMOracle, чтобы, например, не юзать другой софт, делитесь.

1. Координаты героев. Было бы наглядно и удобно при переходе хода смотреть, куда пошли компы. Для умерших невыкупленных - координаты точки слива соответственно.
2. Построенную магию в замках выделять цветом