Heroes of Might and Magic Community
visiting hero! Register | Today's Posts | Games | Search! | FAQ/Rules | AvatarList | MemberList | Profile


Age of Heroes Headlines:  
5 Oct 2016: Heroes VII development comes to an end.. - read more
6 Aug 2016: Troubled Heroes VII Expansion Release - read more
26 Apr 2016: Heroes VII XPack - Trial by Fire - Coming out in June! - read more
17 Apr 2016: Global Alternative Creatures MOD for H7 after 1.8 Patch! - read more
7 Mar 2016: Romero launches a Piano Sonata Album Kickstarter! - read more
19 Feb 2016: Heroes 5.5 RC6, Heroes VII patch 1.7 are out! - read more
13 Jan 2016: Horn of the Abyss 1.4 Available for Download! - read more
17 Dec 2015: Heroes 5.5 update, 1.6 out for H7 - read more
23 Nov 2015: H7 1.4 & 1.5 patches Released - read more
31 Oct 2015: First H7 patches are out, End of DoC development - read more
5 Oct 2016: Heroes VII development comes to an end.. - read more
[X] Remove Ads
LOGIN:     Username:     Password:         [ Register ]
HOMM1: info forum | HOMM2: info forum | HOMM3: info mods forum | HOMM4: info CTG forum | HOMM5: info mods forum | MMH6: wiki forum | MMH7: wiki forum
Heroes Community > Heroes 3.5 - WoG and Beyond > Thread: Heroes 3 Hacking Reference Guide
Thread: Heroes 3 Hacking Reference Guide This thread is 42 pages long: 1 2 3 4 5 ... 10 20 30 ... 38 39 40 41 42 · «PREV / NEXT»
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 13, 2023 12:44 AM

Make sure you get a correct pointer to a _Town_ structure.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
parascus
parascus


Adventuring Hero
posted October 13, 2023 08:23 AM

Hi AlexSpl,

I thought I have the correct town:

{
  int playerId = o_ActivePlayer->id;
  _Player_* player = pGame->GetPlayer(playerId);

  for (int i = 0; i < pGame->GetTownsCount(); i++)
  {
     _Town_* town = pGame->GetTown(i);

     string townName = town->name;
     debugStrWin("%s", townName);
     debugStrWin("%x/%x/%x", town->x, town->y, town->z);
  }
}

The town name is correct, but x, y and z isn't. Am I using the Wrong _Town_?

Also I thought that with
 debugStrWin("%x", gpGame->sMapHeader.teamInfo[playerId]);

I get the team number of a player but it is always 0.

It's really confusing.

Best regards

Parascus

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 13, 2023 11:22 AM
Edited by AlexSpl at 11:24, 13 Oct 2023.

Quote:
The town name is correct, but x, y and z isn't. Am I using the Wrong _Town_?

Knowing x, y, z of a town, try to use pGame->FindTownIdByXYZ(x, y, z) to get its ID and compare it with the ID of the town you get using pGame->GetTown(i); Note, that towns' IDs that belong to a player may not be consequent.

Quote:
I get the team number of a player but it is always 0.

Some classes of the API may be not properly aligned as it's WIP. I didn't verify those classes that I didn't use in my code. If you really need to use them, upload your code, and I'll see what can be done. You may as well include H3API in your solution.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
parascus
parascus


Adventuring Hero
posted October 13, 2023 03:24 PM
Edited by parascus at 16:35, 13 Oct 2023.

Hi AlexSpl,

I don't get it. I'd like to progam it on my own but it seams that there is something blocking my brain. I have uploaded my source and plugin (actually your source and your pluging with some changes ): NewSpells.zip.

The main changes are in the dllmain.cpp, where I inserted a new case-section for my spell SEPLL_RETREAT. This calls a new method executeSpellRetreat(Hero). There all hell breaks loose. I have inserted the things I want to do, marked as "TODO". There are also two remarks with "Problem" where I do not manage to get the town location and the team members of a game/map.

I have included homm3.h and HoMM3API.h but neither of them showed success. I assume that the reason is not the libraries but maybe my lack of knowledge of the structures as well as pointers and details of variable types.

I would appreciate if you can tell me the mistakes I've done in my code at the parts marked as problem.

Best regards

Parascus

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 14, 2023 12:38 AM
Edited by AlexSpl at 00:40, 14 Oct 2023.

I will add your spell with placeholder graphics but without the actual implementation. It will show values in your TODOs sections. But it will take some time (obviously).

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 14, 2023 03:35 AM
Edited by AlexSpl at 12:28, 14 Oct 2023.

Well, I pretty much simplified the task for you (default graphics).

NewSpells (test)

