Author Topic: Unit Maximum Hit Point Detection Function  (Read 2985 times)

Offline Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1269
Unit Maximum Hit Point Detection Function
« on: February 10, 2006, 08:23:25 PM »
Hacker coded the first version of this (that crashed the game), Hooman fixed it (the problem was apparently a shortcoming in MSVC++).

Code: [Select]
int GetHitPoints(Unit &unit)
{
    int unitObj = *((int*)0x54F848) + (120*unit.unitID);
    int hp = 0;
    __asm
    {
        mov ecx, unitObj
        mov eax, [ecx]
        call [eax]
        add eax, 8
        mov eax, [eax]
        mov hp, eax
    }
    return hp;
}

When setting unit damage with SetDamage(), units don't automatically die if the damage set exceeds the unit's maximum hit points, which is why I needed this function.
« Last Edit: February 10, 2006, 08:23:46 PM by Arklon »

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Unit Maximum Hit Point Detection Function
« Reply #1 on: February 11, 2006, 10:33:32 AM »
good work, arklon, hoomand & hacker !
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline HaXtOr

  • Sr. Member
  • ****
  • Posts: 423
    • http://www.wtfmoogle.com
Unit Maximum Hit Point Detection Function
« Reply #2 on: February 11, 2006, 12:58:21 PM »
now how can I use this code exactly?

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Unit Maximum Hit Point Detection Function
« Reply #3 on: February 11, 2006, 01:04:49 PM »
...? Copy/paste and call it. I'm sure you can figure it out.


There were a few changes to the assembly. One of the oddities that I noticed was that MOV EAX, [unitObj] assembled to the same instruction as MOV EAX, unitObj. I'm kinda used to NASM syntax, so that was a little unexpected. But then, there is only one sensical opcode either of those could have compiled to. I guess I was just expecting an error message from one of them.


I should mention a limitation that function has though. It'll only return the max hitpoints for unit of that type *for player 0*. If some research is done that increases max hitpoints for that unit, then you might get some funny results.
 

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Unit Maximum Hit Point Detection Function
« Reply #4 on: February 12, 2006, 12:57:55 PM »
Arg! I must be losing it again. Forgot that I was calling a class member (and had forgotten to fill ecx with the 'this' pointer)

Oh yeah, good point. Research and other factors could screw it up.

Anyway, thanks for fixing my code when I was out. :P

Offline Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1269
Unit Maximum Hit Point Detection Function
« Reply #5 on: February 12, 2006, 01:30:45 PM »
Well, it's not really a problem for structures. Don't think there's any research to increase a structure's HP (just armor for guard posts). Vehicles, on the other hand...

Anyway, here's an example of how you could use the code:
Code: [Select]
IUnit x; // Need to use extended unit functions for GetDamage (http://www.wiki.outpostuniverse.net/Op2extra)
x.SetDamage(1000);
if (x.GetDamage() == GetHitPoints(x)) // When using setdamage, if the damage exceeds the unit's HP, it DOES NOT automatically die. Utterly dumb programming.
     x.DoDeath();
« Last Edit: February 12, 2006, 01:35:05 PM by Arklon »

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Unit Maximum Hit Point Detection Function
« Reply #6 on: February 12, 2006, 07:34:52 PM »
Quote
Well, it's not really a problem for structures. Don't think there's any research to increase a structure's HP (just armor for guard posts).
Well, it would be good if it would give the current max HP, taking upgrades into effect. You can't guarantee that techtrees won't be made that increase the HP of a building. (I'm sure plenty of people will want to do it at some point)

However, I thought upgrades in the techtree modified the UnitInfo structure's HP value. Correct me if I'm wrong?

Also, I think the game scales the HP values somewhat (that's why things take much longer to build in single player than they do in multiplayer). Again, I'm not sure if the scaled values are written into the UnitInfo structure.

BTW: I think this code deserves a technical explanation, on how / why it works.
Remember that the Unit variable is just a wrapper around the actual internal unit structures inside the game, and it has one member, the index of the internal unit. The function looks up the internal unit entry using this ID.

Next, it dereferences it to obtain the vtable. (The vtable is a structure in memory that holds all the addresses of functions within the class. I think any class with at least one non-static member function has a vtable. I can't remember currently). The first function in the vtable is one that returns a pointer to the given unit's UnitInfo structure, so it is called to obtain the structure. (A UnitInfo structure contains most of the information about the unit that is loaded from the .txt files in sheets.vol)

The code dereferences the pointer, and can then load the HP value located at offset 8. (Hence the add eax, 8) This value is put into hp and then returned.

Well, there's the technical explanation, if anyone is interested. It probably helps to know assembly and a little about how MSVC's implementation of C++ classes works.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Unit Maximum Hit Point Detection Function
« Reply #7 on: February 12, 2006, 08:06:50 PM »
Well, to be fair, the ECX thing wasn't what caused the crash. If you look at the implementation of the function you were calling, no class variables were accessed, so ECX was never used. Although, it is more correct to put it there, since there is no reason why it can't be used. (The bug was insufficient indirection on the call. It was calling the vtbl instead of a function in the vtbl. See my note about two different things assembling to the same opcode. Also, there was insufficient indirection to load the hp value. It loaded the address instead).

Also, the offset of 8 is only valid for player 0. Technically, the UnitInfo structs have 2 fields at the start (the vtbl pointer, and the map_id enum of the unit it is describing). After that, there is an array of player specific data. So it'd be UnitInfo.playerInfo.maxHP that you want to access. It's only at offset 8 if i = 0. The bad news is, sizeof(playerInfo) as I've just written it, is dependent on the unit type. So you can't used a fixed size for indexing.

I'd imagine the game has built in functions to handle retrieving the max hp. The best way to get this working reliably in all cases, would be to find that function (if it exists). On the other hand, it might be fixed for say, all buildings, and all vehicles, so maybe the game just indexes differently depending on whether it's a vehicle or a building. Also keep in mind that each unit has both a creatorNum and and ownerNum (I might have referred to it as creatorID or ownerID before). Usually upgrades are checked for using the creatorNum and not the ownerNum, so you'd want to use the right one for proper behavior.


And yes, the tech upgrades do upgrade the fields in the UnitInfo struct, but only for that one player who completed the research.
 
« Last Edit: February 12, 2006, 08:09:30 PM by Hooman »