Jump to content

[Done!] Animated 3D Monsters in RO


DoSS

Recommended Posts


  • Group:  Members
  • Topic Count:  87
  • Topics Per Day:  0.02
  • Content Count:  1335
  • Reputation:   932
  • Joined:  10/26/11
  • Last Seen:  

Thank you a lot! we really appreciate your help on this. I have sent you some files that will be really helpful to you in case you want to dissamble this client, please take a look at your skype when you can afford it.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  5
  • Topics Per Day:  0.00
  • Content Count:  59
  • Reputation:   7
  • Joined:  03/13/12
  • Last Seen:  

just seen this, and i think its awsome, gl to you guys!

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  8
  • Topics Per Day:  0.00
  • Content Count:  417
  • Reputation:   354
  • Joined:  11/11/11
  • Last Seen:  

struct granny_animation* __cdecl C3dGrannyBoneRes::GetAnimation(int nType, int nAniIdx)
{
if( this->m_Animation[nType][nAniIdx] == NULL )
{
	g_Unknown1[0] = 0;

	if( nType >= 0 && nType <= 9 )
	{
		if( nAniIdx >=0 && nAniIdx <= 4 )
		{
			if( nAniIdx )
			{
				sprintf(g_Unknown1, "model\\3dmob\\%s", g_session.GetJobName(g_Unknown2[nType]));
			}
			else
			{
				sprintf(g_Unknown1, "model\\3dmob\\%d_%s.gr2", nType, g_Unknown3[nAniIdx]);
			}

			if( g_Unknown1[0] && CFile::IsFileExist(g_Unknown1) )
			{
				if( nType != 1 )
				{
					this->AddBone(g_Unknown1, nType, nAniIdx);
				}

				return this->m_Animation[nType][nAniIdx];
			}
		}
	}
}
else
{
	return this->m_Animation[nType][nAniIdx];
}

return NULL;
}

  • Upvote 2
Link to comment
Share on other sites


  • Group:  Developer
  • Topic Count:  28
  • Topics Per Day:  0.01
  • Content Count:  547
  • Reputation:   270
  • Joined:  11/08/11
  • Last Seen:  

Thanks for your contribution Ai4rei, could you post some functions that are calling this function or the best would be if it would only be called by one function. Since we need to know how "nType" is set for the certain IDs.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  87
  • Topics Per Day:  0.02
  • Content Count:  1335
  • Reputation:   932
  • Joined:  10/26/11
  • Last Seen:  

With all my limits in the area and being afraid of saying something stupid, I'm dare to say:

"g_unknown1", "g_unknown2", ""g_unknown3", are assigned if there are animations presents. It is like checking if it is certainly a 3d GR2 mob, in different levels (first as default gr2, then, going onwards assigning it as a gr2 with 4 animation indexes present) I'm thinking our gr2 default missing standing animation has to do with these?

Again, sorry, and perhaps I should shut the fuck up XD. All of this dissamble stuff is beyond all I know.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  8
  • Topics Per Day:  0.00
  • Content Count:  417
  • Reputation:   354
  • Joined:  11/11/11
  • Last Seen:  