Basically, it's Town Gate mechanics. To make it produce sound, spend mana points add corresponding methods (though it may be done by the method itself). To make it different from Town Gate, you'll need to hook advManager::TownGate() method. Believe me, it's simpler than rewriting it from scratch (but it can be rewritten as a new method, of course*). Some changes to advManager::TownGate() are needed to distinguish Retreat from it. This can be done with LoHooks.

*) You've chosen not a simple spell to start with If you have the void17's database, you can see how Town Portal is implemented. And it's not quite simple code with many additional checks such as victory conditions. Probably, the best solution, after all, will be to write your own method, borrowing the code from advManager::TownGate(). Also see there how gpGame->sMapHeader.teamInfo[] is used.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
parascus
parascus


Adventuring Hero
posted October 14, 2023 07:37 PM

Hi AlexSpl,

thank you for your response and modification. It is not exactly what I try to do. I rather would write my own method because then I have everything in my own hand. E.g. I don't like the "occupied"-Response. If the nearest town is occupied the next one should be used. And if my power is not high enough to reach any town the spell is not working.

So option two would be more fitting. Here my "only" problem is that on the one hand I do not fully understand the object model (connections and tasks of the classes) and on the other hand when I think "yes, this is the right object, e.g. teams array where I assume for every player the team number is present or town.x, town.y, town.z represent the position of the town on the map, it does not work and the fields of the object does not have the expected values.

Is it easier when I use the void17 database? Where can I find it?

Best regards

Parascus

P.S.: I not only want to create the new retreat spell but also want to change town gate because it is to mighty.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
parascus
parascus


Adventuring Hero
posted October 15, 2023 09:55 PM

Hi AlexSpl,

I digged a little bit deeper now but did not realy proceed.

First of all I called the TownGate function as you suggested. I inserted a LoHook into this function so right before it tests on the team membership I step in to check the distance between the hero and the town. No crashes, good sign. But I don't have the town in any  but also no access to the town and its location.
So I tried again to get the town by pGame->GetTown(0) but again it doesn't seem to be the town structure.

I found that pGame is short for (*(Game**)0x699538) which seems to be a constant pointer to a running game.
Game is a structure which wraps a _GameMgr_ which has the structure implementation.
In homm3.h there is the inline function GetTown in the _GameMgr_ structure which takes the pointer to itself and adds 0x21614. Than it adds 360 the townId. The result is taken as a pointer to a town class. But this doesn't seem to work. Interesting is that at 0x21620 the heroes are started.

Looking into HoMM3API.h there is the structure "game" having a std::vector<int> townPool beginning at 0x21610, 4 bytes earlier than in homm3. But when I try to access towns with this value the game crashes.

I am confused. Has anybody successfully tried to get the town in a plugin program?

Best regards

Parascus

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
parascus
parascus


Adventuring Hero
posted October 18, 2023 09:50 PM

Hi AlexSpl,

I think I'm missing some game process points and this is the reason I do not get the correct values.

Debugging into the code of TownGateat 41D515 the active hero of the current player is determined. I believe that the structure beneath is the game structure where in HoMM3API the field is playerdata at 0x20AD0. of the structure. when I calculate the address of the game it results to 0x067F4B30 - 0x20AD0 = 0x067D4060.
In the template code I try to modify, there is a define-statement for pgGame resulting in (*(game**)0x699538).
I tried to define another variable pGameOther with (*(game**)0x067D4060) and than access it with char teamCount = pGameOther->sMapHeader.numTeams but this also shows not the number of teams.
So I wonder why there are different addresses in the code and in the game and how do I get the game information of the running game. And also I wonder why I still don't get the correct team number of 3 but the number 0.

Any advise?

Best regards

Parascus

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 20, 2023 08:04 AM

Note, that I didn't check classes/fields which I didn't use in my plugin. Probably, you are right, and there are some issues with alignments/offsets. I'll try to recheck that later.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
parascus
parascus


Adventuring Hero
posted October 20, 2023 08:17 AM

Hi AlexSpl,

thanks for your help. But I don't want to impose on you reagrding this point any further...

Yesterday I got a step in the right direction. It is a pointer problem. I still don't get the town coordinates but I managed to get the team assignments. The solution is that at 0x699538 there is not the game data, it is the address where the game data can be found. I still do not understand why the structures are not working but using the following statement I get the team of the current player:

_Player_* player = pGame->GetPlayer(playerId);
char* teamArray = ((char*)pGame) + 0x1F86C + 0x0d;

... still wondering why it can't be helped withgpGame->sMapHeader.teamInfo[playerId]

But the main thing is that it works. So I'm positive that I'll find a why to juggle the pointers and reach my goal.

Once again thank you and I will notify you on the progress.

