Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Server Code Submissions

Reply
 
Thread Tools Display Modes
  #1  
Old 01-23-2008, 01:03 PM
AiliaMorisato
Sarnak
 
Join Date: Sep 2005
Posts: 34
Default multiple fixes to common damage and death

common damage from attack.cpp
Changes:
Moved death and other checks to the bottom to allow proper damage message generation in the case of the damage still killing the mob.
Made hits that dealt no damage to not interrupt spells.

Code:
got knocked down to 2nd post due to length
next, fixing the death struct, need to update all instances of it to my knowledge.
Code:
struct Death_Struct
{
/*000*/	int32	spawn_id;
/*004*/	int32	killer_id;
/*008*/	int32	corpseid;	// was corpseid
/*012*/	int32	bindzoneid;	// was type -- was attack_skill
/*016*/	int32	spell_id;
/*020*/ int32	attack_skill;	//was bindzoneid -- switched by Ailia
/*024*/	int32	damage;
/*028*/	int32	unknown028;
};
now preventing the client from displaying damage message from the death packet it was sent since the server is generating the damage message instead.

Code:
void NPC::Death(Mob* other, sint32 damage, int16 spell, SkillType attack_skill) {

	mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", other->GetName(), damage, spell, attack_skill);
	
	if (this->IsEngaged())
	{
		zone->DelAggroMob();
#if EQDEBUG >= 11
		LogFile->write(EQEMuLog::Debug,"NPC::Death() Mobs currently Aggro %i", zone->MobsAggroCount());
#endif
	}
	SetHP(0);
	SetPet(0);
	Mob* killer = GetHateDamageTop(this);
	
	entity_list.RemoveFromTargets(this);

	if(p_depop == true)
		return;

	BuffFadeAll();
	
	EQApplicationPacket* app= new EQApplicationPacket(OP_Death,sizeof(Death_Struct));
	Death_Struct* d = (Death_Struct*)app->pBuffer;
	d->spawn_id = GetID();
	d->killer_id = other ? other->GetID() : 0;
//	d->unknown12 = 1;
	d->bindzoneid = 0;
	d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell;
	d->attack_skill = 231; // prevent client from displaying extra hit message
	d->damage = damage;
	app->priority = 6;
	entity_list.QueueClients(other, app, false);
and again for client death
Code:
void Client::Death(Mob* other, sint32 damage, int16 spell, SkillType attack_skill)
{
	if(dead)
		return;	//cant die more than once...
	int exploss;

	mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", other->GetName(), damage, spell, attack_skill);
	
	//
	// #1: Send death packet to everyone
	//

	if(!spell) spell = SPELL_UNKNOWN;
	
	SendLogoutPackets();
	
	//make our become corpse packet, and queue to ourself before OP_Death.
	EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct));
	BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer;
	bc->spawn_id = GetID();
	bc->x = GetX();
	bc->y = GetY();
	bc->z = GetZ();
	QueuePacket(&app2);
	
	// make death packet
	EQApplicationPacket app(OP_Death, sizeof(Death_Struct));
	Death_Struct* d = (Death_Struct*)app.pBuffer;
	d->spawn_id = GetID();
	d->killer_id = other ? other->GetID() : 0;
	//d->unknown12 = 1;
	d->bindzoneid = m_pp.binds[0].zoneId;
	d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell;
	d->attack_skill = 231;
	d->damage = damage;
	app.priority = 6;
	entity_list.QueueClients(this, &app);
Reply With Quote
  #2  
Old 01-23-2008, 01:04 PM
AiliaMorisato
Sarnak
 
Join Date: Sep 2005
Posts: 34
Default

