Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Development

Development::Development Forum for development topics and for those interested in EQEMu development. (Not a support forum)

Reply
 
Thread Tools Display Modes
  #1  
Old 06-19-2006, 08:41 AM
unicorn97211
Sarnak
 
Join Date: May 2006
Posts: 37
Default Feign Death Reliability Fix - Pet Fix - Feign Memory Overhaul

Feign Reliability Fix

Feign was failing outside our control.

client_process.cpp

In Client::Process

Change

Code:
#ifdef REVERSE_AGGRO
	//At this point, we are still connected, everything important has taken
	//place, now check to see if anybody wants to aggro us.
	if(ret && scanarea_timer.Check()) {
		entity_list.CheckClientAggro(this);
	}
#endif
to

Code:
#ifdef REVERSE_AGGRO
	//At this point, we are still connected, everything important has taken
	//place, now check to see if anybody wants to aggro us.
	if(ret && scanarea_timer.Check()) {
		// Everhood 6/15/06 - only check prox agro if we are not feigned
		if(!feigned){
			entity_list.CheckClientAggro(this);
		}
	}
#endif
client.h

Change

Code:
inline bool    GetFeigned()	{ return(GetAppearance() != eaDead ? false : feigned); }
To

Code:
	// EverHood 6/16/06
	// this cures timing issues cuz dead animation isn't done but server side feigning is?
	inline bool    GetFeigned()	{return feigned; }
Feigned Owner Pet Aggro Fix

Now that pets don't die when we feign, this stops us from inheriting any hate the pet does while we are fiegned.

i.e. If someone trains my pet with 20 mobs and I feign, when my pet dies the mobs should return home. Pre-Fix when my pet died the mobs would begin attacking me even though I am feigned.

attack.cpp

In Mob::AddToHateList

Change

Code:
	if (owner) { // Other has a pet, add him and it
		hate_list.Add(other, hate, 0, bFrenzy, !iBuffTic);
		hate_list.Add(owner, 1, damage, false, !iBuffTic);
	}
To

Code:
	if (owner) { // Other is a pet, add him and it
		hate_list.Add(other, hate, 0, bFrenzy, !iBuffTic);
		// EverHood 6/12/06
		// Can't add a feigned owner to hate list
		if(!owner->CastToClient()->GetFeigned()){
			hate_list.Add(owner, 1, damage, false, !iBuffTic);
		}
	}
Improved Feign Memory

The current feign memory only allows an NPC to remember a single attacker and the attacker was being remembered 100% of the time. When the NPCs feign memory was being processed, the dice were thrown and the attacker got a chance to be forgotten and if he lost the roll enough times the attacker was eventually removed from feign memory. This was a problem since Necro A could aggro then feign then Necro B could aggro and feign overwriting Necro A in the feign memory allowing Necro A to get up when he shouldn't be able to yet. This did not follow live feign memory functionality in any way.

I've replaced the single slot feign memory with a feign memory list to hold multiple attackers. I've changed the code so that when you feign, if the NPC is level 35+, the dice are thrown and there is a 3 in 5 chance you will be added to the NPCs feign memory list. When the NPC processes its feign memory, any attacker on it that has stood up will be added back to that NPCs hate list. If the NPC reaches it's guard point (spawn point) it will clear all attackers from it's feign memory. If the NPC is on a grid and reaches waypoint 1 it will clear all attackers from it's feign memory.

npc.h

Change
Code:
	void	SetFeignMemory(const char* num) {feign_memory = num;}

	inline const char*    GetFeignMemory()	{ return feign_memory; }
To
Code:
	// EverHood 6/14/06
	// Mobs need to be able to remember more than one feigned attacker
	void	AddFeignMemory(Client* attacker);
	void	RemoveFromFeignMemory(Client* attacker);
	void	ClearFeignMemory();
Change
Code:
	const char*	feign_memory;
To
Code:
	// EverHood 6/14/06
	LinkedList<const char*> feign_memory_list;
npc.cpp

In NPC::Process