Best regards

Parascus

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 21, 2023 11:54 AM
Edited by AlexSpl at 11:56, 21 Oct 2023.

Quote:
... still wondering why it can't be helped withgpGame->sMapHeader.teamInfo[playerId]

It seems there is a common error in every API I know of. Try to change these lines -

char max_hero_level; // 0x00B
char teamInfo[9]; // 0x00D
int Size; // 0x018


and then see contents of teamInfo[] in your spell's method -

for (int i = 0; i < 9; ++i) {
   debugStrWin("%d", gpGame->sMapHeader.teamInfo[ i ]);
}


Seems to start with 8 always, then IDs of teams follow, then IDs of players which don't belong to any team follow. But I'm not sure.

You can rewrite names of classes' fields as you understand them, but always check the final size of a class to not break offsets.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 21, 2023 12:14 PM
Edited by AlexSpl at 15:39, 21 Oct 2023.

Probably, numTeams means maxNumTeams (in a sense of limit), and you have to calculate them yourself. Currently, there are no APIs publicly available which are 100% correct, so always check what fields actually contain.

For example, here you'll get numTeams = 8, then these numbers follow - 0, 0 (the 1st team; players 0 and 1), 1, 1, 1 (the 2nd team), 2, 2, 2 (the 3rd team), which are obviously not IDs of teams (as you see on the picture below), only the order (index) matters.

Players are in the same team when gpGame->sMapHeader.teamInfo[playerId_A] == gpGame->sMapHeader.teamInfo[playerId_B].

UPD. I suppose, that's how teamInfo originally designed. So, no mistake here.



