[1.10] Checking what type of unit it is

This forum is for discussions on how to edit what can not be edited through the txt files, needless to say this isn't about battle net hacking.

Moderators: Nefarius, Havvoric

Post Reply
User avatar
kingpin
Senior Admin
Cherub
Posts: 10903
Joined: Sat Jan 11, 2003 12:51 pm
Contact:
Sweden

Hand-picked

[1.10] Checking what type of unit it is

Post by kingpin » Thu Jan 15, 2009 11:57 am

I have been made some code to check every single type of unit in the game (those units Im talking about is boss, minion, champion-types e.tc).

The code I'm writing down here is written in c++. But, for those who mainly do their code in asm it should be easy to convert.

Check if unit is regular unit or Superunique unit:

There is a function I call for D2GetUniqueNbr (D2Game6FC6AD10 Arg1 ptGame, Arg2 ptUnit). With this function we will get if the unit have an uniqueID or not (only superunqiue monsters have an uniqueID stored).

Code: Select all

WORD id = (WORD)D2GetUniqueNbr(ptGame, ptMonster);
BYTE file = 2; (setting default as superunique flag).

if(id == 0xFFFF) // ID missing. this is not a superunique monster
{
	id = (WORD)ptMonster->nUnitId; // setting the correct ID of the regular monster
	file = 1; // setting flag to 1. as it's a regular monster
}

Unique, champion type of mobs
Those units type is decided by a flag that I have named as TypeFlags. This flag is located in ptMonsterData structure

Ignoring filling in rest of structure as it should be able to find in the sticky thread.

struct MonsterData
{
BYTE TypeFlags; // +16
}

Typeflags:
Note: Several of "champion-type" of mobs is grouped together as champion e.tc. Only the main types is different in the type flags.

9 : UniqueBoss
13 : Champion
16 : Minion
45 : Possessed
77 : Ghostly

If you want to make a check for let say if unit is an uniqueboss you can do it in this way.

Code: Select all

if (ptMonster->pMonsterData->TypeFlags == 9)
  // Add your code here

Boss
In monstats.txt a boss uses the "boss flag" to tell this unit is a boss. There is a function I named to D2CheckBoss (D2Common2B34 arg1 Unknown, Arg2 ptUnit).

Code: Select all

if (D2CheckBoss(0, ptUnit) == 64){  // A boss have value of 64. so compare with that. I always set first arg at 0. As I haven't find any use of that param so far. Isn't needed for what we want anyway.

your code here...
}
Last edited by kingpin on Thu Jan 15, 2009 2:20 pm, edited 1 time in total.

User avatar
Necrolis
Site Admin
Throne
Posts: 8991
Joined: Sat Mar 25, 2006 1:22 pm
Location: The Land of the Dead
Contact:
South Africa

Hand-picked

Post by Necrolis » Thu Jan 15, 2009 3:56 pm

each unit type is a combination of basic flags:

Code: Select all

enum eD2MonsterFlags
{
	MONSTERFLAG_LEGACY				= 0x01,
	MONSTERFLAG_SUPERUNIQUE			= 0x02,
	MONSTERFLAG_CHAMPION			        = 0x04,
	MONSTERFLAG_UNIQUE				= 0x08,
	MONSTERFLAG_MINION				= 0x10,
	MONSTERFLAG_POSSESED			        = 0x20,
	MONSTERFLAG_GHOSTLY				= 0x40,
	MONSTERFLAG_MULTISHOT			        = 0x80
};

#define MONFLAG_GHOSTLY		MONSTERFLAG_GHOSTLY|MONSTERFLAG_UNIQUE|MONSTERFLAG_CHAMPION	
#define MONFLAG_POSSESSED	MONSTERFLAG_POSSESED|MONSTERFLAG_UNIQUE|MONSTERFLAG_CHAMPION				
#define MONFLAG_BOSS 		MONSTERFLAG_CHAMPION|MONSTERFLAG_UNIQUE

bool __fastcall MONSTERS_CheckBoss(D2UnitStrc* pMonster)
{
	if(pMonster == NULL || pMonster->nUnitType != UNIT_MONSTER)
		return false;

	D2MonsterDataStrc* pData = pMonster->pMonsterData;
	if(pData == NULL)
		return false;

	return (pData->fBossFlags & MONFLAG_BOSS || MONSTERS_CheckBoss(NULL,pMonster));
}
btw the func your refering to has this def ;)

