diff --git a/changelog.txt b/changelog.txt index 29747ba45..3ab8093cc 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 7/9/2017 == +Akkadius: Fix HP update issues, rework logic for more accurate HP updates Akkadius: Massive reductions in unnecessary network traffic especially during high spam combat fights - HP Updates now only send to others when HP percentage changes (0-100%) - HP Updates were sending excessively even during idle zones when HP wasn't changing at all diff --git a/common/ruletypes.h b/common/ruletypes.h index c5fc1b9ce..397f1a832 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -134,7 +134,6 @@ RULE_INT(Character, TradeskillUpMakePoison, 2) // Make Poison skillup rate adjus RULE_INT(Character, TradeskillUpPottery, 4) // Pottery skillup rate adjust. Lower is faster. RULE_INT(Character, TradeskillUpResearch, 1) // Research skillup rate adjust. Lower is faster. RULE_INT(Character, TradeskillUpTinkering, 2) // Tinkering skillup rate adjust. Lower is faster. -RULE_BOOL(Character, SpamHPUpdates, false) // if your server has stupid amounts of HP that causes client display issues, turn this on! RULE_BOOL(Character, MarqueeHPUpdates, false) // Will show Health % in center of screen < 100% RULE_INT(Character, IksarCommonTongue, 95) // 95 By default (live-like?) RULE_INT(Character, OgreCommonTongue, 95) // 95 By default (live-like?) @@ -235,7 +234,6 @@ RULE_BOOL(World, StartZoneSameAsBindOnCreation, true) //Should the start zone AL RULE_CATEGORY_END() RULE_CATEGORY(Zone) -RULE_INT(Zone, NPCPositonUpdateTicCount, 32) //ms between intervals of sending a position update to the entire zone. RULE_INT(Zone, ClientLinkdeadMS, 180000) //the time a client remains link dead on the server after a sudden disconnection RULE_INT(Zone, GraveyardTimeMS, 1200000) //ms time until a player corpse is moved to a zone's graveyard, if one is specified for the zone RULE_BOOL(Zone, EnableShadowrest, 1) // enables or disables the shadowrest zone feature for player corpses. Default is turned on. diff --git a/zone/attack.cpp b/zone/attack.cpp index ddd8c04ce..ee1384b44 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -137,9 +137,7 @@ EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstanc if (Hand == EQEmu::inventory::slotSecondary) // DW anim type = animDualWield; - if (attack_anim_timer.Check()) { - DoAnim(type, 0, false); - } + DoAnim(type, 0, false); return skillinuse; } diff --git a/zone/client.cpp b/zone/client.cpp index 2df13f25b..986c2b582 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -158,7 +158,8 @@ Client::Client(EQStreamInterface* ieqs) m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), last_region_type(RegionTypeUnsupported), m_dirtyautohaters(false), - npc_close_scan_timer(6000) + npc_close_scan_timer(6000), + hp_self_update_throttle_timer(500) { for(int cf=0; cf < _FilterCount; cf++) ClientFilters[cf] = FilterShow; @@ -357,7 +358,7 @@ Client::~Client() { m_tradeskill_object = nullptr; } - close_npcs.clear(); + close_mobs.clear(); if(IsDueling() && GetDuelTarget() != 0) { Entity* entity = entity_list.GetID(GetDuelTarget()); @@ -1832,13 +1833,13 @@ void Client::SendManaUpdatePacket() { if (!Connected()) return; - if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { - SendManaUpdate(); - SendEnduranceUpdate(); - } - if (last_reported_mana != cur_mana || last_reported_endur != cur_end) { + if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { + SendManaUpdate(); + SendEnduranceUpdate(); + } + auto outapp = new EQApplicationPacket(OP_ManaChange, sizeof(ManaChange_Struct)); ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer; manachange->new_mana = cur_mana; @@ -8745,8 +8746,8 @@ void Client::SendHPUpdateMarquee(){ return; /* Health Update Marquee Display: Custom*/ - int8 health_percentage = (int8)(this->cur_hp * 100 / this->max_hp); - if (health_percentage == 100) + uint8 health_percentage = (uint8)(this->cur_hp * 100 / this->max_hp); + if (health_percentage >= 100) return; std::string health_update_notification = StringFormat("Health: %u%%", health_percentage); diff --git a/zone/client.h b/zone/client.h index 50f36ba72..7bd02c46c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -222,7 +222,7 @@ public: Client(EQStreamInterface * ieqs); ~Client(); - std::unordered_map close_npcs; + std::unordered_map close_mobs; bool is_client_moving; //abstract virtual function implementations required by base abstract class @@ -1485,6 +1485,7 @@ private: Timer helm_toggle_timer; Timer aggro_meter_timer; Timer npc_close_scan_timer; + Timer hp_self_update_throttle_timer; glm::vec3 m_Proximity; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2ca3a1ed4..e4891b999 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -583,8 +583,17 @@ void Client::CompleteConnect() if (raid->IsLocked()) raid->SendRaidLockTo(this); + + raid->SendHPPacketsTo(this); } } + else { + Group *group = nullptr; + group = this->GetGroup(); + if (group) + group->SendHPPacketsTo(this); + } + //bulk raid send in here eventually diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 583ad627b..a346b7ce3 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -254,19 +254,31 @@ bool Client::Process() { /* Build a close range list of NPC's */ if (npc_close_scan_timer.Check()) { - close_npcs.clear(); + close_mobs.clear(); - auto &npc_list = entity_list.GetNPCList(); + auto &mob_list = entity_list.GetMobList(); + float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan)); + float client_update_range = (RuleI(Range, MobPositionUpdates) * RuleI(Range, MobPositionUpdates)); - float scan_range = RuleI(Range, ClientNPCScan); - for (auto itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - NPC* npc = itr->second; - float distance = DistanceNoZ(m_Position, npc->GetPosition()); - if(distance <= scan_range) { - close_npcs.insert(std::pair(npc, distance)); + for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) { + Mob* mob = itr->second; + float distance = DistanceSquared(m_Position, mob->GetPosition()); + if (mob->IsNPC()) { + if (distance <= scan_range) { + close_mobs.insert(std::pair(mob, distance)); + } + else if (mob->GetAggroRange() > scan_range) { + close_mobs.insert(std::pair(mob, distance)); + } } - else if (npc->GetAggroRange() > scan_range) { - close_npcs.insert(std::pair(npc, distance)); + + /* Clients need to be kept up to date for position updates more often otherwise they disappear */ + if (mob->IsClient() && this != mob && distance <= client_update_range) { + auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* spawn_update = (PlayerPositionUpdateServer_Struct*)app->pBuffer; + mob->MakeSpawnUpdateNoDelta(spawn_update); + this->FastQueuePacket(&app, false); + safe_delete(app); } } } @@ -455,7 +467,7 @@ bool Client::Process() { // Send a position packet every 8 seconds - if not done, other clients // see this char disappear after 10-12 seconds of inactivity if (position_timer_counter >= 36) { // Approx. 4 ticks per second - entity_list.SendPositionUpdates(this, pLastUpdateWZ, 500, GetTarget(), true); + entity_list.SendPositionUpdates(this, pLastUpdateWZ, RuleI(Range, MobPositionUpdates), GetTarget(), true); pLastUpdate = Timer::GetCurrentTime(); pLastUpdateWZ = pLastUpdate; position_timer_counter = 0; @@ -619,11 +631,17 @@ bool Client::Process() { // only if client is not feigned if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) { int npc_scan_count = 0; - for (auto it = close_npcs.begin(); it != close_npcs.end(); ++it) { - NPC *npc = it->first; + for (auto it = close_mobs.begin(); it != close_mobs.end(); ++it) { + Mob *mob = it->first; - if (npc->CheckWillAggro(this) && !npc->CheckAggro(this)) { - npc->AddToHateList(this, 25); + if (!mob) + continue; + + if (mob->IsClient()) + continue; + + if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) { + mob->AddToHateList(this, 25); } npc_scan_count++; } diff --git a/zone/entity.cpp b/zone/entity.cpp index 35d67a5d3..52eb41165 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2282,6 +2282,9 @@ bool EntityList::RemoveMob(uint16 delete_id) auto it = mob_list.find(delete_id); if (it != mob_list.end()) { + + RemoveMobFromClientCloseLists(it->second); + if (npc_list.count(delete_id)) entity_list.RemoveNPC(delete_id); else if (client_list.count(delete_id)) @@ -2304,6 +2307,8 @@ bool EntityList::RemoveMob(Mob *delete_mob) auto it = mob_list.begin(); while (it != mob_list.end()) { if (it->second == delete_mob) { + RemoveMobFromClientCloseLists(it->second); + safe_delete(it->second); if (!corpse_list.count(it->first)) free_ids.push(it->first); @@ -2323,10 +2328,9 @@ bool EntityList::RemoveNPC(uint16 delete_id) // make sure its proximity is removed RemoveProximity(delete_id); // remove from client close lists - RemoveNPCFromClientCloseLists(npc); + RemoveMobFromClientCloseLists(npc->CastToMob()); // remove from the list npc_list.erase(it); - // remove from limit list if needed if (npc_limit_list.count(delete_id)) @@ -2336,11 +2340,11 @@ bool EntityList::RemoveNPC(uint16 delete_id) return false; } -bool EntityList::RemoveNPCFromClientCloseLists(NPC *npc) +bool EntityList::RemoveMobFromClientCloseLists(Mob *mob) { auto it = client_list.begin(); while (it != client_list.end()) { - it->second->close_npcs.erase(npc); + it->second->close_mobs.erase(mob); ++it; } return false; @@ -2634,10 +2638,9 @@ void EntityList::RemoveDebuffs(Mob *caster) // Currently, a new packet is sent per entity. // @todo: Come back and use FLAG_COMBINED to pack // all updates into one packet. -void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate, - float range, Entity *alwayssend, bool iSendEvenIfNotChanged) +void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate, float update_range, Entity *always_send, bool iSendEvenIfNotChanged) { - range = range * range; + update_range = (update_range * update_range); EQApplicationPacket *outapp = 0; PlayerPositionUpdateServer_Struct *ppu = 0; @@ -2645,27 +2648,37 @@ void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate, auto it = mob_list.begin(); while (it != mob_list.end()) { - if (outapp == 0) { - outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; - } + mob = it->second; - if (mob && !mob->IsCorpse() && (it->second != client) + + if ( + mob && !mob->IsCorpse() + && (it->second != client) && (mob->IsClient() || iSendEvenIfNotChanged || (mob->LastChange() >= cLastUpdate)) - && (!it->second->IsClient() || !it->second->CastToClient()->GMHideMe(client))) { + && (!it->second->IsClient() || !it->second->CastToClient()->GMHideMe(client)) + ) { + if ( + update_range == 0 + || (it->second == always_send) + || mob->IsClient() + || (DistanceSquared(mob->GetPosition(), client->GetPosition()) <= update_range) + ) { + if (mob && mob->IsClient() && mob->GetID() > 0) { + client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); - //bool Grouped = client->HasGroup() && mob->IsClient() && (client->GetGroup() == mob->CastToClient()->GetGroup()); + if (outapp == 0) { + outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; + } - //if (range == 0 || (iterator.GetData() == alwayssend) || Grouped || (mob->DistNoRootNoZ(*client) <= range)) { - if (range == 0 || (it->second == alwayssend) || mob->IsClient() || (DistanceSquared(mob->GetPosition(), client->GetPosition()) <= range)) { - mob->MakeSpawnUpdate(ppu); - } - if(mob && mob->IsClient() && mob->GetID()>0) { - client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + mob->MakeSpawnUpdate(ppu); + + safe_delete(outapp); + outapp = 0; + } } } - safe_delete(outapp); - outapp = 0; + ++it; } diff --git a/zone/entity.h b/zone/entity.h index 7752e97b1..036e0c419 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -279,7 +279,7 @@ public: bool RemoveTrap(uint16 delete_id); bool RemoveObject(uint16 delete_id); bool RemoveProximity(uint16 delete_npc_id); - bool RemoveNPCFromClientCloseLists(NPC *npc); + bool RemoveMobFromClientCloseLists(Mob *mob); void RemoveAllMobs(); void RemoveAllClients(); void RemoveAllNPCs(); @@ -370,7 +370,7 @@ public: Mob* FindDefenseNPC(uint32 npcid); void OpenDoorsNear(NPC* opener); void UpdateWho(bool iSendFullUpdate = false); - void SendPositionUpdates(Client* client, uint32 cLastUpdate = 0, float range = 0, Entity* alwayssend = 0, bool iSendEvenIfNotChanged = false); + void SendPositionUpdates(Client* client, uint32 cLastUpdate = 0, float update_range = 0, Entity* always_send = 0, bool iSendEvenIfNotChanged = false); char* MakeNameUnique(char* name); static char* RemoveNumbers(char* name); void SignalMobsByNPCID(uint32 npc_type, int signal_id); diff --git a/zone/groups.cpp b/zone/groups.cpp index 6216de4ac..28b4abd6a 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -338,6 +338,13 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte database.SetGroupID(NewMemberName, GetID(), owner->CharacterID(), true); } } + + Group* group = newmember->CastToClient()->GetGroup(); + if (group) { + group->SendHPPacketsTo(newmember); + group->SendHPPacketsFrom(newmember); + } + } else { @@ -389,26 +396,25 @@ void Group::QueuePacket(const EQApplicationPacket *app, bool ack_req) // first joins a group, but otherwise there shouldn't be a need to call it void Group::SendHPPacketsTo(Mob *member) { - if(member && member->IsClient()) - { + if(member && member->IsClient()) { EQApplicationPacket hpapp; EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) - { - if(members[i] && members[i] != member) - { + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(members[i] && members[i] != member) { members[i]->CreateHPPacket(&hpapp); member->CastToClient()->QueuePacket(&hpapp, false); safe_delete_array(hpapp.pBuffer); hpapp.size = 0; - if (member->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) - { + + if (member->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { outapp.SetOpcode(OP_MobManaUpdate); + MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; mmus->spawn_id = members[i]->GetID(); mmus->mana = members[i]->GetManaPercent(); member->CastToClient()->QueuePacket(&outapp, false); + MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; outapp.SetOpcode(OP_MobEnduranceUpdate); meus->endurance = members[i]->GetEndurancePercent(); diff --git a/zone/mob.cpp b/zone/mob.cpp index e795cf040..256fd7df7 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -125,13 +125,12 @@ Mob::Mob(const char* in_name, last_z = 0; - last_hp = 100; + AI_Init(); SetMoving(false); moved=false; m_RewindLocation = glm::vec3(); - move_tic_count = 0; _egnode = nullptr; name[0]=0; @@ -180,6 +179,8 @@ Mob::Mob(const char* in_name, fearspeed = ((float)base_fearspeed) * 0.025f; } + last_hp_percent = 0; + last_hp = 0; current_speed = base_runspeed; @@ -1303,123 +1304,136 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) // sends hp update of this mob to people who might care void Mob::SendHPUpdate(bool skip_self) { + + /* If our HP is different from last HP update call - let's update ourself */ + if (IsClient() && cur_hp != last_hp) { + /* This is to prevent excessive packet sending under trains/fast combat */ + if (this->CastToClient()->hp_self_update_throttle_timer.Check()) { + Log(Logs::General, Logs::HP_Update, + "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i last: %i skip_self: %s", + this->GetCleanName(), + cur_hp, + last_hp, + (skip_self ? "true" : "false") + ); - int8 current_hp = (max_hp == 0 ? 0 : static_cast(cur_hp * 100 / max_hp)); + if (!skip_self || this->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::UF) { + auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct)); + SpawnHPUpdate_Struct* hp_packet_client = (SpawnHPUpdate_Struct*)client_packet->pBuffer; - Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", this->GetCleanName(), current_hp, last_hp); - if (current_hp == last_hp) { + hp_packet_client->cur_hp = CastToClient()->GetHP() - itembonuses.HP; + hp_packet_client->spawn_id = GetID(); + hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; + + CastToClient()->QueuePacket(client_packet); + + safe_delete(client_packet); + + ResetHPUpdateTimer(); + } + } + } + + /* Used to check if HP has changed to update self next round */ + last_hp = cur_hp; + + int8 current_hp_percent = (max_hp == 0 ? 0 : static_cast(cur_hp * 100 / max_hp)); + + Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", this->GetCleanName(), current_hp_percent, last_hp_percent); + + if (current_hp_percent == last_hp_percent) { Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: Same HP - skipping update"); ResetHPUpdateTimer(); + return; } else { + + if (IsClient() && RuleB(Character, MarqueeHPUpdates)) + this->CastToClient()->SendHPUpdateMarquee(); + Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: HP Changed - Send update"); - last_hp = current_hp; + + last_hp_percent = current_hp_percent; } EQApplicationPacket hp_app; Group *group = nullptr; - // destructor will free the pBuffer CreateHPPacket(&hp_app); - // send to people who have us targeted + /* Update those who have use targeted */ entity_list.QueueClientsByTarget(this, &hp_app, false, 0, false, true, EQEmu::versions::bit_AllClients); + + /* Update those who have us on x-target */ entity_list.QueueClientsByXTarget(this, &hp_app, false); + + /* Update groups using Group LAA health name tag counter */ entity_list.QueueToGroupsForNPCHealthAA(this, &hp_app); - // send to group - if(IsGrouped()) - { + /* Update group */ + if(IsGrouped()) { group = entity_list.GetGroupByMob(this); if(group) //not sure why this might be null, but it happens group->SendHPPacketsFrom(this); } + /* Update Raid */ if(IsClient()){ - Raid *r = entity_list.GetRaidByClient(CastToClient()); - if(r){ - r->SendHPPacketsFrom(this); - } + Raid *raid = entity_list.GetRaidByClient(CastToClient()); + if (raid) + raid->SendHPPacketsFrom(this); } - // send to master - if(GetOwner() && GetOwner()->IsClient()) - { + /* Pet - Update master - group and raid if exists */ + if(GetOwner() && GetOwner()->IsClient()) { GetOwner()->CastToClient()->QueuePacket(&hp_app, false); group = entity_list.GetGroupByClient(GetOwner()->CastToClient()); + if(group) group->SendHPPacketsFrom(this); - Raid *r = entity_list.GetRaidByClient(GetOwner()->CastToClient()); - if(r) - r->SendHPPacketsFrom(this); + + Raid *raid = entity_list.GetRaidByClient(GetOwner()->CastToClient()); + if(raid) + raid->SendHPPacketsFrom(this); } - // send to pet - if(GetPet() && GetPet()->IsClient()) - { + /* Send to pet */ + if(GetPet() && GetPet()->IsClient()) { GetPet()->CastToClient()->QueuePacket(&hp_app, false); } - // Update the damage state of destructible objects - if(IsNPC() && IsDestructibleObject()) - { - if (GetHPRatio() > 74) - { - if (GetAppearance() != eaStanding) - { - SendAppearancePacket(AT_DamageState, eaStanding); - _appearance = eaStanding; + /* Destructible objects */ + if (IsNPC() && IsDestructibleObject()) { + if (GetHPRatio() > 74) { + if (GetAppearance() != eaStanding) { + SendAppearancePacket(AT_DamageState, eaStanding); + _appearance = eaStanding; } } - else if (GetHPRatio() > 49) - { - if (GetAppearance() != eaSitting) - { + else if (GetHPRatio() > 49) { + if (GetAppearance() != eaSitting) { SendAppearancePacket(AT_DamageState, eaSitting); _appearance = eaSitting; } } - else if (GetHPRatio() > 24) - { - if (GetAppearance() != eaCrouching) - { + else if (GetHPRatio() > 24) { + if (GetAppearance() != eaCrouching) { SendAppearancePacket(AT_DamageState, eaCrouching); _appearance = eaCrouching; } } - else if (GetHPRatio() > 0) - { - if (GetAppearance() != eaDead) - { + else if (GetHPRatio() > 0) { + if (GetAppearance() != eaDead) { SendAppearancePacket(AT_DamageState, eaDead); _appearance = eaDead; } } - else if (GetAppearance() != eaLooting) - { + else if (GetAppearance() != eaLooting) { SendAppearancePacket(AT_DamageState, eaLooting); _appearance = eaLooting; } } - - bool dospam = RuleB(Character, SpamHPUpdates); - // send to self - we need the actual hps here - if(IsClient() && (!skip_self || dospam)) { - - if (RuleB(Character, MarqueeHPUpdates)) - this->CastToClient()->SendHPUpdateMarquee(); - - auto hp_app2 = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct)); - SpawnHPUpdate_Struct* ds = (SpawnHPUpdate_Struct*)hp_app2->pBuffer; - ds->cur_hp = CastToClient()->GetHP() - itembonuses.HP; - ds->spawn_id = GetID(); - ds->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; - CastToClient()->QueuePacket(hp_app2); - safe_delete(hp_app2); - } - if (!dospam) - ResetHPUpdateTimer(); // delay the timer } // this one just warps the mob to the current location @@ -1428,8 +1442,7 @@ void Mob::SendPosition() auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; MakeSpawnUpdateNoDelta(spu); - move_tic_count = 0; - entity_list.QueueClients(this, app, true); + entity_list.QueueCloseClients(this, app, true, RuleI(Range, MobPositionUpdates), nullptr, false); safe_delete(app); } @@ -1441,45 +1454,32 @@ void Mob::SendPosUpdate(uint8 iSendToSelf) { if (iSendToSelf == 2) { if (IsClient()) { - CastToClient()->FastQueuePacket(&app,false); + CastToClient()->FastQueuePacket(&app, false); } } - else - { - if(move_tic_count == RuleI(Zone, NPCPositonUpdateTicCount)) - { - entity_list.QueueClients(this, app, (iSendToSelf == 0), false); - move_tic_count = 0; - } - else if(move_tic_count % 2 == 0) - { - entity_list.QueueCloseClients(this, app, (iSendToSelf == 0), RuleI(Range, MobPositionUpdates), nullptr, false); - move_tic_count++; - } - else { - move_tic_count++; - } + else { + entity_list.QueueCloseClients(this, app, (iSendToSelf == 0), RuleI(Range, MobPositionUpdates), nullptr, false); } safe_delete(app); } // this is for SendPosition() -void Mob::MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct *spu){ - memset(spu,0xff,sizeof(PlayerPositionUpdateServer_Struct)); - spu->spawn_id = GetID(); - spu->x_pos = FloatToEQ19(m_Position.x); - spu->y_pos = FloatToEQ19(m_Position.y); - spu->z_pos = FloatToEQ19(m_Position.z); - spu->delta_x = NewFloatToEQ13(0); - spu->delta_y = NewFloatToEQ13(0); - spu->delta_z = NewFloatToEQ13(0); - spu->heading = FloatToEQ19(m_Position.w); - spu->animation = 0; +void Mob::MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct *spu) { + memset(spu, 0xff, sizeof(PlayerPositionUpdateServer_Struct)); + spu->spawn_id = GetID(); + spu->x_pos = FloatToEQ19(m_Position.x); + spu->y_pos = FloatToEQ19(m_Position.y); + spu->z_pos = FloatToEQ19(m_Position.z); + spu->delta_x = NewFloatToEQ13(0); + spu->delta_y = NewFloatToEQ13(0); + spu->delta_z = NewFloatToEQ13(0); + spu->heading = FloatToEQ19(m_Position.w); + spu->animation = 0; spu->delta_heading = NewFloatToEQ13(0); - spu->padding0002 =0; - spu->padding0006 =7; - spu->padding0014 =0x7f; - spu->padding0018 =0x5df27; + spu->padding0002 = 0; + spu->padding0006 = 7; + spu->padding0014 = 0x7f; + spu->padding0018 = 0x5df27; } @@ -1553,6 +1553,9 @@ void Mob::ShowStats(Client* client) } void Mob::DoAnim(const int animnum, int type, bool ackreq, eqFilterType filter) { + if (!attack_anim_timer.Check()) + return; + auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct)); Animation_Struct* anim = (Animation_Struct*)outapp->pBuffer; anim->spawnid = GetID(); diff --git a/zone/mob.h b/zone/mob.h index 1d692d977..27ea48d4f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1373,7 +1373,6 @@ protected: void ClearItemFactionBonuses(); void CalculateFearPosition(); - uint32 move_tic_count; bool flee_mode; Timer flee_timer; @@ -1389,7 +1388,8 @@ protected: int wandertype; int pausetype; - int8 last_hp; + int8 last_hp_percent; + int32 last_hp; int cur_wp; glm::vec4 m_CurrentWayPoint; diff --git a/zone/pathing.cpp b/zone/pathing.cpp index 417ee3af2..25d996eac 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -1454,7 +1454,10 @@ void PathManager::NodeInfo(Client *c) void PathManager::DumpPath(std::string filename) { std::ofstream o_file; - o_file.open(filename.c_str(), std::ios_base::binary | std::ios_base::trunc | std::ios_base::out); + + std::string file_to_write = StringFormat("%s%s", Config->MapDir.c_str(), filename.c_str()); + + o_file.open(file_to_write.c_str(), std::ios_base::binary | std::ios_base::trunc | std::ios_base::out); o_file.write("EQEMUPATH", 9); o_file.write((const char*)&Head, sizeof(Head)); o_file.write((const char*)PathNodes, (sizeof(PathNode)*Head.PathNodeCount)); diff --git a/zone/raids.cpp b/zone/raids.cpp index c9d4ee6c5..d32ad4def 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -145,6 +145,13 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo } } + Group *group_update = nullptr; + group_update = c->GetGroup(); + if (group_update) { + group_update->SendHPPacketsTo(c); + group_update->SendHPPacketsFrom(c); + } + auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); @@ -1539,35 +1546,37 @@ void Raid::MemberZoned(Client *c) group_mentor[gid].mentoree = nullptr; } -void Raid::SendHPPacketsTo(Client *c) +void Raid::SendHPPacketsTo(Client *client) { - if(!c) + if(!client) return; - uint32 gid = this->GetGroup(c); - EQApplicationPacket hpapp; + uint32 group_id = this->GetGroup(client); + + EQApplicationPacket hp_packet; EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); - for(int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if(members[x].member) - { - if((members[x].member != c) && (members[x].GroupNumber == gid)) - { - members[x].member->CreateHPPacket(&hpapp); - c->QueuePacket(&hpapp, false); - safe_delete_array(hpapp.pBuffer); - hpapp.size = 0; - if (c->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) - { + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) { + if(members[x].member) { + if((members[x].member != client) && (members[x].GroupNumber == group_id)) { + + members[x].member->CreateHPPacket(&hp_packet); + client->QueuePacket(&hp_packet, false); + safe_delete_array(hp_packet.pBuffer); + + hp_packet.size = 0; + if (client->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { + outapp.SetOpcode(OP_MobManaUpdate); - MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; - mmus->spawn_id = members[x].member->GetID(); - mmus->mana = members[x].member->GetManaPercent(); - c->QueuePacket(&outapp, false); + MobManaUpdate_Struct *mana_update = (MobManaUpdate_Struct *)outapp.pBuffer; + mana_update->spawn_id = members[x].member->GetID(); + mana_update->mana = members[x].member->GetManaPercent(); + client->QueuePacket(&outapp, false); + outapp.SetOpcode(OP_MobEnduranceUpdate); - MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; - meus->endurance = members[x].member->GetEndurancePercent(); - c->QueuePacket(&outapp, false); + MobEnduranceUpdate_Struct *endurance_update = (MobEnduranceUpdate_Struct *)outapp.pBuffer; + endurance_update->endurance = members[x].member->GetEndurancePercent(); + client->QueuePacket(&outapp, false); } } }