Code:
void Mob::CommonDamage(Mob* attacker, sint32 &damage, const int16 spell_id, const SkillType skill_used, bool &avoidable, const sint8 buffslot, const bool iBuffTic) {
	
	mlog(COMBAT__HITS, "Applying damage %d done by %s with skill %d and spell %d, avoidable? %s, is %sa buff tic in slot %d",
		damage, attacker?attacker->GetName():"NOBODY", skill_used, spell_id, avoidable?"yes":"no", iBuffTic?"":"not ", buffslot);
	
	if (GetInvul() || DivineAura()) {
		mlog(COMBAT__DAMAGE, "Avoiding %d damage due to invulnerability.", damage);
		damage = -5;
	}
	
	if( spell_id != SPELL_UNKNOWN || attacker == NULL )
		avoidable = false;
	
    // only apply DS if physical damage (no spell damage)
    // damage shield calls this function with spell_id set, so its unavoidable
	if (attacker && damage > 0 && spell_id == SPELL_UNKNOWN) {
		this->DamageShield(attacker);
		for(int bs = 0; bs < BUFF_COUNT; bs++){
			if(buffs[bs].numhits > 0 && !IsDiscipline(buffs[bs].spellid)){
				if(buffs[bs].numhits == 1){
					BuffFadeBySlot(bs, true);
				}
				else{
					buffs[bs].numhits--;
				}
			}
		}		
	}
	
	if(attacker){
		if(attacker->IsClient()){
			if(!attacker->CastToClient()->GetFeigned())
				AddToHateList(attacker, 0, damage, true, false, iBuffTic);
		}
		else
			AddToHateList(attacker, 0, damage, true, false, iBuffTic);
	}
    
	if(damage > 0) {
		//if there is some damage being done and theres an attacker involved
		if(attacker) {
			if(spell_id == SPELL_HARM_TOUCH2 && attacker->IsClient() && attacker->CastToClient()->CheckAAEffect(aaEffectLeechTouch)){
				int healed = damage;
				healed = attacker->GetActSpellHealing(spell_id, healed);
				attacker->HealDamage(healed);
				entity_list.MessageClose(this, true, 300, MT_Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName() );
				attacker->CastToClient()->DisableAAEffect(aaEffectLeechTouch);
			}

			// if spell is lifetap add hp to the caster
			if (spell_id != SPELL_UNKNOWN && IsLifetapSpell( spell_id )) {
				int healed = damage;
				healed = attacker->GetActSpellHealing(spell_id, healed);				
				mlog(COMBAT__DAMAGE, "Applying lifetap heal of %d to %s", healed, attacker->GetName());
				attacker->HealDamage(healed);
				
				//we used to do a message to the client, but its gone now.
				// emote goes with every one ... even npcs
				entity_list.MessageClose(this, true, 300, MT_Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName() );
			}
			
			// if we got a pet, thats not already fighting something send it into battle
			Mob *pet = GetPet();
			if (pet && !pet->IsFamiliar() && !pet->SpecAttacks[IMMUNE_AGGRO] && !pet->IsEngaged() && attacker != this) 
			{
				mlog(PETS__AGGRO, "Sending pet %s into battle due to attack.", pet->GetName());
				pet->AddToHateList(attacker, 1);
				pet->SetTarget(attacker);
				Message_StringID(10, PET_ATTACKING, pet->GetCleanName(), attacker->GetCleanName());
			}			
		}	//end `if there is some damage being done and theres anattacker person involved`
	
		//see if any runes want to reduce this damage
		if(spell_id == SPELL_UNKNOWN) {
			damage = ReduceDamage(damage);
			mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage);
		} else {
			sint32 origdmg = damage;
			damage = ReduceMagicalDamage(damage);
			mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage);
			if (origdmg != damage && attacker && attacker->IsClient()) {
				if(attacker->CastToClient()->GetFilter(FILTER_DAMAGESHIELD) != FilterHide)
					attacker->Message(15, "The Spellshield absorbed %d of %d points of damage", origdmg - damage, origdmg);
			}
		}
		

	if(IsClient() && CastToClient()->sneaking){
		CastToClient()->sneaking = false;
		SendAppearancePacket(AT_Sneak, 0);
	}
	if(attacker && attacker->IsClient() && attacker->CastToClient()->sneaking){
		attacker->CastToClient()->sneaking = false;
		attacker->SendAppearancePacket(AT_Sneak, 0);
	}
		//final damage has been determined.
	}	//end `if damage was done`
Reply With Quote
  #3  
Old 01-23-2008, 01:05 PM
AiliaMorisato
Sarnak
 
Join Date: Sep 2005
Posts: 34
Default

