Browse Source

Player/Target Radius added to distance checks

Radius is now built into the GetDistance formula, it also is based on f32/32.0f per recommendations of peak (Thx peak!)

Added combat range as a rule: rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat()

Fixes #53
Image 4 năm trước cách đây
mục cha
commit
04498a2490

+ 4 - 3
EQ2/source/WorldServer/Bots/BotBrain.cpp

@@ -2,6 +2,9 @@
 #include "../Combat.h"
 #include "../Spells.h"
 #include "../../common/Log.h"
+#include "../Rules/Rules.h"
+
+extern RuleManager rule_manager;
 
 BotBrain::BotBrain(Bot* body) : Brain(body) {
 	Body = body;
@@ -46,11 +49,9 @@ void BotBrain::Think() {
 
 	// Get distance from the owner
 	float distance = GetBody()->GetDistance(target);
-	distance -= target->appearance.pos.collision_radius / 10;
-	distance -= GetBody()->appearance.pos.collision_radius / 10;
 
 	// If out of melee range then move closer
-	if (distance > MAX_COMBAT_RANGE)
+	if (distance > rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat())
 		MoveCloser(target);
 }
 

+ 2 - 4
EQ2/source/WorldServer/Combat.cpp

@@ -170,7 +170,7 @@ bool Entity::AttackAllowed(Entity* target, float distance, bool range_attack) {
 		}
 	}
 	else if (distance != 0) {
-		if(distance >= MAX_COMBAT_RANGE) {
+		if(distance >= rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat()) {
 			LogWrite(COMBAT__DEBUG, 3, "AttackAllowed", "Failed to attack: distance is beyond melee range");
 			return false;
 		}
@@ -1151,8 +1151,6 @@ void Player::ProcessCombat() {
 
 	float distance = 0;
 	distance = GetDistance(combat_target);
-	distance -= combat_target->appearance.pos.collision_radius / 10;
-	distance -= appearance.pos.collision_radius / 10;
 
 	// Check to see if we are doing ranged auto attacks if not check to see if we are in melee range
 	if (GetRangeAttack()) {
@@ -1185,7 +1183,7 @@ void Player::ProcessCombat() {
 			}
 		}
 	}
-	else if(distance <= MAX_COMBAT_RANGE) {
+	else if(distance <= rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat()) {
 		// We are doing melee auto attacks and are within range
 
 		// Check to see if we can attack the target

+ 2 - 1
EQ2/source/WorldServer/Combat.h

@@ -27,7 +27,8 @@
 #define COMBAT_NORMAL_FIGHTER	0
 #define COMBAT_ADD_FIGHTER		1
 #define COMBAT_REMOVE_FIGHTER	2
-#define MAX_COMBAT_RANGE		3
+// replace with rule rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat()
+//#define MAX_COMBAT_RANGE		3
 
 class ZoneServer;
 class SpellProcess;

+ 1 - 13
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -864,8 +864,6 @@ int EQ2Emu_lua_SpellHeal(lua_State* state) {
 		luaspell->resisted = false;
 		if (target) {
 			float distance = caster->GetDistance(target, true);
-			distance -= caster->appearance.pos.collision_radius / 10;
-			distance -= target->appearance.pos.collision_radius / 10;
 			if (((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs))
 				success = true;
 		}
@@ -876,8 +874,6 @@ int EQ2Emu_lua_SpellHeal(lua_State* state) {
 			for (int32 i = 0; i < luaspell->targets.size(); i++) {
 				if ((target = zone->GetSpawnByID(luaspell->targets[i]))) {
 					float distance = caster->GetDistance(target, true);
-					distance -= caster->appearance.pos.collision_radius / 10;
-					distance -= target->appearance.pos.collision_radius / 10;
 					((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs);
 				}
 			}
@@ -1230,8 +1226,6 @@ int EQ2Emu_lua_SpellDamage(lua_State* state) {
 						race_match = true; // if the race_req.size = 0 then there is no race requirement and the race_match will be true
 					if (race_match == true) {
 						float distance = caster->GetDistance(target, true);
-						distance -= caster->appearance.pos.collision_radius / 10;
-						distance -= target->appearance.pos.collision_radius / 10;
 						((Entity*)caster)->SpellAttack(target, distance, luaspell, type, min_damage, max_damage, crit_mod, no_calcs);
 					}
 				}
@@ -1253,8 +1247,6 @@ int EQ2Emu_lua_SpellDamage(lua_State* state) {
 				race_match = true; // if the race_req.size = 0 then there is no race requirement and the race_match will be true
 			if (race_match == true) {
 				float distance = caster->GetDistance(target, true);
-				distance -= caster->appearance.pos.collision_radius / 10;
-				distance -= target->appearance.pos.collision_radius / 10;
 				if (((Entity*)caster)->SpellAttack(target, distance, luaspell, type, min_damage, max_damage, crit_mod, no_calcs))
 					success = true;
 			}
@@ -2322,11 +2314,7 @@ int EQ2Emu_lua_GetDistance(lua_State* state) {
 	Spawn* spawn2 = lua_interface->GetSpawn(state, 2);
 	bool include_radius = lua_interface->GetInt8Value(state, 3) == 1;
 	if (spawn && spawn2) {
-		float distance = spawn->GetDistance(spawn2);
-		if (include_radius) {
-			distance -= spawn->appearance.pos.collision_radius / 10;
-			distance -= spawn2->appearance.pos.collision_radius / 10;
-		}
+		float distance = spawn->GetDistance(spawn2, false, include_radius);
 
 		lua_interface->SetFloatValue(state, distance);
 		return 1;

+ 6 - 11
EQ2/source/WorldServer/NPC_AI.cpp

@@ -25,6 +25,9 @@
 #include "../common/Log.h"
 #include "LuaInterface.h"
 #include "World.h"
+#include "Rules/Rules.h"
+
+extern RuleManager rule_manager;
 
 extern LuaInterface* lua_interface;
 extern World world;
@@ -101,8 +104,6 @@ void Brain::Think() {
 				// Still within max chase distance lets to the combat stuff now
 
 				float distance = m_body->GetDistance(target);
-				distance -= target->appearance.pos.collision_radius / 10;
-				distance -= m_body->appearance.pos.collision_radius / 10;
 
 				if(!m_body->IsCasting() && (!HasRecovered() || !ProcessSpell(target, distance))) {
 					LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "%s is attempting melee on %s.", m_body->GetName(), target->GetName());
@@ -328,7 +329,7 @@ bool Brain::CheckBuffs() {
 }
 
 void Brain::ProcessMelee(Entity* target, float distance) {
-	if(distance > MAX_COMBAT_RANGE)
+	if(distance > rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat())
 		MoveCloser(target);
 	else {
 		if (target) {
@@ -479,11 +480,9 @@ void CombatPetBrain::Think() {
 
 	// Get distance from the owner
 	float distance = GetBody()->GetDistance(target);
-	distance -= target->appearance.pos.collision_radius / 10;
-	distance -= GetBody()->appearance.pos.collision_radius / 10;
 
 	// If out of melee range then move closer
-	if (distance > MAX_COMBAT_RANGE)
+	if (distance > rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat())
 		MoveCloser(target);
 }
 
@@ -513,11 +512,9 @@ void NonCombatPetBrain::Think() {
 
 	// Get distance from the owner
 	float distance = GetBody()->GetDistance(target);
-	distance -= target->appearance.pos.collision_radius / 10;
-	distance -= GetBody()->appearance.pos.collision_radius / 10;
 
 	// If out of melee range then move closer
-	if (distance > MAX_COMBAT_RANGE)
+	if (distance > rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat())
 		MoveCloser(target);
 }
 
@@ -591,8 +588,6 @@ void DumbFirePetBrain::Think() {
 			}
 
 			float distance = GetBody()->GetDistance(target);
-			distance -= target->appearance.pos.collision_radius / 10;
-			distance -= GetBody()->appearance.pos.collision_radius / 10;
 
 			if(!GetBody()->IsCasting() && (!HasRecovered() || !ProcessSpell(target, distance))) {
 				LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "%s is attempting melee on %s.", GetBody()->GetName(), target->GetName());

+ 2 - 0
EQ2/source/WorldServer/Rules/Rules.cpp

@@ -207,6 +207,8 @@ RuleManager::RuleManager() {
 	RULE_INIT(R_PVP, LevelRange, "4");
 	RULE_INIT(R_PVP, InvisPlayerDiscoveryRange, "20"); // value > 0 sets radius inner to see, = 0 means always seen, -1 = never seen
 
+	/* COMBAT */
+	RULE_INIT(R_Combat, MaxCombatRange, "4.0");
 	/* SPAWN */
 	RULE_INIT(R_Spawn, SpeedMultiplier, "300"); // note: this value was 1280 until 6/1/2009, then was 600 til Sep 2009, when it became 300...?
 	RULE_INIT(R_Spawn, SpeedRatio, "0");		// was 1280/7.5 and 600/7.5 until it became 300.

+ 4 - 0
EQ2/source/WorldServer/Rules/Rules.h

@@ -33,6 +33,7 @@ enum RuleCategory {
 	R_Guild,
 	R_Player,
 	R_PVP,
+	R_Combat,
 	R_Spawn,
 	R_UI,
 	R_World,
@@ -74,6 +75,9 @@ enum RuleType {
 	LevelRange,
 	InvisPlayerDiscoveryRange,
 
+	/* COMBAT */
+	MaxCombatRange,
+
 	/* SPAWN */
 	SpeedMultiplier,
 	SpeedRatio,

+ 57 - 12
EQ2/source/WorldServer/Spawn.cpp

@@ -22,6 +22,7 @@
 #include "../common/timer.h"
 #include <time.h>
 #include <math.h>
+#include <emmintrin.h>
 #include "Entity.h"
 #include "Widget.h"
 #include "Sign.h"
@@ -1078,20 +1079,64 @@ float Spawn::GetDistance(float x1, float y1, float z1, float x2, float y2, float
 	return sqrt(x1*x1 + y1*y1 + z1*z1); 
 }
 
-float Spawn::GetDistance(float x, float y, float z, bool ignore_y){
-	if(ignore_y)
-		return GetDistance(x, y, z, GetX(), y, GetZ());
+float Spawn::GetDistance(float x, float y, float z, float radius, bool ignore_y) {
+	if (ignore_y)
+		return GetDistance(x, y, z, GetX(), y, GetZ()) - radius;
 	else
-		return GetDistance(x, y, z, GetX(), GetY(), GetZ());
+		return GetDistance(x, y, z, GetX(), GetY(), GetZ()) - radius;
 }
 
-float Spawn::GetDistance(Spawn* spawn, bool ignore_y){
+float Spawn::GetDistance(float x, float y, float z, bool ignore_y) {
+	return GetDistance(x, y, z, 0.0f, ignore_y);
+}
+float Spawn::GetDistance(Spawn* spawn, bool ignore_y, bool includeRadius){
 	float ret = 0;
-	if(spawn)
-		ret = GetDistance(spawn->GetX(), spawn->GetY(), spawn->GetZ(), ignore_y);
+
+	if (spawn)
+	{
+		float radius = 0.0f;
+		if (includeRadius)
+			radius = CalculateRadius(spawn);
+		ret = GetDistance(spawn->GetX(), spawn->GetY(), spawn->GetZ(), radius, ignore_y);
+	}
+
+	// maybe distance against ourselves, in that case we want to nullify the radius check
+	if (ret < 0)
+		ret = 0.0f;
+
 	return ret;
 }
 
+float Spawn::GetDistance(Spawn* spawn, float x1, float y1, float z1, bool includeRadius) {
+	float ret = 0;
+
+	if (spawn)
+	{
+		float radius = 0.0f;
+		if (includeRadius)
+			radius = CalculateRadius(spawn);
+		ret = GetDistance(x1, y1, z1, spawn->GetX(), spawn->GetY(), spawn->GetZ()) - radius;
+	}
+
+	// maybe distance against ourselves, in that case we want to nullify the radius check
+	if (ret < 0)
+		ret = 0.0f;
+
+	return ret;
+}
+
+float Spawn::CalculateRadius(Spawn* target)
+{
+	float srcRadius = short_to_float(appearance.pos.collision_radius);
+	if (target)
+	{
+		float targRadius = short_to_float(target->appearance.pos.collision_radius);
+		return (targRadius / 32.0f) + (srcRadius / 32.0f);
+	}
+	else
+		return (srcRadius / 32.0f);
+}
+
 int32 Spawn::GetRespawnTime(){
 	return respawn;
 }
@@ -1841,19 +1886,19 @@ void Spawn::ProcessMovement(bool isSpawnListLocked){
 				SetSpeed(speed);
 			}
 			MovementLocation* loc = GetCurrentRunningLocation();
-			if (GetDistance(followTarget, true) <= MAX_COMBAT_RANGE || (loc && loc->x == GetX() && loc->y == GetY() && loc->z == GetZ())) {
+			if ((GetDistance(followTarget, true) <= rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat()) || (loc && loc->x == GetX() && loc->y == GetY() && loc->z == GetZ())) {
 				ClearRunningLocations();
 				CalculateRunningLocation(true);
 			}
 			else if (loc) {
-				float distance = GetDistance(loc->x, loc->y, loc->z, followTarget->GetX(), followTarget->GetY(), followTarget->GetZ());
-				if (distance > MAX_COMBAT_RANGE) {
-					MoveToLocation(followTarget, MAX_COMBAT_RANGE);
+				float distance = GetDistance(followTarget, loc->x, loc->y, loc->z);
+				if (distance > rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat()) {
+					MoveToLocation(followTarget, rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat());
 					CalculateRunningLocation();
 				}
 			}
 			else {
-				MoveToLocation(followTarget, MAX_COMBAT_RANGE);
+				MoveToLocation(followTarget, rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat());
 				CalculateRunningLocation();
 			}
 		}

+ 6 - 2
EQ2/source/WorldServer/Spawn.h

@@ -590,9 +590,13 @@ public:
 	int32 GetID(){
 		return id;
 	}
-	float GetDistance(Spawn* spawn, bool ignore_y = false);
-	float GetDistance(float x, float y, float z, bool ignore_y = false);
 	float GetDistance(float x1, float y1, float z1, float x2, float y2, float z2);
+	float GetDistance(float x, float y, float z, float radius, bool ignore_y = false);
+	float GetDistance(float x, float y, float z, bool ignore_y = false);
+	float GetDistance(Spawn* spawn, bool ignore_y = false, bool includeRadius=true);
+	float GetDistance(Spawn* spawn, float x1, float y1, float z1, bool includeRadius=true);
+	float CalculateRadius(Spawn* target);
+
 	int8 GetEncounterLevel(){
 		return appearance.encounter_level;
 	}

+ 24 - 1
EQ2/source/common/MiscFunctions.cpp

@@ -922,4 +922,27 @@ strlcpy(char *dst, const char *src, size_t size) {
 	}
 
 	return(s - src - 1);
-}
+}
+
+float short_to_float(const ushort x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits
+	const uint32 e = (x & 0x7C00) >> 10; // exponent
+	const uint32 m = (x & 0x03FF) << 13; // mantissa
+	const uint32 v = as_uint((float)m) >> 23; // evil log2 bit hack to count leading zeros in denormalized format
+	return as_float((x & 0x8000) << 16 | (e != 0) * ((e + 112) << 23 | m) | ((e == 0) & (m != 0)) * ((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000))); // sign : normalized : denormalized
+}
+
+uint32 float_to_int(const float x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits
+	const uint32 b = as_uint(x) + 0x00001000; // round-to-nearest-even: add last bit after truncated mantissa
+	const uint32 e = (b & 0x7F800000) >> 23; // exponent
+	const uint32 m = b & 0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
+	return (b & 0x80000000) >> 16 | (e > 112)* ((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101))* ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143) * 0x7FFF; // sign : normalized : denormalized : saturate
+}
+
+uint32 as_uint(const float x) {
+	return *(uint32*)&x;
+}
+
+float as_float(const uint32 x) {
+	return *(float*)&x;
+}
+

+ 5 - 0
EQ2/source/common/MiscFunctions.h

@@ -80,6 +80,11 @@ int16 GetOpcodeVersion(int16 version);
 void SleepMS(int32 milliseconds);
 size_t strlcpy(char *dst, const char *src, size_t size);
 
+float short_to_float(const ushort x);
+uint32 float_to_int(const float x);
+uint32 as_uint(const float x);
+float as_float(const uint32 x);
+
 bool INIReadBool(FILE *f, const char *section, const char *property, bool *out);
 bool INIReadInt(FILE *f, const char *section, const char *property, int *out);