Change
Code:
	//Feign Death Memory
	if (forget_timer.Check() && strstr(GetFeignMemory(),"0") == NULL) {
		Client* remember_client = entity_list.GetClientByName(GetFeignMemory());
		if (remember_client != 0)
		{
			if (!remember_client->CastToClient()->GetFeigned())
			{
				AddToHateList(remember_client,1);
				SetFeignMemory("0");
				forgetchance = 0;
			}
			else if (rand()%100 <= forgetchance)
			{
				SetFeignMemory("0");
				forgetchance = 0;
			}
			else
			{
				forgetchance += 1;
			}
		}
		else
		{
			SetFeignMemory("0");
		}
	}
To
Code:
	// EverHood - 6/14/06
	// Improved Feign Death Memory
	if (forget_timer.Check()) {
		LinkedListIterator<const char*> iterator(feign_memory_list);
		iterator.Reset();
		while(iterator.MoreElements())
		{
			Client* remember_client = entity_list.GetClientByName(iterator.GetData());
			if (!remember_client->CastToClient()->GetFeigned())
			{
				AddToHateList(remember_client,1);
				iterator.RemoveCurrent();
				// Personal humor
				Emote("realizes %s wasn't really dead",remember_client->GetName());
			}
			iterator.Advance();
		}		
	}
Add
Code:
	// EverHood 6/14/06 - Feign Death memory 
	void   NPC::AddFeignMemory(Client* attacker) {
		feign_memory_list.Insert(attacker->CastToMob()->GetName());
		// Leaving this debug in is kinda interesting...
		Emote("is suspicious of %ss unexpected death.",attacker->GetName());
	}
	void   NPC::RemoveFromFeignMemory(Client* attacker){
		LinkedListIterator<const char*> iterator(feign_memory_list);
		iterator.Reset();
		while(iterator.MoreElements())
		{
			if(iterator.GetData() == attacker->GetName()){
				// Leaving this debug in is kinda interesting...
				this->CastToMob()->Emote("loses interest in %s.",attacker->GetName());
				iterator.RemoveCurrent();
			}
			iterator.Advance();
		}
	}
	void   NPC::ClearFeignMemory(){
		if(feign_memory_list.Count()>0){
			feign_memory_list.Clear();
			// Leaving this debug in is kinda interesting...
			this->CastToMob()->Emote("is no longer suspicious of the dead.");
		}
	}
entity.cpp

Change
Code:
void EntityList::ClearFeignAggro(Mob* targ)
{
	LinkedListIterator<NPC*> iterator(npc_list);
	iterator.Reset();
	while(iterator.MoreElements())
	{
		if (iterator.GetData()->CastToNPC()->CheckAggro(targ))
		{
			iterator.GetData()->CastToNPC()->RemoveFromHateList(targ);
			if (iterator.GetData()->CastToMob()->GetLevel() >= 35)
				iterator.GetData()->CastToNPC()->SetFeignMemory(targ->CastToMob()->GetName());
		}
		iterator.Advance();
	}
}
To
Code:
void EntityList::ClearFeignAggro(Mob* targ)
{
	LinkedListIterator<NPC*> iterator(npc_list);
	iterator.Reset();
	while(iterator.MoreElements())
	{
		if (iterator.GetData()->CastToNPC()->CheckAggro(targ))
		{
			iterator.GetData()->CastToNPC()->RemoveFromHateList(targ);
			if (iterator.GetData()->CastToMob()->GetLevel() >= 35)
				// EverHood 6/14/06
				// the mob that hated us is 35+ so 3 outta 5 chance 
				// he adds us to its feign memory. 
				if(((float)rand()/RAND_MAX)*100 < 40){
					iterator.GetData()->CastToNPC()->AddFeignMemory(targ->CastToClient());
				}
		}
		iterator.Advance();
	}
}
MobAI.cpp

In NPC::AI_DoMovement

Change
Code:
				{	// are we there yet? then stop
					mlog(AI__WAYPOINTS, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid());
					SetWaypointPause();
					SetAppearance(eaStanding, false);
					SetMoving(false);
					SendPosition();
				}
To
Code:
				{	// are we there yet? then stop
					mlog(AI__WAYPOINTS, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid());
					SetWaypointPause();
					SetAppearance(eaStanding, false);
					SetMoving(false);
					SendPosition();
					// EverHood - wipe feign memory since we reached our first waypoint
					if(cur_wp==1){
						ClearFeignMemory();
					}
				}