and the rest
Code:
    //send damage packet...
   	if(!iBuffTic) { //buff ticks do not send damage, instead they just call SendHPUpdate(), which is done below
		EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
		CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer;
		a->target = GetID();
		if (attacker == NULL)
			a->source = 0;
		else if (attacker->IsClient() && attacker->CastToClient()->GMHideMe())
			a->source = 0;
		else
			a->source = attacker->GetID();
	    a->type = SkillDamageTypes[skill_used]; // was 0x1c
		a->damage = damage;
//		if (attack_skill != 231)
//			a->spellid = SPELL_UNKNOWN;
//		else
			a->spellid = spell_id;
		
		//Note: if players can become pets, they will not receive damage messages of their own
		//this was done to simplify the code here (since we can only effectively skip one mob on queue)
		eqFilterType filter;
		Mob *skip = attacker;
		if(attacker && attacker->GetOwnerID()) {
			//attacker is a pet, let pet owners see their pet's damage
			Mob* owner = attacker->GetOwner();
			if (owner && owner->IsClient()) {
				if ((spell_id != SPELL_UNKNOWN) && damage>0) {
					//special crap for spell damage, looks hackish to me
					char val1[20]={0};
					owner->Message_StringID(4,OTHER_HIT_NONMELEE,GetCleanName(),ConvertArray(damage,val1));
			    } else {
			    	if(damage > 0) {
						if(spell_id != SPELL_UNKNOWN)
							filter = iBuffTic ? FilterDOT : FilterSpellDamage;
						else
							filter = FILTER_MYPETHITS;
					} else if(damage == -5)
						filter = FilterNone;	//cant filter invulnerable
					else
						filter = FILTER_MYPETMISSES;
					owner->CastToClient()->QueuePacket(outapp,true,CLIENT_CONNECTED,filter);
				}
			}
			skip = owner;
		} else {
			//attacker is not a pet, send to the attacker
			
			//if the attacker is a client, try them with the correct filter
			if(attacker && attacker->IsClient()) {
				if ((spell_id != SPELL_UNKNOWN) && damage>0) {
					//special crap for spell damage, looks hackish to me
					char val1[20]={0};
					attacker->Message_StringID(4,OTHER_HIT_NONMELEE,GetCleanName(),ConvertArray(damage,val1));
			    } else {
			    	if(damage > 0) {
						if(spell_id != SPELL_UNKNOWN)
							filter = iBuffTic ? FilterDOT : FilterSpellDamage;
						else
							filter = FilterNone;	//cant filter our own hits
					} else if(damage == -5)
						filter = FilterNone;	//cant filter invulnerable
					else
						filter = FILTER_MYMISSES;
					attacker->CastToClient()->QueuePacket(outapp, true, CLIENT_CONNECTED, filter);
				}
			}
			skip = attacker;
		}
		
		//send damage to all clients around except the specified skip mob (attacker or the attacker's owner) and ourself
		if(damage > 0) {
			if(spell_id != SPELL_UNKNOWN)
				filter = iBuffTic ? FilterDOT : FilterSpellDamage;
			else
				filter = FILTER_OTHERHITS;
		} else if(damage == -5)
			filter = FilterNone;	//cant filter invulnerable
		else
			filter = FILTER_OTHERMISSES;
		//make attacker (the attacker) send the packet so we can skip them and the owner
		//this call will send the packet to `this` as well (using the wrong filter) (will not happen until PC charm works)
//LogFile->write(EQEMuLog::Debug, "Queue damage to all except %s with filter %d (%d), type %d", skip->GetName(), filter, IsClient()?CastToClient()->GetFilter(filter):-1, a->type);
		entity_list.QueueCloseClients(this, outapp, true, 200, skip, true, filter);
		
		//send the damage to ourself if we are a client
		if(IsClient()) {
			//I dont think any filters apply to damage affecting us
			CastToClient()->QueuePacket(outapp);
		}
		
		safe_delete(outapp);
	} else {
        //else, it is a buff tic...
		// Everhood - So we can see our dot dmg like live shows it.
		if(spell_id != SPELL_UNKNOWN && damage > 0 && attacker && attacker != this && attacker->IsClient()) {
			//might filter on (attack_skill>200 && attack_skill<250), but I dont think we need it
			if(attacker->CastToClient()->GetFilter(FilterDOT) != FilterHide) {
				attacker->Message_StringID(MT_DoTDamage, OTHER_HIT_DOT, GetCleanName(),itoa(damage),spells[spell_id].name);
			}
		}
	} //end packet sending

	if(damage > 0) {
			//check for death conditions
		if(IsClient()) {
			if((GetHP() - damage) <= -10) {
				Death(attacker, damage, spell_id, skill_used);
				return;
			}
		} else {
			if (damage >= GetHP()) {
				//killed...
				SetHP(-100);
				Death(attacker, damage, spell_id, skill_used);
				return;
			}
		}
		
		//not killed. Apply the damage
		SetHP(GetHP() - damage);
		
		
    	//fade mez if we are mezzed
		if (IsMezzed()) {
			mlog(COMBAT__HITS, "Breaking mez due to attack.");
			BuffFadeByEffect(SE_Mez);
		}
    	
    	//check stun chances if bashing
		if ((skill_used == BASH || skill_used == KICK && (attacker && attacker->GetLevel() >= 55)) && GetLevel() < 56) {
			int stun_resist = itembonuses.StunResist+spellbonuses.StunResist;
			if(this->GetBaseRace() == OGRE && this->IsClient() && !attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) {
				mlog(COMBAT__HITS, "Stun Resisted. Ogres are immune to frontal melee stuns.");
			} else {
				if(stun_resist <= 0 || MakeRandomInt(0,99) >= stun_resist) {
					mlog(COMBAT__HITS, "Stunned. We had %dpercent resist chance.");
					Stun(0);
				} else {
					mlog(COMBAT__HITS, "Stun Resisted. We had %dpercent resist chance.");
				}
			}
		}
		
		if(spell_id != SPELL_UNKNOWN) {
			//see if root will break
			if (IsRooted()) { // neotoyko: only spells cancel root
				if(GetAA(aaEnhancedRoot))
				{
					if (MakeRandomInt(0, 99) < 10) {
						mlog(COMBAT__HITS, "Spell broke root! 10percent chance");
						BuffFadeByEffect(SE_Root, buffslot); // buff slot is passed through so a root w/ dam doesnt cancel itself
					} else {
						mlog(COMBAT__HITS, "Spell did not break root. 10 percent chance");
					}
				}
				else
				{
					if (MakeRandomInt(0, 99) < 20) {
						mlog(COMBAT__HITS, "Spell broke root! 20percent chance");
						BuffFadeByEffect(SE_Root, buffslot); // buff slot is passed through so a root w/ dam doesnt cancel itself
					} else {
						mlog(COMBAT__HITS, "Spell did not break root. 20 percent chance");
					}
				}			
			}
		}
		else{
			//increment chances of interrupting
			if(IsCasting() && damage > 0) { //shouldnt interrupt on regular spell damage
				attacked_count++;
				mlog(COMBAT__HITS, "Melee attack while casting. Attack count %d", attacked_count);
			}
		}
		
		//send an HP update if we are hurt
		if(GetHP() < GetMaxHP())
			SendHPUpdate();
	}
}
Reply With Quote
  #4  
