From b937444425054d642bd329574203401715bebe18 Mon Sep 17 00:00:00 2001 From: Emagi Date: Fri, 30 May 2025 21:51:53 -0400 Subject: [PATCH] Fix leashing, leading, rubberbanding mobs. Work in progress for size mod. Fix #18 Leashing, leading, rubberbanding issues with spawns resolved Issue #17 Work in Progress, size mod stat support in the works, setting temporary_scale in info struct seems to modify size, the pos_size values in the position struct are not for KoS and older clients. AddSpellBonus was translating values from float to sint32 early, now we take bonus values into player add bonus so that float values will be honored, as well as sint32. This applies to uncontested parry, block, dodge, riposte and the size mod. --- source/WorldServer/Entity.cpp | 14 +++++++++++++- source/WorldServer/Entity.h | 8 ++++++-- source/WorldServer/Items/Items.h | 3 ++- source/WorldServer/Spawn.cpp | 32 ++++++++++++++++++++----------- source/WorldServer/Spawn.h | 1 + source/WorldServer/World.cpp | 28 +++++++++++++++++++-------- source/WorldServer/World.h | 2 +- source/WorldServer/zoneserver.cpp | 1 + 8 files changed, 65 insertions(+), 24 deletions(-) diff --git a/source/WorldServer/Entity.cpp b/source/WorldServer/Entity.cpp index 2211a8f..6cde2f0 100644 --- a/source/WorldServer/Entity.cpp +++ b/source/WorldServer/Entity.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -290,6 +290,7 @@ void Entity::MapInfoStruct() get_float_funcs["recovery_speed"] = l::bind(&InfoStruct::get_recovery_speed, &info_struct); get_float_funcs["spell_reuse_speed"] = l::bind(&InfoStruct::get_spell_reuse_speed, &info_struct); get_float_funcs["spell_multi_attack"] = l::bind(&InfoStruct::get_spell_multi_attack, &info_struct); + get_float_funcs["size_mod"] = l::bind(&InfoStruct::get_size_mod, &info_struct); get_float_funcs["dps"] = l::bind(&InfoStruct::get_dps, &info_struct); get_float_funcs["dps_multiplier"] = l::bind(&InfoStruct::get_dps_multiplier, &info_struct); get_float_funcs["attackspeed"] = l::bind(&InfoStruct::get_attackspeed, &info_struct); @@ -498,6 +499,7 @@ void Entity::MapInfoStruct() set_float_funcs["recovery_speed"] = l::bind(&InfoStruct::set_recovery_speed, &info_struct, l::_1); set_float_funcs["spell_reuse_speed"] = l::bind(&InfoStruct::set_spell_reuse_speed, &info_struct, l::_1); set_float_funcs["spell_multi_attack"] = l::bind(&InfoStruct::set_spell_multi_attack, &info_struct, l::_1); + set_float_funcs["size_mod"] = l::bind(&InfoStruct::set_size_mod, &info_struct, l::_1); set_float_funcs["dps"] = l::bind(&InfoStruct::set_dps, &info_struct, l::_1); set_float_funcs["dps_multiplier"] = l::bind(&InfoStruct::set_dps_multiplier, &info_struct, l::_1); set_float_funcs["attackspeed"] = l::bind(&InfoStruct::set_attackspeed, &info_struct, l::_1); @@ -1458,6 +1460,9 @@ void Entity::CalculateBonuses(){ info->set_recovery_speed(0); info->set_spell_reuse_speed(0); info->set_spell_multi_attack(0); + + float previous_size_mod = info->get_size_mod(); + info->set_size_mod(0.0f); info->set_dps(0); info->set_dps_multiplier(0); info->set_haste(0); @@ -1543,6 +1548,7 @@ void Entity::CalculateBonuses(){ info->add_recovery_speed(values->abilityrecoveryspeed); info->add_spell_reuse_speed(values->spellreusespeed); info->add_spell_multi_attack(values->spellmultiattackchance); + info->add_size_mod(values->size_mod); info->add_dps(values->dps); info->add_dps_multiplier(CalculateDPSMultiplier()); info->add_haste(values->attackspeed); @@ -1653,6 +1659,12 @@ void Entity::CalculateBonuses(){ UpdateWeapons(); + if(previous_size_mod != info->get_size_mod()) { + info_changed = true; + changed = true; + size_changed = true; + AddChangedZoneSpawn(); + } safe_delete(values); } diff --git a/source/WorldServer/Entity.h b/source/WorldServer/Entity.h index 8a993cf..d66552c 100644 --- a/source/WorldServer/Entity.h +++ b/source/WorldServer/Entity.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -43,7 +43,7 @@ struct BonusValues{ int32 spell_id; int8 tier; int16 type; - sint32 value; + float value; int64 class_req; vector race_req; vector faction_req; @@ -636,6 +636,7 @@ struct InfoStruct{ float get_recovery_speed() { std::lock_guard lk(classMutex); return recovery_speed_; } float get_spell_reuse_speed() { std::lock_guard lk(classMutex); return spell_reuse_speed_; } float get_spell_multi_attack() { std::lock_guard lk(classMutex); return spell_multi_attack_; } + float get_size_mod() { std::lock_guard lk(classMutex); return size_mod_; } float get_dps() { std::lock_guard lk(classMutex); return dps_; } float get_dps_multiplier() { std::lock_guard lk(classMutex); return dps_multiplier_; } float get_attackspeed() { std::lock_guard lk(classMutex); return attackspeed_; } @@ -915,6 +916,7 @@ struct InfoStruct{ void set_recovery_speed(float value) { std::lock_guard lk(classMutex); recovery_speed_ = value; } void set_spell_reuse_speed(float value) { std::lock_guard lk(classMutex); spell_reuse_speed_ = value; } void set_spell_multi_attack(float value) { std::lock_guard lk(classMutex); spell_multi_attack_ = value; } + void set_size_mod(float value) { std::lock_guard lk(classMutex); size_mod_ = value; } void set_dps(float value) { std::lock_guard lk(classMutex); dps_ = value; } void set_dps_multiplier(float value) { std::lock_guard lk(classMutex); dps_multiplier_ = value; } void set_attackspeed(float value) { std::lock_guard lk(classMutex); attackspeed_ = value; } @@ -946,6 +948,7 @@ struct InfoStruct{ void add_recovery_speed(float value) { std::lock_guard lk(classMutex); recovery_speed_ += value; } void add_spell_reuse_speed(float value) { std::lock_guard lk(classMutex); spell_reuse_speed_ += value; } void add_spell_multi_attack(float value) { std::lock_guard lk(classMutex); spell_multi_attack_ += value; } + void add_size_mod(float value) { std::lock_guard lk(classMutex); size_mod_ += value; } void add_dps(float value) { std::lock_guard lk(classMutex); if(dps_ + value < 0.0f) dps_ = 0.0f; else dps_ += value; } void add_dps_multiplier(float value) { std::lock_guard lk(classMutex); if(dps_multiplier_ + value < 0.0f) dps_multiplier_ = 0.0f; else dps_multiplier_ += value; } void add_attackspeed(float value) { std::lock_guard lk(classMutex); if(attackspeed_ + value < 0.0f) attackspeed_ = 0.0f; else attackspeed_ += value; } @@ -1187,6 +1190,7 @@ private: float recovery_speed_; float spell_reuse_speed_; float spell_multi_attack_; + float size_mod_; float dps_; float dps_multiplier_; float attackspeed_; diff --git a/source/WorldServer/Items/Items.h b/source/WorldServer/Items/Items.h index 38c0e8d..25c021c 100644 --- a/source/WorldServer/Items/Items.h +++ b/source/WorldServer/Items/Items.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -692,6 +692,7 @@ struct ItemStatsValues{ float uncontested_block; float uncontested_dodge; float uncontested_riposte; + float size_mod; }; diff --git a/source/WorldServer/Spawn.cpp b/source/WorldServer/Spawn.cpp index de6a0e1..8556681 100644 --- a/source/WorldServer/Spawn.cpp +++ b/source/WorldServer/Spawn.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -80,6 +80,7 @@ Spawn::Spawn(){ position_changed = false; send_spawn_changes = true; info_changed = false; + size_changed = false; appearance.pos.Speed1 = 0; last_attacker = 0; faction_id = 0; @@ -2193,24 +2194,21 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b packet->setDataByName("pos_heading2", appearance.pos.Dir2); } + if (size == 0) + size = 32; + if (version <= 910) { packet->setDataByName("pos_collision_radius", appearance.pos.collision_radius > 0 ? appearance.pos.collision_radius : 32); packet->setDataByName("pos_size", size > 0 ? size : 32); - packet->setDataByName("pos_size_multiplier", 32); //32 is normal } else { - if (size == 0) - size = 32; - packet->setDataByName("pos_collision_radius", appearance.pos.collision_radius > 0 ? appearance.pos.collision_radius : 32); - packet->setDataByName("pos_size", 1.0f); - - if (!IsPlayer()) + if(!IsPlayer()) packet->setDataByName("pos_size", size > 0 ? (((float)size) / 32.0f) : 1.0f); // float not an integer else packet->setDataByName("pos_size", 1.0f); - + // please do not remove! This makes it so NPCs for example do not resize large/small when you are in combat with them! packet->setDataByName("pos_size_ratio", 1.0f); } @@ -2490,6 +2488,11 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) { if (GetMerchantID() > 0) packet->setDataByName("merchant", 1); + if(IsEntity() && ((Entity*)this)->GetInfoStruct()->get_size_mod() != 0.0f) { + float mod = ((Entity*)this)->GetInfoStruct()->get_size_mod(); // e.g., -0.25 or +0.25 + //packet->setDataByName("temporary_scale", mod); //TODO: Understand what these mod values should be, anything we send makes the client shrink to nothing + } + packet->setDataByName("effective_level", IsEntity() && ((Entity*)this)->GetInfoStruct()->get_effective_level() != 0 ? (int8)((Entity*)this)->GetInfoStruct()->get_effective_level() : (int8)GetLevel()); packet->setDataByName("level", (int8)GetLevel()); packet->setDataByName("unknown4", (int8)GetLevel()); @@ -3187,8 +3190,15 @@ void Spawn::ProcessMovement(bool isSpawnListLocked){ float distance = GetDistance(followTarget, loc->x, loc->y, loc->z); if ( (!EngagedInCombat() && m_followDistance > 0 && distance > m_followDistance) || ( EngagedInCombat() && distance > rule_manager.GetZoneRule(GetZoneID(), R_Combat, MaxCombatRange)->GetFloat())) { - MoveToLocation(followTarget, rule_manager.GetZoneRule(GetZoneID(), R_Combat, MaxCombatRange)->GetFloat(), true, loc->mapped); - CalculateRunningLocation(); + float self_distance = GetDistance(this, loc->x, loc->y, loc->z); + if(dist < distance || dist < self_distance) { + ClearRunningLocations(); + CalculateRunningLocation(true); + } + else { + MoveToLocation(followTarget, rule_manager.GetZoneRule(GetZoneID(), R_Combat, MaxCombatRange)->GetFloat(), true, loc->mapped); + CalculateRunningLocation(); + } } } else { diff --git a/source/WorldServer/Spawn.h b/source/WorldServer/Spawn.h index bb4b526..e3e6a6a 100644 --- a/source/WorldServer/Spawn.h +++ b/source/WorldServer/Spawn.h @@ -1192,6 +1192,7 @@ public: std::atomic info_changed; std::atomic vis_changed; std::atomic is_running; + std::atomic size_changed; int16 size; int32 faction_id; int8 oversized_packet; //0xff diff --git a/source/WorldServer/World.cpp b/source/WorldServer/World.cpp index f295777..63e7c92 100644 --- a/source/WorldServer/World.cpp +++ b/source/WorldServer/World.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -2246,16 +2246,24 @@ bool World::RejoinGroup(Client* client, int32 group_id){ } -void World::AddBonuses(Item* item, ItemStatsValues* values, int16 type, sint32 value, Entity* entity){ +void World::AddBonuses(Item* item, ItemStatsValues* values, int16 type, float value, Entity* entity){ if(values){ if(item && entity && entity->IsPlayer()) { - int32 effective_level = entity->GetInfoStructUInt("effective_level"); - if(effective_level && effective_level < entity->GetLevel() && item->details.recommended_level > effective_level) - { - int32 diff = item->details.recommended_level - effective_level; - float tmpValue = (float)value; - value = (sint32)(float)(tmpValue / (1.0f + ((float)diff * .05f))); + switch(type) { + case ITEM_STAT_SIZEMOD: + case ITEM_STAT_CONCENTRATION: + break; + default: { + int32 effective_level = entity->GetInfoStructUInt("effective_level"); + if(effective_level && effective_level < entity->GetLevel() && item->details.recommended_level > effective_level) + { + int32 diff = item->details.recommended_level - effective_level; + float tmpValue = (float)value; + value = (sint32)(float)(tmpValue / (1.0f + ((float)diff * .05f))); + } + break; + } } } switch(type){ @@ -2383,6 +2391,10 @@ void World::AddBonuses(Item* item, ItemStatsValues* values, int16 type, sint32 v values->spellmultiattackchance += value; break; } + case ITEM_STAT_SIZEMOD:{ + values->size_mod += value; + break; + } case ITEM_STAT_DPS:{ values->dps += value; break; diff --git a/source/WorldServer/World.h b/source/WorldServer/World.h index 039c83d..400431a 100644 --- a/source/WorldServer/World.h +++ b/source/WorldServer/World.h @@ -635,7 +635,7 @@ public: bool RejoinGroup(Client* client, int32 group_id); //bool MakeLeader(Client* leader, string new_leader); - void AddBonuses(Item* item, ItemStatsValues* values, int16 type, sint32 value, Entity* entity); + void AddBonuses(Item* item, ItemStatsValues* values, int16 type, float value, Entity* entity); int32 CreateGuild(const char* guild_name, Client* leader = 0, int32 group_id = 0); void SaveGuilds(); void PickRandomLottoDigits(int32* digits); diff --git a/source/WorldServer/zoneserver.cpp b/source/WorldServer/zoneserver.cpp index a846120..50f2604 100644 --- a/source/WorldServer/zoneserver.cpp +++ b/source/WorldServer/zoneserver.cpp @@ -2317,6 +2317,7 @@ void ZoneServer::SendSpawnChanges(){ spawn->position_changed = false; spawn->vis_changed = false; spawn->info_changed = false; + spawn->size_changed = false; } MSpawnList.releasereadlock(__FUNCTION__, __LINE__); }