* * *
About vectors. Note, that std::vector<int> is a placeholder and needs a real type instead of int (the idea was to save actual sizes of game classes, which I didn't fully use). Also note, that std::vector<> is not a modern version of it, that's why you need

#define _SECURE_SCL 0
#define _HAS_ITERATOR_DEBUGGING 0


I'll try to solve your problem with towns laters.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
parascus
parascus


Adventuring Hero
posted October 21, 2023 04:02 PM

Hi AlexSpl,

thanks for your support. No software is flawless and I'm surprised by the high quality of the header files.

I tried to investigate the team array a little bit further but using the structures I always land 0x275 bytes next to the real array. The important parts are i.m.o.:

The pointer to the game in progress:
#define gpGame (*(game**)0x699538)

The game class with the map info:
class game
{
public:
...
NewSMapHeader sMapHeader; // 0x1F86C
...
}

The map info:
class NewSMapHeader : public CMapHeaderData
{
public:
  stdString Name; // 0x2D0
  stdString Description; // 0x2E0
  std::bitset<156> hero_available; // 0x2F0
// 0x304
};
With the parent structure:
class CMapHeaderData
{
public:
  int iVersion; // 0x000
...
  char numTeams; // 0x00C
  char teamInfo[8]; // 0x00D
  int Size; // 0x018
...
}

With this I can access the teamInfo-array with ((char*)pGame) + 0x1F86C + 0x0d = e.g. 067C38D9
Using the class pointers gpGame->sMapHeader.teamInfo I get an address of 067C3B41.

But I think  I can live with the direct pointers.

Regarding towns I have made a huge step in the right direction. Taking a deeper look into a big class/method file I found TownGate and in it the loop over towns. I copied this code and modified it a bit into:

for (int i = 0; ; i = (i + 360))
{
First = pGame->GetTown(0);
if (!First || i/360 >= townCount)
break;
town = (_Town_*)((char*)First + i);
               ...

This way get a real town and the x, y and z variables are correct! Yipiieee! I can calculate the distince between my hero and the town, look if there is alerady a hero in garrsion or visiting and I can see the owner and with this if he is in the same team. I also already managed to look if the hero has enouch points to cast the sepll and if not to display a heroes message box to inform about the need for a good sleep.

But looking into the _Town_ structure I find
_char_ name[12];

When I debug this it has an address to the town name itself, atleast when I use a map of the map editor and give a name to the town.

And now the problem is another one: Pointers. I tried different access types but every time it tried to use the char array as a string. How can I tell C that the bates should be used as a pointer and that it should return the contents of the memory space this address is pointing to?

Thanks a lot for yoour help.

Best regards

Parascus

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 21, 2023 04:13 PM

Quote:
I tried to investigate the team array a little bit further but using the structures I always land 0x275 bytes next to the real array.

You may use not the final version of the headers I've provided. Try to check this. If you don't see 8, 0, 0, 1, 1, 1, 2, 2, 2 in my example, then something wrong with alignments. Also I don't know if my plugin correctly compiles under newer versions of MS VS (I use 2008).

Quote:
Regarding towns I have made a huge step in the right direction. Taking a deeper look into a big class/method file I found TownGate and in it the loop over towns.

Nice This can be done with vectors. Note, that 360 is the size of the town class.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 21, 2023 04:40 PM
Edited by AlexSpl at 17:09, 21 Oct 2023.

Quote:
And now the problem is another one: Pointers. I tried different access types but every time it tried to use the char array as a string. How can I tell C that the bates should be used as a pointer and that it should return the contents of the memory space this address is pointing to?

Don't use std::string in your code. There is older version - stdString that works. Also note, that it has const char* text; field to get its content. Anyway, to understand what you mean, I should see an example. Basically, there are char* pointers for strings or they can be represented as char array, the difference is in that in the second case characters can be read directly. str[0], str[1], and so on.

* * *
Probably, I understand what you mean. Note, that any pointer takes 4 bytes of memory (under x86), including char*. char name[12]; takes 12.

char* is just an address (4 bytes). char array is an actual string.

That's why you see

char_ name[12]; // C8
int   _u8[3]; //* +D4 = 0


0xD4 - 0xC8 = 0x0C (12, which is length of a string).

I didn't include the actual town class, as I hadn't checked it and don't use in my plugin. But offsets of the _Town_ class should be correct, as it's a very old version used by HotA.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
parascus
parascus


Adventuring Hero
posted October 21, 2023 09:45 PM
Edited by parascus at 21:50, 21 Oct 2023.

Hi Alexpli,

Thank you for the info. char * is a pointer to a char and it can be used as a char array by accessing the next chars as well. in the homm3.h there should be a name with 12 chars beginning with the 200th char. But it is not the case.
On the first screenshot you can see the town structure beginning at xxx. 200 bytes in it there is the name array with 12 chars but it is not really a name but rather an address. On the second screenshot you can see the memory at the address mentioned in the town structure and it is the name I was looking for.





But how can I access the data in the town structure so it handles it as an address to the char array (or string because it is 00-terminated)?

Best regards

Parascus

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 22, 2023 03:26 AM

Then it's the first case I've mentioned. It's stdString (see HoMM3API, the size of stdString is 12 bytes also). You can access the name using stdString's text field.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 22, 2023 06:14 AM
Edited by AlexSpl at 11:23, 22 Oct 2023.

I've added the missing town class for you. Both examples, how to iterate through towns and how to get text field of stdSting, are in the executeSpellRetreat() method. There is also a compiled dll in the Release folder. Don't forget to set your configuration to Release, as well, when you build the solution.

NewSpells (test)

Forgot to mention that now you can directly use gpGame->townPool -

void executeSpellRetreat(hero* Hero)
{
   std::vector<town>* towns = &gpGame->townPool;
   for (std::vector<town>::iterator it = towns->begin(); it != towns->end(); ++it)
   {
       debugStrWin("%s", it->cName.text);
   }
}


Quote:
I tried to investigate the team array a little bit further but using the structures I always land 0x275 bytes next to the real array.

Btw, that might be caused by that you didn't set default alignment (Project -> Properties -> Configuration Properties -> C/C++ -> Code Generation -> Struct Member Alignment must be 1 Byte).

1. Try to use my solution (that I've provided).
2. Try to build the solution under Visual Studio 2008.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
parascus
parascus


Adventuring Hero
posted October 22, 2023 02:07 PM

Hi AlexSpl,

thank you for your effort and it sounds realy good.

I inserted your town structure into HoMM3API.h and changed the type of townPool in the game structure to std::vector<town>. My function now looks like:
void executeSpellRetreat(hero* hero)
{
std::vector<town>* towns = &gpGame->townPool;
debugStrWin("%x = %x?", towns->begin(), towns->end());
for (std::vector<town>::iterator it = towns->begin(); it != towns->end(); ++it)
{
debugStrWin("Townname: %s", &it->cName);
}
}

But I noticed that the loop is not entered. towns->begin() has the same value as towns->end() although the map was generated by random and has 8 players on it so at least 8 towns.

Also I cannot change my configuration to Release and am still running it with Debug because I get the following error:
  error C2338: static_assert failed: 'Windows headers require the default packing option. Changing this can lead to memory corruption. This diagnostic can be disabled by building with WINDOWS_IGNORE_PACKING_MISMATCH defined.'

If this is not a cause for my trouble I will stay at the debug mode which is working totally fine.

Have a nice Sunday

Parascus

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
Jump To: « Prev Thread . . . Next Thread » This thread is 42 pages long: 1 2 3 4 5 ... 10 20 30 ... 38 39 40 41 42 · «PREV / NEXT»
Post New Poll    Post New Topic    Post New Reply

Page compiled in 0.1218 seconds