Old 01-23-2008, 01:11 PM
AiliaMorisato
Sarnak
 
Join Date: Sep 2005
Posts: 34
Default

REALLY NEED THIS LOOKED AT --

Not sure if only the eq_packet_structs and Titanium struct are the only needing the values swapped, problem only verified on titanium client.
Reply With Quote
  #5  
Old 01-23-2008, 01:18 PM
AiliaMorisato
Sarnak
 
Join Date: Sep 2005
Posts: 34
Default

Damn 5 minute edit timer....

anyways, since my explanation was rather bad, the death struct changes fix the "you crush mobname for # damage" no matter how you killed it messages, that was even displaying from spells.

now no damage message is displayed by client when mob dies, so calling death routine will kill a mob with only the X was slain by X message, so no more "mobname tries to punch itself but misses" on #kill for example either.
Reply With Quote
  #6  
Old 01-23-2008, 03:00 PM
moydock
Discordant
 
Join Date: Jun 2005
Posts: 286
Default

Very nice That has annoyed me for quite some time.
__________________
-Croup (the rogue)
Creator of Pandemic (PvP-Racewars)
Reply With Quote
  #7  
Old 02-26-2008, 09:09 AM
So_1337
Dragon
 
Join Date: May 2006
Location: Cincinnati, OH
Posts: 689
Default

Was there any chance of this getting committed?
Reply With Quote
  #8  
Old 02-26-2008, 10:02 AM
KLS
Administrator
 
Join Date: Sep 2006
Posts: 1,348
Default

Yeah I had it ready to be committed but lost my system so it sorta got set back, I'll get it done sooner or later >< sorry.
Reply With Quote
  #9  
Old 02-26-2008, 01:40 PM
So_1337
Dragon
 
Join Date: May 2006
Location: Cincinnati, OH
Posts: 689
Default

Doh! Was wondering where you'd been, hehe. Sorry to hear it, hope you were able to get up and running again without too much trouble.
Reply With Quote
  #10  
Old 03-04-2008, 08:59 PM
KLS
Administrator
 
Join Date: Sep 2006
Posts: 1,348
Default

Alright poking back in, if this hasn't been put in yet I hope to have it or something similar in in a few days now that I can compile.
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 02:53 PM.


 

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