View Single Post
  #1  
Old 01-20-2004, 04:27 AM
Mongrel
Hill Giant
 
Join Date: Jul 2003
Location: Germany
Posts: 232
Default Fix: Invisibility and IVU (Huge! Requires DB change)

This is a pretty big update, so I probably made a mistake somewhere in this post. I'll update this accordingly.

Note to the devs: I'd love to post this in a better way, but I don't know how to use diffs. It's a large update, sorry if it's confusing at some points.

Another note: Some of the linebreaks might be wrong (they shouldn't though). I had to add them to make this somewhat readable.

@Trump: If this is too large for you to merge (not meant as an insult!), maybe you could send me your cvs version and I'll merge it in myself.

To make invis and IVU work we have to change the DB structure a bit and modify the code for zone.exe.

DB changes:
First we need to change our NPC Types table. We will add three columns: see_invis, see_invis_undead and undead. All three are pretty self-explanatory, however they're not linked with each other in any way shape or form. That way it's easy to make high level undeads that can see through Invis vs. Undead or boss mobs that see through normal Invisibility.

Here are the SQL commands to change the DB (copy&paste the commands below into a text file):

Code:
-- Add a see_invis, see_invis_undead and an undead column
ALTER TABLE npc_types ADD COLUMN 
(
see_invis TINYINT NOT NULL default '0',
see_invis_undead TINYINT NOT NULL default '1'
);
This will make all our mobs "alive" and able to see through IVU, but not through invis.

Here are SQL commands to make a few npcs undead:

Code:
-- Change race 14 (Werewolf) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=14;

-- Change race 27 (Froglok Ghoul) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=27;

-- Change race 32 (Ghost) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=32;

-- Change race 33 (Ghoul) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=33;

-- Change race 45 (Demi Lich) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=45;

-- Change race 60 (Skeleton) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=60;

-- Change race 65 (Vampire) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=65;

-- Change race 70 (Zombie) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=70;

-- Change race 85 (Spectre) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=85;

-- Change race 98 (Elf Vampire) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=98;

-- Change race 117 (Ghost Dwarf) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=117;

-- Change race 118 (Erudite Ghost) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=118;

-- Change race 122 (Dragon Skeleton) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=122;

-- Change race 146 (Spectral Sarnak) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=146;

-- Change race 147 (Spectral Iksar) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=147;

-- Change race 155 (Sarnak Skeleton) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=155;

-- Change race 161 (Iksar Skeleton) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=161;

-- Change race 174 (Cold Spectre) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=174;

-- Change race 196 (Ghost Dragon) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=196;

-- Change race 208 (Vampyre) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=208;

-- Change race 234 (Vah Shir Skeleton) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=234;

-- Change race 282 (Skeletal Horse) to undead
UPDATE npc_types SET see_invis=1, see_invis_undead=0 WHERE race=282;
I'm sure I missed a few PoP undeads, but I never played PoP on live, so I don't really know those races. Feel free to add a few on your own.
That was easy, wasn't it? Gets a lot more difficult now ...

Now to the code changes:
In zone/zonedump.h add the following two lines to the structure NPCType:

Code:
	bool	see_invis;			// Mongrel: See Invis flag added
	bool	see_invis_undead;	// Mongrel: See Invis flag added
At this point you may have to rebuild EQEmuShareMem. While I was debugging this I found that sizeof(NPCType) returned the wrong value. Recompiling fixed this.

In common/database.cpp search for the following line four times. The first appearance is right at the top of the file, we don't want that one. The second and third appearances is about items and doors, we don't need those. The fourth appearance is the one we need (about npc_types).
Code:
#ifdef SHAREMEM
Inside this #ifdef block you will find a function named bool Database:BLoadNPCTypes(). This function is used to load the NPC Types from the DB when you're using EQEmuShareMem. I doubt anyone is using the non-sharemem version, so won't go into that now.

Inside bool Database:BLoadNPCTypes() look for the following lines:
Code:
			NPCType tmpNPCType;
			// support the old database too for a while...
        		// neotokyo: added aggroradius
        		// neotokyo: added bodytype
			MakeAnyLenString(&query, "SELECT id,name,level,race,class,hp,gender,texture,helmtexture,size,loottable_id, merchant_id, banish, mindmg, maxdmg, npcspecialattks, npc_spells_id, d_meele_texture1,d_meele_texture2, walkspeed, runspeed,fixedz,hp_regen_rate,mana_regen_rate,aggroradius,bodytype,npc_faction_id,face FROM npc_types");//WHERE zone='%s'", zone_name
			if (RunQuery(query, strlen(query), errbuf, &result))
			{
Replace it with:
Code:
			NPCType tmpNPCType;
			// support the old database too for a while...
        		// neotokyo: added aggroradius
        		// neotokyo: added bodytype
			MakeAnyLenString(&query, "SELECT id,name,level,race,class,hp,gender,texture,helmtexture,size,loottable_id, merchant_id, banish, mindmg, maxdmg, npcspecialattks, npc_spells_id, d_meele_texture1,d_meele_texture2, walkspeed, runspeed,fixedz,hp_regen_rate,mana_regen_rate,aggroradius,
bodytype,npc_faction_id,face,see_invis,see_invis_undead FROM npc_types");//WHERE zone='%s'", zone_name
			if (RunQuery(query, strlen(query), errbuf, &result))
			{
I have added "see_invis,see_invis_undead" to the end of the query.

Then change this:
Code:
					tmpNPCType.luclinface=atoi(row[27]);
                			// set defaultvalue for aggroradius
					if (tmpNPCType.aggroradius <= 0)
                    			    tmpNPCType.aggroradius = 70;

					//tmpNPCType.ipc = atoi(row[27]);
to this:
Code:
					tmpNPCType.luclinface=atoi(row[27]);
                			// set defaultvalue for aggroradius
					if (tmpNPCType.aggroradius <= 0)
                    			    tmpNPCType.aggroradius = 70;

					tmpNPCType.see_invis = atoi(row[28]);
					tmpNPCType.see_invis_undead = atoi(row[29]);
							//tmpNPCType.ipc = atoi(row[27]);
At this point the NPC Types have been updated, however mobs, when spawned, do not know about this yet. That's what we'll do next.

In zone/mob.h find the constructor of the class "mob"
Code:
	Mob(const char*   in_name,
	    const char*   in_lastname,
	    sint32  in_cur_hp,
	    sint32  in_max_hp,

// Many more lines of code here ... skipping ...

	    int16   in_d_meele_texture1,
	    int16   in_d_meele_texture2
	);
	virtual ~Mob();
add the following two lines to the end of the constructor:
Code:
		int8	in_see_invis,
		int8	in_see_invis_undead
The result should look like this:
Code:
	Mob(const char*   in_name,
	    const char*   in_lastname,
	    sint32  in_cur_hp,
	    sint32  in_max_hp,

// Many more lines of code here ... skipping ...

	    int16   in_d_meele_texture1,
	    int16   in_d_meele_texture2,
		int8	in_see_invis,
		int8	in_see_invis_undead
	);
	virtual ~Mob();
Do not forget to add the comma after "in_d_meele_texture2"

Now search for the following lines in the same file:
Code:
	inline bool SeeInvisible() { return false; }
	inline bool SeeInvisibleUndead() { return true; }
	
	bool IsInvisible(Mob* other = 0) {
	// TC - removing until SeeInvisible is implemented.
	//if (other && other->SeeInvisible() && !sneaking) { return false; }
		if (other && !sneaking) { return false; }
	   if (sneaking && BehindMob(other, GetX(), GetY()) ){ return true; }
       else { return invisible; }
    }
Replace it with:
Code:
	inline bool SeeInvisible() { return see_invis; }				// Mongrel: Now using the flags
	inline bool SeeInvisibleUndead() { return see_invis_undead; }	// Mongrel: Now using the flags
	
	bool IsInvisible(Mob* other = 0) 
	{
	// TC - removing until SeeInvisible is implemented.
	// Mongrel: Reimplementing see invis
		if (other && invisible && !other->SeeInvisible()) 
		{ 
			return true; 
		}
		if (other && invisible_undead && !other->SeeInvisibleUndead()) 
		{ 
			return true; 
		}
		if (other && !sneaking) 
		{ 
			return false; 
		}
		if (sneaking && BehindMob(other, GetX(), GetY()) )
		{ 
			return true; 
		}
		else 
		{ 
			return invisible; 
		}
	}
Now find this (still in zone/mob.h)
Code:
	bool	invulnerable;
	bool	invisible, invisible_undead, sneaking;
	void	Spin();
	void	Kill();
Replace with:
Code:
	bool	invulnerable;
	bool	invisible, invisible_undead, sneaking;
	bool	see_invis, see_invis_undead;	// Mongrel: See Invis and See Invis vs. Undead
	void	Spin();
	void	Kill();
Now open zone/mob.cpp and apply the changes we just made to the constructor:
Change
Code:
		 int8	in_luclinface, // and beard
		 int8	in_aa_title,
		 float	in_fixed_z,
		 int16	in_d_meele_texture1,
		 int16	in_d_meele_texture2
		 )
{
to

Code:
		 int8	in_luclinface, // and beard
		 int8	in_aa_title,
		 float	in_fixed_z,
		 int16	in_d_meele_texture1,
		 int16	in_d_meele_texture2,
		 int8	in_see_invis,
		 int8	in_see_invis_undead
		 )
{
At the end of the constructor change:
Code:
	pStandingPetOrder = SPO_Follow;
	
	// Bind wound
	bindwound_timer = new Timer(10000);
	bindwound_timer->Disable();
	bindwound_target = 0;
to

Code:
	pStandingPetOrder = SPO_Follow;

	see_invis = in_see_invis;
	see_invis_undead = in_see_invis_undead;
	
	// Bind wound
	bindwound_timer = new Timer(10000);
	bindwound_timer->Disable();
	bindwound_target = 0;
Now open zone/npc.cpp and search for the following:
Code:
	  d->fixedZ,
	  d->d_meele_texture1,
	  d->d_meele_texture2)
{
	Mob* mob = entity_list.GetMob(name);
Replace with:
Code:
	  d->fixedZ,
	  d->d_meele_texture1,
	  d->d_meele_texture2,
	  d->see_invis,
	  d->see_invis_undead
	  )
{
	Mob* mob = entity_list.GetMob(name);
Open zone/client.cpp and find:
Code:
	1, // fixedz
	0, // standart text1
	0 // standart text2
	)
{
	for(int cf=0;cf<21;cf++)
Replace with:

Code:
	1, // fixedz
	0, // standart text1
	0, // standart text2
	0, // see_invis
	0 // see_invis_undead
	)
{
	for(int cf=0;cf<21;cf++)
Open zone/PlayerCorpse.cpp and search for:
Code:
// To be used on NPC death and ZoneStateLoad
Corpse::Corpse(NPC* in_npc, ItemList** in_itemlist, int32 in_npctypeid, NPCType** in_npctypedata, int32 in_decaytime)
 : Mob("Unnamed_Corpse","",0,0,in_npc->GetGender(),in_npc->GetRace(),in_npc->GetClass(),0//bodytype added
       ,in_npc->GetDeity(),in_npc->GetLevel(),in_npc->GetNPCTypeID(),0,in_npc->GetSize(),0,0,
in_npc->GetHeading(),in_npc->GetX(),in_npc->GetY(),in_npc->GetZ(),0,0,in_npc->GetTexture(),
in_npc->GetHelmTexture(),0,0,0,0,0,0,0,0,0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,1,0,0)
{
Replace with:
Code:
// To be used on NPC death and ZoneStateLoad
Corpse::Corpse(NPC* in_npc, ItemList** in_itemlist, int32 in_npctypeid, NPCType** in_npctypedata, int32 in_decaytime)
 : Mob("Unnamed_Corpse","",0,0,in_npc->GetGender(),in_npc->GetRace(),in_npc->GetClass(),0//bodytype added
       ,in_npc->GetDeity(),in_npc->GetLevel(),in_npc->GetNPCTypeID(),0,in_npc->GetSize(),0,0,
in_npc->GetHeading(),in_npc->GetX(),in_npc->GetY(),in_npc->GetZ(),0,0,in_npc->GetTexture(),
in_npc->GetHelmTexture(),0,0,0,0,0,0,0,0,0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,1,0,0,0,0)
{
This just adds two zeros at the end for the two new flags

Repeat with
Code:
// To be used on PC death
Corpse::Corpse(Client* client, PlayerProfile_Struct* pp, sint32 in_rezexp, int8 iCorpseLevel)
 : Mob("Unnamed_Corpse","",0,0,client->GetGender(),client->GetRace(),client->GetClass(), 0, // bodytype added
        client->GetDeity(),client->GetLevel(),0,0, client->GetSize(), 0, 0,client->GetHeading(),client->GetX(),client->GetY(),client->GetZ(),0,0,client->GetTexture(),
client->GetHelmTexture(),0,0,0,0,0,0,0,0,0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,1,0,0)
{
change to:
Code:
// To be used on PC death
Corpse::Corpse(Client* client, PlayerProfile_Struct* pp, sint32 in_rezexp, int8 iCorpseLevel)
 : Mob("Unnamed_Corpse","",0,0,client->GetGender(),client->GetRace(),client->GetClass(), 0, // bodytype added
        client->GetDeity(),client->GetLevel(),0,0, client->GetSize(), 0, 0,client->GetHeading(),client->GetX(),client->GetY(),client->GetZ(),0,0,client->GetTexture(),
client->GetHelmTexture(),0,0,0,0,0,0,0,0,0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,1,0,0,0,0)
{
and again ... find:
Code:
// To be called from LoadFromDBData
Corpse::Corpse(int32 in_dbid, int32 in_charid, char* in_charname, ItemList* in_itemlist, int32 in_copper, int32 in_silver, int32 in_gold, int32 in_plat, float in_x, float in_y, float in_z, float in_heading, float in_size, int8 in_gender, int16 in_race, int8 in_class, int8 in_deity, int8 in_level, int8 in_texture, int8 in_helmtexture,int32 in_rezexp)
 : Mob("Unnamed_Corpse","",0,0,in_gender, in_race, in_class, 0, in_deity, in_level,0,0, in_size, 0, 0, in_heading, in_x, in_y, in_z,0,0,in_texture,in_helmtexture,0,0,0,0,0,0,0,0,0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,1,0,0)
{
change to:
Code:
// To be called from LoadFromDBData
Corpse::Corpse(int32 in_dbid, int32 in_charid, char* in_charname, ItemList* in_itemlist, int32 in_copper, int32 in_silver, int32 in_gold, int32 in_plat, float in_x, float in_y, float in_z, float in_heading, float in_size, int8 in_gender, int16 in_race, int8 in_class, int8 in_deity, int8 in_level, int8 in_texture, int8 in_helmtexture,int32 in_rezexp)
 : Mob("Unnamed_Corpse","",0,0,in_gender, in_race, in_class, 0, in_deity, in_level,0,0, in_size, 0, 0, in_heading, in_x, in_y, in_z,0,0,in_texture,in_helmtexture,0,0,0,0,0,0,0,0,0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,1,0,0,0,0)
{
Ok, we're almost done now. Open zone/spells.cpp. Function bool Mob::SpellEffect():

Search for "case SE_InvisVsUndead"
Replace
Code:
			case SE_InvisVsUndead: {
#ifdef SPELL_EFFECT_SPAM
				Message(0, "Effect #%i: You cast an Invis Vs Undead spell.", i);
#endif
				Mob *p = this->GetPet();
				if (p)
with
Code:
			case SE_InvisVsUndead: {
#ifdef SPELL_EFFECT_SPAM
				Message(0, "Effect #%i: You cast an Invis Vs Undead spell.", i);
#endif
				invisible_undead = true;
				Mob *p = this->GetPet();
				if (p)
In function void Mob::BuffFadeBySlot() replace
Code:
			case SE_Invisibility: {
				invisible = false;
				break;
			}
with
Code:
			case SE_Invisibility: {
				invisible = false;
				break;
			}
			case SE_InvisVsUndead: {
				invisible_undead = false;
				break;
			}
I've tested this quite a lot and it works. As I already mentioned at the beginning of this post, I might have forgotten something somewhere.

Well, that's it. It could be done in an easier way, but doing it like above makes invis/ivu very flexible.

Edit: Rewrote some lines. They weren't exactly wrong, but sometimes it wasn't clear what I meant.
Reply With Quote