Code: Select all

CALL <JMP.&D2Common.#11060>              ;  D2CheckUnitBoss(FileMonstatsTable* pRecord, D2UnitStrc* pMonster);
or better :D

Code: Select all

bool __fastcall MONSTERS_CheckBoss(FileMonstatsTable* pRecord, D2UnitStrc* pMonster)
{
	if(pMonster == NULL || pMonster->nUnitType != UNIT_MONSTER)
		return false;

	if(pRecord == NULL)
		pRecord = INLINE_GetMonsterRecord(pMonster->nUnitId);

	if(pRecord == NULL)
		return false;

	return pRecord->fMonsterFlags.fBoss & 1;
}
& a better way to get boss index[your does't even need the first param, btw, it can be NULL]

Code: Select all

CALL D2Game.6FC6ACD0           	  	 ;  D2GetBossIndex(D2UnitStrc* pMonster);
or better :D

Code: Select all

short __fastcall MONSTERS_GetBossIndex(D2UnitStrc* pMonster)
{
	if(pMonster == NULL)
		return -1;

	D2MonsterDataStrc* pData = pMonster->pMonsterData;
	if(pData == NULL || !(pData->fBossFlags & MONSTERFLAG_SUPERUNIQUE))
		return -1;

	return pData->nBossNo;
}

*I have a theory that the legacy flag actually indicates whether or not this is a server side monster, need to test that though
Last edited by Necrolis on Thu Jan 15, 2009 4:35 pm, edited 5 times in total.
Image
Netiquette, Do you USE it?!?! | Nefarius' Fixed TXT Files | Terms Of Service
Blackened | Day of Death
"What was yours is mine. Your land, your people, and now your life." - Lim-Dul, the Necromancer
Judgement is Final, Death is Eternal

User avatar
Nefarius
Retired Admin
Cherub
Posts: 11607
Joined: Sat Jun 15, 2002 8:13 pm
Location: Where the blood forever rains
Contact:

Hand-picked

Re: [1.10] Checking what type of unit it is

Post by Nefarius » Thu Jan 15, 2009 5:54 pm

You don't need to retrieve monstats from the datatables at all if you already have the monster unit itself in the function.

Code: Select all

int __fastcall COMMON_CheckIfSpecialMonster(D2UnitStrc* pUnit)
{
	// checks if a monster is special or normal
	// champions, uniques and superuniques, quest and act bosses
	// return true, else false
	// REVISED May, 2008 (by Nefarius)

	D2MonsterDataStrc* pMonDat = GetMonsterData(pUnit);

	if (pMonDat == NULL)
	{
		return 0;
	}

	DWORD flags = pMonDat->MonFlags;

	if (flags & (BOSSFLAG_CHAMPION|BOSSFLAG_UNIQUE|BOSSFLAG_SUPERUNIQUE))
	{
		return 1;
	}

	MonStatsTXTStrc* pMonstats = pMonDat->pRecord;

	ASSERT(pMonstats);

	return (pMonstats->dwFlags & (MON_BOSS|MON_PRIMEEVIL));
}
The pUnit->pMonData struct already contains a pointer to the record.
''(...) The game can basically be considered unhackable. '' - Blizzard Entertainment (30th May 2000)
Black Omen Productions | MetalStorm: Progress Report | Screenshots

User avatar
Necrolis
Site Admin
Throne
Posts: 8991
Joined: Sat Mar 25, 2006 1:22 pm
Location: The Land of the Dead
Contact:
South Africa

Hand-picked

Post by Necrolis » Thu Jan 15, 2009 6:33 pm

Yes, thats why the first param is a pointer to the monstats record, the inline thing is only there as a backup/i'm too lazy to care thing(i have a few variants of that func, that just happens to be the oldest one that worked like the ordinal i mentioned above it)
Image
Netiquette, Do you USE it?!?! | Nefarius' Fixed TXT Files | Terms Of Service
Blackened | Day of Death
"What was yours is mine. Your land, your people, and now your life." - Lim-Dul, the Necromancer
Judgement is Final, Death is Eternal

Post Reply

Return to “Code Editing”