Change
Code:
			mlog(AI__WAYPOINTS, "Reached guard point (%.3f,%.3f,%.3f)", guard_x, guard_y, guard_z);
			moved=false;
To
Code:
			mlog(AI__WAYPOINTS, "Reached guard point (%.3f,%.3f,%.3f)", guard_x, guard_y, guard_z);
			// EverHood 6/18/06- we are home so clear our feign memory
			ClearFeignMemory();
			moved=false;
Feign Memory 2 Minute Wipe

I saw no code to handle this. When a client is feigned for 2 minutes, clear client off all feign memory lists in zone and let client know it is clear to stand up.


Client.h

Change
Code:
	Timer	fishing_timer;
#ifdef REVERSE_AGGRO
	Timer	scanarea_timer;
#endif
To
Code:
	Timer	fishing_timer;
	// EverHood 6/16/06
	// our 2 min everybody forgets you timer
	Timer	forget_timer;
#ifdef REVERSE_AGGRO
	Timer	scanarea_timer;
#endif
client.cpp

In Client::Client

Change
Code:
	fishing_timer(8000),
#ifdef REVERSE_AGGRO
	scanarea_timer(AIClientScanarea_delay),
#endif
To
Code:
	fishing_timer(8000),
	// EverHood 6/16/06
	forget_timer(120000),
#ifdef REVERSE_AGGRO
	scanarea_timer(AIClientScanarea_delay),
#endif
client_process.cpp

In Client::Process

Change
Code:
		OnDisconnect(true);
	}
	
	
	return ret;
}
To
Code:
		OnDisconnect(true);
	}
	// EverHood Feign Death 2 minutes and zone forgets you
	if (forget_timer.Check()) {
		forget_timer.Disable();
		entity_list.ClearZoneFeignAggro(this);
		Message(0,"Your enemies have forgotten your aggressions.");
	}
	
	return ret;
}
entity.h

Change
Code:
	void	ClearFeignAggro(Mob* targ);
	bool	Fighting(Mob* targ);
to
Code:
	void	ClearFeignAggro(Mob* targ);
	// Everhood 6/17/06
	void	ClearZoneFeignAggro(Client* targ);
	bool	Fighting(Mob* targ);
entity.cpp

Add
Code:
// EverHood 6/17/06
void EntityList::ClearZoneFeignAggro(Client* targ)
{
	LinkedListIterator<NPC*> iterator(npc_list);
	iterator.Reset();
	while(iterator.MoreElements())
	{
		iterator.GetData()->RemoveFromFeignMemory(targ);
		iterator.Advance();
	}
}
Reply With Quote
  #2  
Old 06-19-2006, 08:43 AM
unicorn97211
Sarnak
 
Join Date: May 2006
Posts: 37
Default Continued...

At the moment there are a couple issues left...

I can't be sure, but I didn't see any code to handle feign death actually failing. I saw nothing to generate the "player has fallen to the ground" message to a client which is what you see on live when a feign has failed. As it stands it appears feign will succeed 100% of the time. Feign failure chance should be based on skill for monks and for necro/sk it should be based on which feign death spell they cast, the higher level the feign spell, the lower the chance of failure.

When a mob is snared and I feign, he is able to walk. On live when a mob is snared with any necro snare Dooming Darkness or higher, an NPC cannot walk. The NPC can run at the reduced speed but the reduction in walkspeed from the snare should put the NPCs walkspeed at 0 or less leaving the NPC in essence rooted. I don't know if this is because walkspeeds in the database are too high or if the snare speed reduction is not high enough or if the speed reduction is not being applied to walkspeed etc. Perhaps FatherNitWit can point me in the right direction

Last edited by unicorn97211; 06-19-2006 at 04:51 PM..
Reply With Quote
  #3  
Old 06-19-2006, 12:23 PM
fathernitwit
Developer
 
Join Date: Jul 2004
Posts: 773
Default

please make a unified diff of this so I can look at it for inclusion.

feign should be based on skill when it is not cast by a spell, see:
Client::Handle_OP_FeignDeath

