mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
fix: run speed modifiers stack as the client's do
LWOControllablePhysComponent::msgAddRunspeedModifier (0x00be0af0) and msgRemoveRunSpeedModifier (0x00be0720): a modifier over 1000 or of exactly 500 counts for nothing, any slow (under 500) wins with the lowest one, and otherwise the highest boost is the speed. Before, the last one applied won, so a small boost cast after a big one, or a boost cast while slowed, replaced it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -333,8 +333,7 @@ void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
|
||||
|
||||
void ControllablePhysicsComponent::AddSpeedboost(float value) {
|
||||
m_ActiveSpeedBoosts.push_back(value);
|
||||
m_SpeedBoost = value;
|
||||
SetSpeedMultiplier(value / 500.0f); // 500 being the base speed
|
||||
UpdateSpeedBoost();
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::RemoveSpeedboost(float value) {
|
||||
@@ -346,16 +345,35 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Recalculate speedboost since we removed one
|
||||
m_SpeedBoost = 500.0f; // Entities without level progression (enemies, pets) go back to the normal multiplier of 1
|
||||
if (m_ActiveSpeedBoosts.empty()) { // no active speed boosts left, so return to base speed
|
||||
UpdateSpeedBoost();
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::UpdateSpeedBoost() {
|
||||
// The client's rule (LWOControllablePhysComponent::msgAddRunspeedModifier 0x00be0af0, msgRemoveRunSpeedModifier
|
||||
// 0x00be0720): a modifier over 1000 or of exactly the base 500 counts for nothing; with any slow (under 500) the
|
||||
// lowest one is the speed, otherwise the highest boost is
|
||||
constexpr float baseSpeed = 500.0f;
|
||||
constexpr float maxModifier = 1000.0f;
|
||||
float lowestSlow = baseSpeed;
|
||||
float highestBoost = baseSpeed;
|
||||
for (const auto boost : m_ActiveSpeedBoosts) {
|
||||
if (boost > maxModifier) continue;
|
||||
if (boost < lowestSlow) lowestSlow = boost;
|
||||
if (boost > highestBoost) highestBoost = boost;
|
||||
}
|
||||
|
||||
if (lowestSlow < baseSpeed) {
|
||||
m_SpeedBoost = lowestSlow;
|
||||
} else if (highestBoost > baseSpeed) {
|
||||
m_SpeedBoost = highestBoost;
|
||||
} else {
|
||||
// No modifier counts: entities without level progression (enemies, pets) go back to the normal multiplier of 1
|
||||
m_SpeedBoost = baseSpeed;
|
||||
auto* levelProgressionComponent = m_Parent->GetComponent<LevelProgressionComponent>();
|
||||
if (levelProgressionComponent) m_SpeedBoost = levelProgressionComponent->GetSpeedBase();
|
||||
} else { // Used the last applied speedboost
|
||||
m_SpeedBoost = m_ActiveSpeedBoosts.back();
|
||||
}
|
||||
SetSpeedMultiplier(m_SpeedBoost / 500.0f); // 500 being the base speed
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
SetSpeedMultiplier(m_SpeedBoost / baseSpeed);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims) {
|
||||
|
||||
@@ -395,6 +395,11 @@ private:
|
||||
*/
|
||||
bool m_IsTeleporting;
|
||||
|
||||
/**
|
||||
* Works the speed out of the active speed boosts: the lowest slow if there is one, otherwise the highest boost
|
||||
*/
|
||||
void UpdateSpeedBoost();
|
||||
|
||||
/**
|
||||
* The list of speed boosts for this entity
|
||||
*/
|
||||
|
||||
@@ -432,6 +432,40 @@ TEST_F(ReplicaConstructionTest, CharacterGuildChangeIsWrittenOnce) {
|
||||
player.SetCharacter(nullptr);
|
||||
}
|
||||
|
||||
// Run speed modifiers stack as in LWOControllablePhysComponent::msgAddRunspeedModifier (0x00be0af0) and
|
||||
// msgRemoveRunSpeedModifier (0x00be0720): the lowest slow wins, otherwise the highest boost; 500 and anything over 1000
|
||||
// count for nothing
|
||||
TEST_F(ReplicaConstructionTest, RunSpeedModifiersStackAsTheClientDoes) {
|
||||
Entity entity(15, info);
|
||||
auto* const physics = entity.AddComponent<ControllablePhysicsComponent>(-1);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 1.0f);
|
||||
|
||||
physics->AddSpeedboost(750.0f);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 1.5f);
|
||||
physics->AddSpeedboost(550.0f); // a smaller boost applied later does not replace the bigger one
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 1.5f);
|
||||
physics->AddSpeedboost(250.0f); // any slow beats every boost
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 0.5f);
|
||||
physics->AddSpeedboost(133.0f);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 133.0f / 500.0f);
|
||||
physics->AddSpeedboost(350.0f);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 133.0f / 500.0f);
|
||||
physics->AddSpeedboost(1500.0f);
|
||||
physics->AddSpeedboost(500.0f);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 133.0f / 500.0f);
|
||||
|
||||
physics->RemoveSpeedboost(133.0f);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 0.5f);
|
||||
physics->RemoveSpeedboost(250.0f);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 0.7f);
|
||||
physics->RemoveSpeedboost(350.0f);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 1.5f);
|
||||
physics->RemoveSpeedboost(750.0f);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 1.1f);
|
||||
physics->RemoveSpeedboost(550.0f);
|
||||
EXPECT_FLOAT_EQ(physics->GetSpeedMultiplier(), 1.0f); // 1500 and 500 are left and count for nothing
|
||||
}
|
||||
|
||||
// Live wrote the cheat block (gravity scale, speed multiplier) on construction only when one of them was changed: 216
|
||||
// of 20,617 controllable-physics constructions had it (players at run speed 1.05, enemies with gravity 0), never 1/1.
|
||||
TEST_F(ReplicaConstructionTest, ControllablePhysicsConstructionWritesCheatsOnlyWhenChanged) {
|
||||
|
||||
Reference in New Issue
Block a user