Continuing this PM-wise will probably be quite inefficient.

  • char g_Unknown1[]
    A global variable used for setting up the 3dmob file name, there's probably no actual reason for it to be global.
  • int g_Unknown2[]
    A constant fixed-size global array used for mapping nType (3dmob ID) to NPC/Mob IDs (that's why standing animations do not work for customs).
  • const char* g_Unknown3[]
    A constant fixed-size global array used for mapping nAniIdx (animation type) to respective animation name (stand, move etc).

Edit #1:

Corrected wrong variable types.

Edit #2:

I noticed an another thing that I have been after for a while; the fact that guild flags no longer animate like they used to in very old clients. The reason for that turned out to be the AddBone exemption for guild flags:

if( nType != 1 )

Which means, if the exemption is deleted, guild flags starting waving in the wind again.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  87
  • Topics Per Day:  0.02
  • Content Count:  1335
  • Reputation:   932
  • Joined:  10/26/11
  • Last Seen:  

Yes Guild Flags have animation data if you open them with the Granny Viewer you see it.

It is truly amazing that you find what it was disabling it.

Really thanks for taking a look at this. We really apreciate every new you can have about what it is hardcoded in the client about granny.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  7
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   189
  • Joined:  05/20/12
  • Last Seen:  

Maybe the easiest solution would be to export the standing animation to the 3dmob_bone folder for custom monsters and just try to add in a "nType < 9" check which would prevent look-up in the table all together.

if( nAniIdx && nType < 9 )
{
sprintf(g_Unknown1, "model\\3dmob\\%s", g_session.GetJobName(g_Unknown2[nType]));
}
else
{
...

It that works there would be no need to do anything special when adding 3D mobs.

By the way, where do you guys keep getting the debug symbols from?

Edited by curiosity
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  87
  • Topics Per Day:  0.02
  • Content Count:  1335
  • Reputation:   932
  • Joined:  10/26/11
  • Last Seen:  

@curiosity

I'm not sure.

Lets say we do this:

Inside data/3dmob_bone/

10_attack.gr2

10_dead.gr2

10_damage.gr2

10_move.gr2

--> And add a 10_stand.gr2

All fine here, we can do that for our own animated models in 3ds max and exporting the bones only of a standing animation and then using the granny exporter/converter of ricky92.

But how about the default RO models?, they are still preserving the previous way of working.... I'm sure it will be really hard to manage 2 different ways of recalling GR2 mobs customs -with 5 animations- and original ones -with 4 and only 1 default- in terms of the emulator and client.

I don't know, at this point if certainly it will be easier.

@Ai4rei

Since you looked at a limitation of WoE Flags, let me tell you about another one (and perhaps you are interested to take a look): The "_damage.gr2" that corresponds about being hitted, it is not showing by the client too with even official mobs that has it present (guardians for ex). I'm not sure if that is a limitation from the client, or it is not recalled from the emu by intention.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  7
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   189
  • Joined:  05/20/12
  • Last Seen:  

I was hoping if it was done that way it would continue to work normally for all the Gravity GR2 files, but for customs (ID > 9) it would look for the standing animation in the 3dmob_bone folder. I could be wrong though.

I guess the alternative would be to add the custom entries to the ID mapping table?

0	508h	1288	JT_EMPELIUM		  Empelium90_0.gr2
1	2D2h	722	JT_GUILD_FLAG			Guildflag90_1.gr2
2	52Ch	1324	JT_TREASURE_BOX1		TREASUREBOX_2.gr2
3	3E8h	1000	JT_MON_BEGIN			
4	3E8h	1000	JT_MON_BEGIN
5	3E8h	1000	JT_MON_BEGIN
6	3E8h	1000	JT_MON_BEGIN
7	506h	1286	JT_KNIGHT_GUARDIAN		Kguardian90_7.gr2
8	505h	1285	JT_ARCHER_GUARDIAN		Aguardian90_8.gr2
9	507h	1287	JT_SOLDIER_GUARDIAN		Sguardian90_9.gr2

You can add custom 4 entries for the unused values, but if you want more I guess you'd have to relocate it. For some reason the guild flag model is initialized with ID 5 (which used to belong to Zombie Dragon) in the WinMain function. I really don't get what that's about.

On an unrelated note I finally found out the PDB everyone seem to have comes from the HighPriest_081105 client, but it's no longer available for download anywhere...

If anyone would be willing to upload HighPriest.exe and HighPriest.pdb I'd really appreciate it!

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  87
  • Topics Per Day:  0.02
  • Content Count:  1335
  • Reputation:   932
  • Joined:  10/26/11
  • Last Seen:  

I have sent you an inbox (thanks to Yommy for the files of course) days ago :P with the DL.

  • Upvote 1
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  7
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   189
  • Joined:  05/20/12
  • Last Seen:  

LOL thanks! I searched so hard for it yesterday. Then I'll experiment a bit and see if I can get something working!

Double posting for a little update. I managed to get almost everything working with custom monster ID.

I used 2012-04-10aRagexeRE.exe and did the following:

  • Added the test GR2 files to 3dmob, bone animations to 3dmob_bone and renamed with ID 10
  • Added a new monster entry in the LUA files (monster ID 2500)
  • Patched max. 3D mob animation ID to 10
  • Moved the "GR2 animation ID to monster ID" table to a new offset in the executable where there's more room
  • Added the monster ID (2500) as the 10th entry in the table

So it's a hacky approach, but if anyone really want custom GR2 monsters it's a temporary solution for now. There seems to be a lot of space available in the client I used, so it should be possible to add many monsters. The downside is that you need to edit new entries into the client.

Everything except for damage animation is working, but I'm wondering if it even works for any of Gravity's monsters anyway?

2012-04-10aRagexeRE.exe.patched2.zip

Link to comment
Share on other sites


  • Group:  Developer
  • Topic Count:  28
  • Topics Per Day:  0.01
  • Content Count:  547
  • Reputation:   270
  • Joined:  11/08/11
  • Last Seen:  

Wouldn't it be possible to simply change the mapping for a certain mob id range, for example if mob id is between 2600 and 2700 look it up like a GR2 file otherwise use the static mapping table?

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  87
  • Topics Per Day:  0.02
  • Content Count:  1335
  • Reputation:   932
  • Joined:  10/26/11
  • Last Seen:  

@curiosity

Awesome!. For now that will be enough for us. The damage animation is not showing even in official 3d mobs. So it doesn't matters really.

Don't you mind to explain to us:

  • Moved the "GR2 animation ID to monster ID" table to a new offset in the executable where there's more room
  • Added the monster ID (2500) as the 10th entry in the table

These two methods in detail for other client dates? I'm willing to learn how to do this by my own. Perhaps a quick reference or some guidelines,.. this would be really helpful since I'm aiming to do a guide in the graphics forum about importing animted gr2 at the time the project is done.

Link to comment
Share on other sites


  • Group:  Developer
  • Topic Count:  28
  • Topics Per Day:  0.01
  • Content Count:  547
  • Reputation:   270
  • Joined:  11/08/11
  • Last Seen:  

It means that he copied the table to some free space in the binary and then changed all the addresses to the new one. Additionally he inserted a 10th entry into this mapping table, because he now has more space that he could use. It should work out with other clients too, but the offset has to be calculated for each client date separately and also the free space in the executable has to be looked up by hand for each one.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  7
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   189
  • Joined:  05/20/12
  • Last Seen:  

Lemongrass has it right. This approach requires some handiwork, which is why it'll hopefully just be a temporary solution. Something like mapping a range of IDs could probably work OK. For instance, for nType > 9 set monster ID to some constant + nType instead of doing the table look-up. Hopefully someone more skilled than me can help out, because I'm falling a little short here.

I'll PM you a little tutorial for your client, Olrox!

  • Upvote 1
Link to comment
Share on other sites


  • Group:  Developer
  • Topic Count:  28
  • Topics Per Day:  0.01
  • Content Count:  547
  • Reputation:   270
  • Joined:  11/08/11
  • Last Seen:  

Curiosity could you look into the client a little bit deeper and look for the lookup in the mapping table and maybe post it here, because I think that would be the easiest way to recreate the whole GR2 loading function and point it to our own version of a loading function, which would maybe even work out as a WeeDiffPlugin.

I am sorry that I cannot do this myself, but I only have an ugly decompiled C version of the client not a nice C++ one like you all seem to have.

  • Upvote 1
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  7
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   189
  • Joined:  05/20/12
  • Last Seen:  

The look-up is in the function which already has been posted in this thread (C3dGrannyBoneRes::GetAnimation). I think there's something a little wrong with the code Ai4Rei posted (looks like inner if and else blocks are switched), but if you look at his code the table look-up happens at the line

sprintf(g_Unknown1, "model\\3dmob\\%s", g_session.GetJobName(g_Unknown2[nType]));

g_Unknown2 is the table mapping from nType to job ID, and it's referenced exclusively by this function.

Just to recap a bit: In order to load the requested animation it needs the file name of the GR2 file where the animation is stored. At the point where the mapping table is used it knows that the requested action is the standing animation. It needs the file name from the job name table since the default/standing animation is stored in that GR2 file with the model data. GetJobName requires the job ID, so it needs a way to get the job ID associated with the nType/bone animation index. For that purpose it uses the little table to look-up the job ID by nType.

The above line of code in assembly:

mov edx, job[esi*4] ; fetch job ID using nType from the table
mov this, offset g_session
push edx
call GetJobName;
push eax ; job name entry
push offset aModel3dmobS ; "model\\3dmob\\%s"
push (offset fileName) ; Dest
call _sprintf
add esp, 0Ch
jmp short loc_43585F

So in order to avoid the table something needs to be done with that first instruction there. At this point I know what could be done, but not how to do it because I don't have any experience with this kind of stuff. Maybe you know how to insert a new function and make it call that instead of the table look-up so we can do our own mapping from nType to job ID?

  • Upvote 1
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  50
  • Topics Per Day:  0.01
  • Content Count:  974
  • Reputation:   41
  • Joined:  11/13/11
  • Last Seen:  

@curiousity = how cute is your picture /lv

Link to comment
Share on other sites


  • Group:  Developer
  • Topic Count:  28
  • Topics Per Day:  0.01
  • Content Count:  547
  • Reputation:   270
  • Joined:  11/08/11
  • Last Seen:  

Ok thanks for clearing things up a little. I did get something wrong or had some logical issues, whatever...

So what we actually need to know is how the client comes up with the value for "nType" and where it does that. If we find this, we might be able to change the things to simply load a file with the usual monster id instead of changing it to this curious nType value. But all in all we would need a modification of the function itself and neither me nor you is able to achieve this. I can do assembler, but I do not know how to link the new function pointers correctly, since I have never done/tried something like this up to now. Maybe Ai4rei could help us out with this, if we give him enough information or even the whole function.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  7
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   189
  • Joined:  05/20/12
  • Last Seen:  

The nType is the index used in the names of the bone animation files. I think it just gets it from the file name of the GR2 model, at least it very much seems that way. For instance if you add "spider90_10.gr2" to the LUA files, it means that the model is rotated 90 degrees and uses nType/animation index 10. It also means that it will try to get the bone animation files "10_move.gr2", "10_attack.gr2" and so on from the 3dmob_bone folder when displaying the model. The problem is just that standing animation, which it needs the job ID to get the job name for.

So it first gets the nType from the job name/GR2 file name, then it "forgets" the job name and just has the nType when it needs to get the animations in this function.

nType 0-9 are used by Gravity's models, so we know when nType is 10 or above it will be a custom monster. So if we could just make it do something like

if(nType < 10)
jobId = job[nType];
else
jobId = 2500 + nType;

...instead of just the table look-up we could for instance just add a monster as "name_20.gr2" in the job name entry and ID 2520 in the LUA files. The nType = 20 would get passed to this function, which would instead of the table look-up just add the constant 2500 and end up with the right job ID. Or if you use nType 100 in the GR2 file names you would add it as mob ID 2600 in the LUA files. For Gravity mobs it would continue to use the table, since they are nType < 10. Well, that's at least one solution...

@Diconfrost: Ha ha, thanks.

  • Upvote 1
Link to comment
Share on other sites


  • Group:  Developer
  • Topic Count:  28
  • Topics Per Day:  0.01
  • Content Count:  547
  • Reputation:   270
  • Joined:  11/08/11
  • Last Seen:  

So in order to clean my head and to combine all the information we have gathered so far I have combined the C, C++ and ASM code parts and also the value informations for the variables we have cleared so far.

Additionally I found something strange in Ai4rei's post:

struct granny_animation* __cdecl C3dGrannyBoneRes::GetAnimation(int nType, int nAniIdx)
{
if( this->m_Animation[nType][nAniIdx] == NULL )
{
	g_Unknown1[0] = 0;

	if( nType >= 0 && nType <= 9 )
	{
		if( nAniIdx >=0 && nAniIdx <= 4 )
		{
			if( nAniIdx )
			{
				sprintf(g_Unknown1, "model\\3dmob\\%s", g_session.GetJobName(g_Unknown2[nType]));
			}
			else
			{
				[color=#ff0000]sprintf(g_Unknown1, "model\\3dmob\\%d_%s.gr2", nType, g_Unknown3[nAniIdx]);[/color]
			}

			if( g_Unknown1[0] && CFile::IsFileExist(g_Unknown1) )
			{
				if( nType != 1 )
				{
					this->AddBone(g_Unknown1, nType, nAniIdx);
				}

				return this->m_Animation[nType][nAniIdx];
			}
		}
	}
}
else
{
	return this->m_Animation[nType][nAniIdx];
}

return NULL;
}

Shouldn't this line refer to "model\\3dmob_bone\\%d_%s.gr2"?

After that and seeing the combined output I have come to the same conclusion as you curiosity and that is, that we have to deal with ids higher than 9 by ourselfes. So you might want to take a look at the pseudo code(C/C++ combination) source I came up with and here is the way I guess we should deal with it(I also fixed what I mentioned above):

#define CUSTOMGRANNYSTARTID 2500

int __thiscall getGrannyModel( void *this, int grannyId, int animationType ){
// C++ this reference decompiled to C is my guess here
// It is just like it is setting up the "this" pointer here
void *objSelfReference;
// some object self reference - need info about object structure
// My guess is that it is the set up modelname, which is set in the function
// readGrannyModelFromDisk after/before reading it
char *v4;
char *monster_name;

objSelfReference = this;
v4 = (char *)this + 4 * ( animationType + 4 * ( 5 * grannyId + 40 ) );
if( *(_DWORD *)v4 ){
	return *(_DWORD *)v4;
}

modelResPath = 0;
if( (unsigned int)grannyId <= 9 ){
	if( (unsigned int)animationType <= 4 ){
		if( animationType ){
			sprintf( &modelResPath, "model\\3dmob_bone\\%d_%s.gr2", grannyId, animationType[animationType] );
		}else{
			monster_name = g_session.GetJobName( grannyModelMappingId2MonsterId[grannyId] );
			sprintf( &modelResPath, "model\\3dmob\\%s", monster_name );
		}

		if( modelResPath && fileExists(&modelResPath) ){
			if( grannyId != 1 ){
				readGrannyModelFromDisk( objSelfReference, &modelResPath, grannyId, animationType );
			}

			return *(_DWORD *) v4;
		}
	}
}else{
	if( (unsigned int)animationType <= 4 ){
		if( animationType ){
			sprintf( &modelResPath, "model\\3dmob_bone\\%d_%s.gr2", grannyId, animationType[animationType] );
		}else{
			sprintf( &modelResPath, "model\\3dmob\\%d", grannyId );
		}

		if( modelResPath && fileExists(&modelResPath) ){
			if( grannyId != 1 ){
				// I hope this function doesnt make any problems
				readGrannyModelFromDisk( objSelfReference, &modelResPath, grannyId, animationType );
			}

			return *(_DWORD *) v4;
		}
	}
}

return NULL;
}

struct granny_animation* __cdecl C3dGrannyBoneRes::GetAnimation( int grannyId, int animationType ){
if( this->m_Animation[grannyId][animationType] == NULL ){
	tmpGrannyModelName[0] = 0;

	if( grannyId >= 0 && grannyId <= 9 ){
		if( animationType >= 0 && animationType <= 4 ){
			if( animationType ){
				sprintf( tmpGrannyModelName, "model\\3dmob\\%s", g_session.GetJobName( grannyModelMappingId2MonsterId[grannyId] ) );
			}else{
				sprintf( tmpGrannyModelName, "model\\3dmob_bone\\%d_%s.gr2", grannyId, animationType[animationType] );
			}

			if( tmpGrannyModelName[0] && CFile::IsFileExist( tmpGrannyModelName ) ){
				if( grannyId != 1 ){
					this->AddBone( tmpGrannyModelName, grannyId, animationType );
				}

				return this->m_Animation[grannyId][animationType];
			}
		}
	}else{
		if( animationType >= 0 && animationType <= 4 ){
			if( animationType ){
				sprintf( tmpGrannyModelName, "model\\3dmob\\%s", g_session.GetJobName( grannyModelMappingId2MonsterId[CUSTOMGRANNYSTARTID + grannyId] ) );
			}else{
				sprintf( tmpGrannyModelName, "model\\3dmob_bone\\%d_%s.gr2", grannyId, animationType[animationType] );
			}

			if( tmpGrannyModelName[0] && CFile::IsFileExist( tmpGrannyModelName ) ){
				if( grannyId != 1 ){
					this->AddBone( tmpGrannyModelName, grannyId, animationType );
				}

				return this->m_Animation[grannyId][animationType];
			}
		}
	}
}else{
	return this->m_Animation[grannyId][animationType];
}

return NULL;
}

[Edit]:

Forgot to press the attach button, also the board will not let me upload .c files... :D

granny.c.txt

Edited by Lemongrass
  • Upvote 1
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  7
  • Topics Per Day:  0.00
  • Content Count:  133
  • Reputation:   189
  • Joined:  05/20/12
  • Last Seen:  

I decided to try my luck with assembly and it seems to be working. With my amazing assembly skills I managed to come up with something like this:

L_START:
cmp esi, 10
jna L_ELSE
mov edx, esi
add edx, 9C4
jmp L_END
L_ELSE:
mov edx, job[esi*4]
L_END:
jmp L_REF ; back to function

I tested by adding the test monster with nType = 20 and job ID 2520 and everything seems to be working perfectly without having to hex the new ID into the client. Gravity mobs work as normal. So with this it should be possible add any amount of monsters with no hex editing by putting some nType > 10 in the file name, then adding the monster with job ID = 2500 + nType in npcidentity.lua.

  • Upvote 1
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  87
  • Topics Per Day:  0.02
  • Content Count:  1335
  • Reputation:   932
  • Joined:  10/26/11
  • Last Seen:  

Lemongrass, thank you for joining and help us. Curiosity, thanks for the PM I'm still reading it and understanding what you did.

Sorry to not be that helpful, I wish I could know more about dissamble. I can only express my gratitude to your interest and the amazing progress you are making here. The standing animation is finally working. This is truly amazing.

Just asking and trying to understand... I believe what are you doing for now, is, looking for a more handy way to make this change for actual RO clients? Or am I wrong?

In case you are, what are you thinking about to implement this? I just read about a patch for ShinsDiffPatcher, and that would be truly awesome.

I hope Ai4rei will take a look at this.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  8
  • Topics Per Day:  0.00
  • Content Count:  417
  • Reputation:   354
  • Joined:  11/11/11
  • Last Seen:  

Yeah, I messed up while disassembling with two things (nAniIdx TEST and the bone string). Unlike you guys I do not use IDA+HexRays, so stuff like that can slip in while processing the assembly.

Corrected version would be this:

struct granny_animation* __cdecl C3dGrannyBoneRes::GetAnimation(int nType, int nAniIdx)
{
if( this->m_Animation[nType][nAniIdx] == NULL )
{
	g_Unknown1[0] = 0;

	if( nType >= 0 && nType <= 9 )
	{
		if( nAniIdx >=0 && nAniIdx <= 4 )
		{
			if( !nAniIdx )
			{
				sprintf(g_Unknown1, "model\\3dmob\\%s", g_session.GetJobName(g_Unknown2[nType]));
			}
			else
			{
				sprintf(g_Unknown1, "model\\3dmob_bone\\%d_%s.gr2", nType, g_Unknown3[nAniIdx]);
			}

			if( g_Unknown1[0] && CFile::IsFileExist(g_Unknown1) )
			{
				if( nType != 1 )
				{// prevent guild flag from animating
					this->AddBone(g_Unknown1, nType, nAniIdx);
				}

				return this->m_Animation[nType][nAniIdx];
			}
		}
	}
}
else
{
	return this->m_Animation[nType][nAniIdx];
}

return NULL;
}

Also do not forget the space to add assembly is scarce and relocating blocks of code annoying, so let stick with the easier approach, that was suggested at first. That is, official mobs keep their current mechanics and custom mobs (nType 10+) are all saved as bones, with nAniIdx 0 (non-bone) being "stand"-bone since the client also declares the string. Thus you also do not need to maintain the nType2MobId table, just add monsters at will into data\\ and lua. Remains to figure out how the client comes up with nType; I assume with an another table which we will need to maintain.

  • Upvote 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...