as for snare, the spells apply a percent decrease in movement speed:
http://lucy.allakhazam.com/spell.htm...52&source=Live
and as such, will never result in a true 0 walk speed... Can you provide a 3rd party source to back up your claim of no walking when snared?
Reply With Quote
  #4  
Old 06-19-2006, 01:07 PM
mattmeck
Guest
 
Posts: n/a
Default

The only way a necro's snare acts as a root is when the mob is super low on HP 10% or less. Otherwise it went to the redused percentage.
Reply With Quote
  #5  
Old 06-19-2006, 02:39 PM
rojadruid
Discordant
 
Join Date: May 2005
Location: Smith Falls, Ontario, Canada
Posts: 283
Default

Quote:
Originally Posted by mattmeck
The only way a necro's snare acts as a root is when the mob is super low on HP 10% or less. Otherwise it went to the redused percentage.
That is correct I play a necro on live. when the mob gets to 10% or lower then it stops moving.
__________________
Rojadruid

Innoruuk Server [legit]
Server Admin.
Server Status: UP
Reply With Quote
  #6  
Old 06-19-2006, 05:31 PM
unicorn97211
Sarnak
 
Join Date: May 2006
Posts: 37
Default

Quote:
Originally Posted by mattmeck
The only way a necro's snare acts as a root is when the mob is super low on HP 10% or less. Otherwise it went to the redused percentage.
This is true during combat however I think you are mistaking runspeed for walkspeed. Mobs never walk during combat they run to you. When fleeing they don't walk away, they run away from you. When running away from you unsnared they do suffer a negative runspeed and slow to a near walk which is probably a switch from running to walking at low hps.

What I am referring to is when a mob is snared during combat but then loses all hate and attempts to return home (or to its grid) at which point the mob is walking not running. Therefore walkspeed - snare reduction = stopped mob.

This is very very easy to reproduce on live. Log on a necro that is high enough level to use at least Dooming Darkness (lower level snares don't mitigate speed enough). Cast Dooming on any mob and snare it. After it walks to you (mob is actually running but snare reduces runspeed to a walk) feign death. You will notice that the mob will stand there NOT moving an inch until Dooming wears off at which point it will walk NOT run home. This happens with any mob including those buffed with SoW and uber fast running mobs in OOW like Ukun.

Also I would think just about any experienced player knows that groups have a designated snarer in groups to stop runners because a snared fleeing mob can't move. So again this is walkspeed being mitigated to 0.

This is key to split pulling. You see two mobs standing next to each other. You snare one and they both come at you, one running, one walking. You feign and the unsnared mob walks back home while the snared mob can't move and stays put. Once the unsnared mob reaches home and forgets all about you, you stand up, the snared mob remembers you either via feign memory or a tick of dmg from the snare and resumes it's attack only now his buddy is back home and out of aggro range leaving you with a single pull.

Any experienced Necro or SK who has done any split pulling can verify this.

On a side note, how many hps a mob has to get down to before it flees isn't a fixed percentage per say. My observation on live is that a mob will flee when the mobs hps reach a certain ratio to your own. I noticed this because while soloing with my necro, if I was buffed with conviction mobs would begin to flee with a higher amount of hps than they would if I were only self buffed.

If I were still subscribed to live I would do a series of screenshots to demonstrate both these points but for now I'll rely on the experienced necros in these forums to back me up since it's doubtful anyone here knows of the legendary necro Bodob of Club Fu on Bristlebane haha.

I started reading up on how to use TortoiseCVS to do a diff so hopefully I'll be able to post one shortly for Fathernitwit to review.
Reply With Quote
  #7  
Old 06-19-2006, 05:40 PM
unicorn97211
Sarnak
 
Join Date: May 2006
Posts: 37
Default

Just hypothesizing here, but perhaps on live the amount snare mitigates runspeed is applied to walkspeed as follows:

Mob has a runspeed of 100 and walkspeed of 50 (just to make the math simple)

Snare is 50%. This would reduce his runspeed to 50 and as it sits now would reduce his walkspeed to 25.

My thinking is that because snare reduced runspeed by a value of 50, it also reduces walkspeed by a value of 50 therefore if the mob were to attemp to walk while snared his walkspeed would be 0.

Just trying to figure out how a percentage reduction could cause the behavior observed on live.
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 10:17 AM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3