EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Server Code Submissions (https://www.eqemulator.org/forums/forumdisplay.php?f=669)
-   -   multiple fixes to common damage and death (https://www.eqemulator.org/forums/showthread.php?t=24229)

AiliaMorisato 01-23-2008 01:03 PM

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);


AiliaMorisato 01-23-2008 01:04 PM

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`


AiliaMorisato 01-23-2008 01:05 PM

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();
        }
}


AiliaMorisato 01-23-2008 01:11 PM

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.

AiliaMorisato 01-23-2008 01:18 PM

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.

moydock 01-23-2008 03:00 PM

Very nice :) That has annoyed me for quite some time.

So_1337 02-26-2008 09:09 AM

Was there any chance of this getting committed?

KLS 02-26-2008 10:02 AM

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.

So_1337 02-26-2008 01:40 PM

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.

KLS 03-04-2008 08:59 PM

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.


All times are GMT -4. The time now is 06:28 AM.

Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.