Browse Source

Code changes for region maps (Water!), additional lua functions

Issue #71

- Region Maps support water!  Later on to add lava/no fly/other potential regions
- new LUA functions:
* InWater(spawn)
 * HasControlEffect(spawn, type)
* GetBaseAggroRadius(spawn)
* GetAggroRadius(spawn)
* SetAggroRadius(spawn, distance, override) - override true sets base to this, otherwise its temporary and doesn't impact GetBaseAggroRadius
* SetDeity(spawn, value)
Image 3 years ago
parent
commit
9290bceb60

+ 1 - 1
EQ2/source/WorldServer/CMakeLists.txt

@@ -14,6 +14,6 @@ add_executable(eq2world ${WORLD_SRCS} ${COMMON_SRCS} ${LUA_SRCS})
 
 set(RECAST_LIBRARIES -L${CMAKE_SOURCE_DIR}/EQ2/source/depends/recastnavigation/RecastDemo/Build/gmake/lib/Debug -lDebugUtils -lDetour -lDetourCrowd -lDetourTileCache -lRecast)
 
-target_include_directories(eq2world PUBLIC ${MySQL_INCLUDE_DIRS} ../common/ ../depends/recastnavigation/Detour/Include)
+target_include_directories(eq2world PUBLIC ${MySQL_INCLUDE_DIRS} ../common/ ../depends/recastnavigation/Detour/Include ../depends/)
 target_link_libraries(eq2world PUBLIC ${MySQL_LIBRARIES} ${ZLIB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${RECAST_LIBRARIES})
 

+ 30 - 3
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -3545,6 +3545,24 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 						client->Message(CHANNEL_COLOR_YELLOW, "Best Z for %s is %f", spawn->GetName(), bestZ);
 						break;
 					}
+					else if (ToLower(string(sep->arg[0])) == "inwater")
+					{
+						glm::vec3 targPosZ(cmdTarget->GetX(), cmdTarget->GetZ(), cmdTarget->GetY());
+						float bestZ = client->GetPlayer()->FindDestGroundZ(targPosZ, cmdTarget->GetYOffset());
+						if ( bestZ == BEST_Z_INVALID )
+							bestZ = -999999.0f;
+							
+						glm::vec3 targPos(cmdTarget->GetY(), cmdTarget->GetX(), cmdTarget->GetZ());
+
+						if (client->GetCurrentZone()->regionmap == nullptr)
+							client->SimpleMessage(CHANNEL_COLOR_RED, "No water map for zone.");
+						else
+						{
+							bool inWater = cmdTarget->InWater();
+							client->Message(CHANNEL_COLOR_YELLOW, "%s is %s.", cmdTarget->GetName(), inWater ? "in water" : "out of water");
+						}
+						break;
+					}
 					else if (ToLower(string(sep->arg[0])) == "pathto")
 					{
 
@@ -4011,9 +4029,18 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 							client->SimpleMessage(CHANNEL_ERROR,"Unable to flag character.  Unknown reason.");
 					}
 				}else{
-					client->SimpleMessage(CHANNEL_COLOR_YELLOW,"Usage: /flag {name} {new_status}");
-					client->SimpleMessage(CHANNEL_COLOR_YELLOW," Standard User: 0");
-					client->SimpleMessage(CHANNEL_COLOR_YELLOW," Admin User: 100");
+					sint16 status = database.GetCharacterAdminStatus(client->GetPlayer()->GetName());
+					if(status != client->GetAdminStatus())
+					{
+						client->Message(CHANNEL_COLOR_YELLOW,"Flag status was changed from %i to %i.",status,client->GetAdminStatus());
+						client->SetAdminStatus(status);
+					}
+					else
+					{
+						client->SimpleMessage(CHANNEL_COLOR_YELLOW,"Usage: /flag {name} {new_status}");
+						client->SimpleMessage(CHANNEL_COLOR_YELLOW," Standard User: 0");
+						client->SimpleMessage(CHANNEL_COLOR_YELLOW," Admin User: 100");
+					}
 				}
 				break;
 			}

+ 12 - 0
EQ2/source/WorldServer/Entity.cpp

@@ -2526,6 +2526,18 @@ void Entity::AddSkillBonus(int32 spell_id, int32 skill_id, float value) {
 	return;
 }
 
+bool Entity::HasControlEffect(int8 type)
+{
+	if (type >= CONTROL_MAX_EFFECTS)
+		return false;
+
+	MutexList<LuaSpell*>* spell_list = control_effects[type];
+	if (!spell_list || spell_list->size(true) == 0)
+		return false;
+
+	return true;
+}
+
 void Entity::HaltMovement()
 {
 	this->ClearRunningLocations();

+ 2 - 0
EQ2/source/WorldServer/Entity.h

@@ -661,6 +661,8 @@ public:
 	bool IsSnared();
 	float GetHighestSnare();
 
+	bool HasControlEffect(int8 type);
+
 	void HaltMovement();
 
 

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

@@ -2399,6 +2399,92 @@ int EQ2Emu_lua_RemoveControlEffect(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_HasControlEffect(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	int8 type = lua_interface->GetInt8Value(state, 2);
+
+	bool hasEffect = false;
+
+	if (!spawn)
+		lua_interface->LogError("%s: LUA HasControlEffect error: Could not find spawn.", lua_interface->GetScriptName(state));
+	else if (!spawn->IsEntity())
+		lua_interface->LogError("%s: LUA HasControlEffect error: spawn %s is not an entity!.", lua_interface->GetScriptName(state), spawn->GetName());
+	else if (type < CONTROL_MAX_EFFECTS)
+		hasEffect = ((Entity*)spawn)->HasControlEffect(type);
+	else
+		lua_interface->LogError("%s: LUA HasControlEffect unhandled control effect type of %u.", lua_interface->GetScriptName(state), type);
+
+	lua_interface->SetBooleanValue(state, hasEffect);
+
+	return 1;
+}
+
+int EQ2Emu_lua_GetBaseAggroRadius(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+
+	float distance = 0.0f;
+
+	if (!spawn)
+		lua_interface->LogError("%s: LUA GetBaseAggroRadius error: Could not find spawn.", lua_interface->GetScriptName(state));
+	else if (!spawn->IsNPC())
+		lua_interface->LogError("%s: LUA GetBaseAggroRadius error: spawn %s is not an NPC!.", lua_interface->GetScriptName(state), spawn->GetName());
+	else
+		distance = ((NPC*)spawn)->GetBaseAggroRadius();
+
+	lua_interface->SetFloatValue(state, distance);
+
+	return 1;
+}
+
+int EQ2Emu_lua_GetAggroRadius(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+
+	float distance = 0.0f;
+
+	if (!spawn)
+		lua_interface->LogError("%s: LUA GetAggroRadius error: Could not find spawn.", lua_interface->GetScriptName(state));
+	else if (!spawn->IsNPC())
+		lua_interface->LogError("%s: LUA GetAggroRadius error: spawn %s is not an NPC!.", lua_interface->GetScriptName(state), spawn->GetName());
+	else
+		distance = ((NPC*)spawn)->GetAggroRadius();
+
+	lua_interface->SetFloatValue(state, distance);
+
+	return 1;
+}
+
+int EQ2Emu_lua_SetAggroRadius(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	float distance = lua_interface->GetFloatValue(state, 2);
+	bool override = lua_interface->GetBooleanValue(state, 3);
+
+	bool result = false;
+
+	lua_interface->ResetFunctionStack(state);
+
+	if (!spawn)
+		lua_interface->LogError("%s: LUA SetAggroRadius error: Could not find spawn.", lua_interface->GetScriptName(state));
+	else if (!spawn->IsNPC())
+		lua_interface->LogError("%s: LUA SetAggroRadius error: spawn %s is not an NPC!.", lua_interface->GetScriptName(state), spawn->GetName());
+	else
+	{
+		((NPC*)spawn)->SetAggroRadius(distance, override);
+		result = true;
+	}
+
+	lua_interface->SetBooleanValue(state, result);
+
+	return 1;
+}
+
 int EQ2Emu_lua_SetIntBase(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -2464,6 +2550,34 @@ int EQ2Emu_lua_SetStrBase(lua_State* state) {
 	return 0;
 }
 
+int EQ2Emu_lua_SetDeity(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	int8 value = lua_interface->GetInt8Value(state, 2);
+	if (spawn && spawn->IsEntity()) {
+		((Entity*)spawn)->SetDeity(value);
+		if (spawn->IsPlayer())
+			((Player*)spawn)->SetCharSheetChanged(true);
+	}
+
+	lua_interface->ResetFunctionStack(state);
+	return 0;
+}
+
+int EQ2Emu_lua_GetDeity(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	if (spawn && spawn->IsEntity()) {
+		int8 deity = ((Entity*)spawn)->GetDeity();
+		lua_interface->SetInt32Value(state, deity);
+		return 1;
+	}
+	return 0;
+}
+
+
 int EQ2Emu_lua_SetInt(lua_State* state) {
 	if (!lua_interface)
 		return 0;
@@ -10785,4 +10899,15 @@ int EQ2Emu_lua_CastCustomSpell(lua_State* state) {
 
 	target->GetZone()->ProcessSpell(NULL, (Entity*)caster, (Entity*)target, true, false, spell, 0);
 	return 0;
-}
+}
+
+int EQ2Emu_lua_InWater(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	if (spawn) {
+		lua_interface->SetBooleanValue(state, spawn->InWater());
+		return 1;
+	}
+	return 0;
+}

+ 11 - 0
EQ2/source/WorldServer/LuaFunctions.h

@@ -54,6 +54,15 @@ int EQ2Emu_lua_AddSkillBonus(lua_State* state);
 int EQ2Emu_lua_RemoveSkillBonus(lua_State* state);
 int EQ2Emu_lua_AddControlEffect(lua_State* state);
 int EQ2Emu_lua_RemoveControlEffect(lua_State* state);
+int EQ2Emu_lua_HasControlEffect(lua_State* state);
+
+int EQ2Emu_lua_GetBaseAggroRadius(lua_State* state);
+int EQ2Emu_lua_GetAggroRadius(lua_State* state);
+int EQ2Emu_lua_SetAggroRadius(lua_State* state);
+
+int EQ2Emu_lua_SetDeity(lua_State* state);
+int EQ2Emu_lua_GetDeity(lua_State* state);
+
 int EQ2Emu_lua_SetInt(lua_State* state);
 int EQ2Emu_lua_SetWis(lua_State* state);
 int EQ2Emu_lua_SetSta(lua_State* state);
@@ -490,4 +499,6 @@ int EQ2Emu_lua_GetSpellDataIndex(lua_State* state);
 
 int EQ2Emu_lua_SetSpellDisplayEffect(lua_State* state);
 int EQ2Emu_lua_GetSpellDisplayEffect(lua_State* state);
+
+int EQ2Emu_lua_InWater(lua_State* state);
 #endif

+ 14 - 0
EQ2/source/WorldServer/LuaInterface.cpp

@@ -775,7 +775,19 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "RemoveSkillBonus", EQ2Emu_lua_RemoveSkillBonus);
 	lua_register(state, "AddControlEffect", EQ2Emu_lua_AddControlEffect);
 	lua_register(state, "RemoveControlEffect", EQ2Emu_lua_RemoveControlEffect);
+	lua_register(state, "HasControlEffect", EQ2Emu_lua_HasControlEffect);
+
+	lua_register(state, "GetBaseAggroRadius", EQ2Emu_lua_GetBaseAggroRadius);
+	lua_register(state, "GetAggroRadius", EQ2Emu_lua_GetAggroRadius);
+	lua_register(state, "SetAggroRadius", EQ2Emu_lua_SetAggroRadius);
+
 	lua_register(state, "GetCurrentZoneSafeLocation", EQ2Emu_lua_GetCurrentZoneSafeLocation);
+
+
+	lua_register(state, "SetDeity", EQ2Emu_lua_SetDeity);
+	lua_register(state, "GetDeity", EQ2Emu_lua_GetDeity);
+
+
 	lua_register(state, "GetInt", EQ2Emu_lua_GetInt);
 	lua_register(state, "GetWis", EQ2Emu_lua_GetWis);
 	lua_register(state, "GetSta", EQ2Emu_lua_GetSta);
@@ -1119,6 +1131,8 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 
 	lua_register(state, "SetSpellDisplayEffect", EQ2Emu_lua_SetSpellDisplayEffect);
 	lua_register(state, "GetSpellDisplayEffect", EQ2Emu_lua_GetSpellDisplayEffect);
+
+	lua_register(state, "InWater", EQ2Emu_lua_InWater);
 }
 
 void LuaInterface::LogError(const char* error, ...)  {

+ 8 - 4
EQ2/source/WorldServer/NPC.cpp

@@ -83,7 +83,7 @@ NPC::NPC(NPC* old_npc){
 		SetPrimarySkillList(old_npc->GetPrimarySkillList());
 		SetSecondarySkillList(old_npc->GetSecondarySkillList());
 		SetEquipmentListID(old_npc->GetEquipmentListID());
-		SetAggroRadius(old_npc->GetAggroRadius());
+		SetAggroRadius(old_npc->GetBaseAggroRadius());
 		SetCastPercentage(old_npc->GetCastPercentage());
 		SetRandomize(old_npc->GetRandomize());	
 		if(appearance.randomize > 0)
@@ -125,7 +125,8 @@ void NPC::Initialize(){
 	attack_resume_needed = false;
 	MMovementLoop.SetName("NPC::MMovementLoop");
 	last_movement_update = Timer::GetCurrentTime2();
-	aggro_radius = 0;
+	aggro_radius = 0.0f;
+	base_aggro_radius = 0.0f;
 	skills = 0;
 	spells = 0;
 	runback = 0;
@@ -192,7 +193,7 @@ void NPC::Runback(){
 	m_runningBack = true;
 	SetSpeed(GetMaxSpeed()*2);
 
-	if (IsFlying() && CheckLoS(glm::vec3(runback->x, runback->z, runback->y + 1.0f), glm::vec3(GetX(), GetZ(), GetY() + 1.0f)))
+	if ((IsFlyingCreature() || IsWaterCreature()) && CheckLoS(glm::vec3(runback->x, runback->z, runback->y + 1.0f), glm::vec3(GetX(), GetZ(), GetY() + 1.0f)))
 	{
 		FaceTarget(runback->x, runback->z);
 		ClearRunningLocations();
@@ -775,7 +776,10 @@ Spell* NPC::GetNextBuffSpell() {
 	return ret;
 }
 
-void NPC::SetAggroRadius(float radius){
+void NPC::SetAggroRadius(float radius, bool overrideBaseValue){
+	if (base_aggro_radius == 0.0f || overrideBaseValue)
+		base_aggro_radius = radius;
+
 	aggro_radius = radius;
 }
 

+ 3 - 1
EQ2/source/WorldServer/NPC.h

@@ -104,8 +104,9 @@ public:
 	int32	GetEquipmentListID();
 	Spell*	GetNextSpell(float distance);
 	virtual Spell*	GetNextBuffSpell();
-	void	SetAggroRadius(float radius);
+	void	SetAggroRadius(float radius, bool overrideBaseValue = false);
 	float	GetAggroRadius();
+	float	GetBaseAggroRadius() { return base_aggro_radius; }
 	void	SetCastPercentage(int8 percentage);
 	int8	GetCastPercentage();
 	void	SetSkills(map<string, Skill*>* in_skills);
@@ -148,6 +149,7 @@ private:
 	MovementLocation* runback;
 	int8	cast_percentage;
 	float	aggro_radius;
+	float	base_aggro_radius;
 	Spell*	GetNextSpell(float distance, int8 type);
 	map<string, Skill*>* skills;
 	vector<Spell*>* spells;

+ 4 - 1
EQ2/source/WorldServer/NPC_AI.cpp

@@ -82,8 +82,11 @@ void Brain::Think() {
 			}
 			m_body->FaceTarget(target);
 
+			bool breakWaterPursuit = false;
+			if (m_body->IsWaterCreature() && !target->InWater())
+				breakWaterPursuit = true;
 			// Check to see if the NPC has exceeded the max chase distance
-			if (run_back_distance > MAX_CHASE_DISTANCE) {
+			if (run_back_distance > MAX_CHASE_DISTANCE || breakWaterPursuit) {
 				LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "Run back distance is greater then max chase distance, run_back_distance = %f", run_back_distance);
 				// Over the max chase distance, Check to see if the target is is a client
 				Client* client = target->GetZone()->GetClientBySpawn(target);

+ 45 - 6
EQ2/source/WorldServer/Spawn.cpp

@@ -114,6 +114,8 @@ Spawn::Spawn(){
 	pickup_unique_item_id = 0;
 	disable_sounds = false;
 	has_quests_required = false;
+	is_flying_creature = false;
+	is_water_creature = false;
 }
 
 Spawn::~Spawn(){
@@ -2614,7 +2616,7 @@ void Spawn::MoveToLocation(Spawn* spawn, float distance, bool immediate, bool ma
 
 	if (!IsPlayer() && distance > 0.0f)
 	{
-		if (IsFlying() && CheckLoS(spawn))
+		if ((IsFlyingCreature() || IsWaterCreature()) && CheckLoS(spawn))
 		{
 			if (immediate)
 				ClearRunningLocations();
@@ -2661,7 +2663,7 @@ void Spawn::ProcessMovement(bool isSpawnListLocked){
 		FixZ(true);
 
 		int32 newGrid = GetZone()->Grid->GetGridID(this);
-		if (!IsFlying() && newGrid != 0 && newGrid != appearance.pos.grid_id)
+		if (!IsFlyingCreature() && newGrid != 0 && newGrid != appearance.pos.grid_id)
 			SetPos(&(appearance.pos.grid_id), newGrid);
 
 		forceMapCheck = false;
@@ -3085,7 +3087,7 @@ bool Spawn::CalculateChange(){
 				}
 
 				int32 newGrid = GetZone()->Grid->GetGridID(this);
-				if (!IsFlying() && newGrid != 0 && newGrid != appearance.pos.grid_id)
+				if (!IsFlyingCreature() && newGrid != 0 && newGrid != appearance.pos.grid_id)
 					SetPos(&(appearance.pos.grid_id), newGrid);
 			}
 		}
@@ -3124,7 +3126,7 @@ void Spawn::CalculateRunningLocation(bool stop){
 	{
 		if (GetDistance(GetTarget()) > rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat())
 		{
-			if (IsFlying() && CheckLoS(GetTarget()))
+			if ((IsFlyingCreature() || IsWaterCreature()) && CheckLoS(GetTarget()))
 				AddRunningLocation(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetSpeed(), 0, false);
 			else
 				GetZone()->movementMgr->NavigateTo((Entity*)this, GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ());
@@ -3582,7 +3584,7 @@ float Spawn::GetFixedZ(const glm::vec3& destination, int32 z_find_offset) {
 
 
 void Spawn::FixZ(bool forceUpdate) {
-	if (IsPlayer() || IsFlying() || !GetZone() || (IsObject() && GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE)) {
+	if (IsPlayer() || IsFlyingCreature() || !GetZone() || (IsObject() && GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE)) {
 		return;
 	}
 	/*
@@ -3593,9 +3595,22 @@ void Spawn::FixZ(bool forceUpdate) {
 	if (zone->watermap && zone->watermap->InLiquid(m_Position)) {
 		return;
 	}*/
-
+	
+	// we do the inwater check here manually to avoid double calling for a Z coordinate
 	glm::vec3 current_loc(GetX(), GetZ(), GetY());
 	float new_z = GetFixedZ(current_loc, 1);
+	
+	if ( GetZone()->regionmap != nullptr )
+	{
+		glm::vec3 targPos(GetY(), GetX(), GetZ());
+		float bestZ = -999999.0f;
+		if ( new_z != BEST_Z_INVALID )
+			bestZ = new_z - 1.0f;
+		
+		if(GetZone()->regionmap->InWater(targPos, bestZ))
+			return;
+	}
+	
 	if (new_z == GetY())
 		return;
 
@@ -3799,4 +3814,28 @@ void Spawn::RemoveSpawnFromPlayer(Player* player)
 	m_Update.writelock(__FUNCTION__, __LINE__);
 	player->RemoveSpawn(this); // sets it as removed
 	m_Update.releasewritelock(__FUNCTION__, __LINE__);
+}
+
+bool Spawn::InWater()
+{
+	bool inWater = false;
+
+	if (GetZone()->regionmap != nullptr)
+	{
+		glm::vec3 current_loc(GetX(), GetZ(), GetY());
+		float new_z = GetFixedZ(current_loc, 1);
+				
+		if ( GetZone()->regionmap != nullptr )
+		{
+			glm::vec3 targPos(GetY(), GetX(), GetZ());
+			float bestZ = -999999.0f;
+			if ( new_z != BEST_Z_INVALID )
+				bestZ = new_z;
+					
+			if(GetZone()->regionmap->InWater(targPos, bestZ))
+			inWater = true;
+		}
+	}
+
+	return inWater;
 }

+ 35 - 1
EQ2/source/WorldServer/Spawn.h

@@ -751,6 +751,8 @@ public:
 	void SetModelType(int16 model_type, bool setUpdateFlags = true){
 		SetInfo(&appearance.model_type, model_type, setUpdateFlags);
 		SetInfo(&appearance.soga_model_type, model_type, setUpdateFlags);
+		SetFlyingCreature();
+		SetWaterCreature();
 	}
 	int16 GetSogaModelType(){
 		return appearance.soga_model_type;
@@ -759,7 +761,37 @@ public:
 		return appearance.model_type;
 	}
 	
-	bool IsFlying() { return (GetInitialState() == 49156); }
+	bool IsFlyingCreature() { return is_flying_creature; }
+	bool IsWaterCreature() { return is_water_creature; }
+	bool InWater();
+
+	void SetFlyingCreature() {
+		is_flying_creature = false;
+		switch (GetModelType())
+		{
+		case 260:
+		case 295:
+			is_flying_creature = true;
+			break;
+		}
+	}
+	
+	void SetWaterCreature() {
+		is_water_creature = false;
+
+		switch (GetModelType())
+		{
+		case 194:
+		case 204:
+		case 210:
+		case 241:
+		case 242:
+		case 254:
+		case 20828:
+			is_water_creature = true;
+			break;
+		}
+	}
 	void SetPrimaryCommand(const char* name, const char* command, float distance = 10);
 	void SetPrimaryCommands(vector<EntityCommand*>* commands);
 	void SetSecondaryCommands(vector<EntityCommand*>* commands);
@@ -1008,6 +1040,8 @@ public:
 	int32	last_movement_update;
 	int32	last_location_update;
 	bool	forceMapCheck;
+	bool	is_water_creature;
+	bool	is_flying_creature;
 
 	bool following;
 	bool	IsPet() { return is_pet; }

+ 1 - 1
EQ2/source/WorldServer/Zone/mob_movement_manager.cpp

@@ -1,7 +1,7 @@
 #include "mob_movement_manager.h"
 #include "../Entity.h"
 #include "../zoneserver.h"
-#include "water_map.h"
+#include "region_map.h"
 #include "map.h"
 #include "../../common/timer.h"
 #include "pathfinder_interface.h"

+ 1 - 1
EQ2/source/WorldServer/Zone/pathfinder_nav_mesh.cpp

@@ -6,7 +6,7 @@
 #include <DetourCommon.h>
 #include <DetourNavMeshQuery.h>
 #include "../zoneserver.h"
-#include "water_map.h"
+#include "region_map.h"
 #include "../client.h"
 
 struct PathfinderNavmesh::Implementation

+ 67 - 0
EQ2/source/WorldServer/Zone/region_map.cpp

@@ -0,0 +1,67 @@
+
+
+#include "region_map.h"
+#include "region_map_v1.h"
+#include "../../common/Log.h"
+
+#include <algorithm>
+#include <cctype>
+#include <stdio.h>
+#include <string.h>
+#include <fstream>
+
+/**
+ * @param name
+ * @return
+ */
+inline bool file_exists(const std::string& name) {
+	std::ifstream f(name.c_str());
+	return f.good();
+}
+
+/**
+ * @param zone_name
+ * @return
+ */
+RegionMap* RegionMap::LoadRegionMapfile(std::string zone_name) {
+	std::string filename = "Regions/";
+	filename += zone_name;
+	filename += ".EQ2Region";
+	FILE* f = fopen(filename.c_str(), "rb");
+
+	LogWrite(REGION__DEBUG, 7, "Region", "Attempting load of %s", filename.c_str());
+
+	if (!f)
+	{
+		LogWrite(REGION__ERROR, 7, "Region", "Failed to load of %s", filename.c_str());
+		return nullptr;
+	}
+
+	// Read the string for the zone file name this was created for
+	int8 strSize;
+	char name[256];
+	fread(&strSize, sizeof(int8), 1, f);
+	LogWrite(REGION__DEBUG, 7, "Region", "strSize = %u", strSize);
+
+	size_t len = fread(&name, sizeof(char), strSize, f);
+	name[len] = '\0';
+	LogWrite(REGION__DEBUG, 7, "Region", "name = %s", name);
+
+	string fileName(name);
+	std::size_t found = fileName.find(zone_name);
+	// Make sure file contents are for the correct zone
+	if (found == std::string::npos) {
+		fclose(f);
+		LogWrite(REGION__ERROR, 0, "Region", "WaterMap::LoadWaterMapfile() map contents (%s) do not match its name (%s).", &name, zone_name.c_str());
+		return nullptr;
+	}
+	
+	int32 regionMapVersion;
+	fread(&regionMapVersion, sizeof(int32), 1, f);
+	LogWrite(REGION__INFO, 0, "Region", "Loading %s RegionMapVersion = %u", name, regionMapVersion);
+
+	RegionMapV1* regionmap = new RegionMapV1();
+	regionmap->Load(f);
+
+	return regionmap;
+}

+ 48 - 0
EQ2/source/WorldServer/Zone/region_map.h

@@ -0,0 +1,48 @@
+#ifndef EQ2EMU_REGION_MAP_H
+#define EQ2EMU_REGION_MAP_H
+
+#include "../../common/types.h"
+#include "position.h"
+#include <string>
+
+enum WaterRegionType : int {
+	RegionTypeUnsupported = -2,
+	RegionTypeUntagged = -1,
+	RegionTypeNormal = 0,
+	RegionTypeWater = 1,
+	RegionTypeLava = 2,
+	RegionTypeZoneLine = 3,
+	RegionTypePVP = 4,
+	RegionTypeSlime = 5,
+	RegionTypeIce = 6,
+	RegionTypeVWater = 7
+};
+
+enum WaterRegionClass : int32 {
+	ClassWaterVolume = 0, // matching .region file type by name "watervol"
+	ClassWaterRegion = 1, // matching .region file type by name "waterregion"
+	ClassWaterRegion2 = 2, // represents .region file name "water_region" potentially defunct and just a WaterVolume (0)
+	ClassWaterOcean = 3, // represents .region file with "ocean" and a select node as a parent
+	ClassWaterCavern = 4, // represents .region file with matches on name "ocean" and "water"
+	ClassWaterOcean2 = 5 // represents .region file with matches on name "ocean" without previous matches (no select node parent and no water string match)
+};
+
+class RegionMap
+{
+public:
+	RegionMap() { }
+	virtual ~RegionMap() { }
+
+	static RegionMap* LoadRegionMapfile(std::string zone_name);
+	virtual WaterRegionType ReturnRegionType(const glm::vec3& location, float belowY = -999999.0f) const = 0;
+	virtual bool InWater(const glm::vec3& location, float belowY = -999999.0f) const = 0;
+	virtual bool InLava(const glm::vec3& location) const = 0;
+	virtual bool InLiquid(const glm::vec3& location) const = 0;
+	virtual bool InPvP(const glm::vec3& location) const = 0;
+	virtual bool InZoneLine(const glm::vec3& location) const = 0;
+
+protected:
+	virtual bool Load(FILE *fp) { return false; }
+};
+
+#endif

+ 323 - 0
EQ2/source/WorldServer/Zone/region_map_v1.cpp

@@ -0,0 +1,323 @@
+#include "region_map_v1.h"
+#include "../../common/Log.h"
+
+RegionMapV1::RegionMapV1() {
+
+}
+
+RegionMapV1::~RegionMapV1() {
+	map<Region_Node*, ZBSP_Node*>::const_iterator itr;
+	int region_num = 0;
+	for (itr = Regions.begin(); itr != Regions.end();)
+	{
+		Region_Node* node = itr->first;
+		ZBSP_Node* bsp_node = itr->second;
+		map<Region_Node*, ZBSP_Node*>::const_iterator deleteItr = itr;
+		itr++;
+		Regions.erase(deleteItr);
+		safe_delete(node);
+		safe_delete(bsp_node);
+	}
+
+	Regions.clear();
+}
+
+WaterRegionType RegionMapV1::ReturnRegionType(const glm::vec3& location, float belowY) const {
+	return BSPReturnRegionType(1, glm::vec3(location.y, location.x + 0.5f, location.z));
+}
+
+bool RegionMapV1::InWater(const glm::vec3& location, float belowY) const {
+	return ReturnRegionType(location, belowY) == RegionTypeWater;
+}
+
+bool RegionMapV1::InLava(const glm::vec3& location) const {
+	return ReturnRegionType(location) == RegionTypeLava;
+}
+
+bool RegionMapV1::InLiquid(const glm::vec3& location) const {
+	return InWater(location) || InLava(location);
+}
+
+bool RegionMapV1::InPvP(const glm::vec3& location) const {
+	return ReturnRegionType(location) == RegionTypePVP;
+}
+
+bool RegionMapV1::InZoneLine(const glm::vec3& location) const {
+	return ReturnRegionType(location) == RegionTypeZoneLine;
+}
+
+bool RegionMapV1::Load(FILE* fp) {
+	uint32 region_size;
+	if (fread(&region_size, sizeof(region_size), 1, fp) != 1) {
+		return false;
+	}
+
+	LogWrite(REGION__DEBUG, 0, "RegionMap", "region count = %u", region_size);
+
+	for (int i = 0; i < region_size; i++)
+	{
+		uint32 region_num;
+		if (fread(&region_num, sizeof(region_num), 1, fp) != 1) {
+			return false;
+		}
+
+		uint32 region_type;
+		if (fread(&region_type, sizeof(region_type), 1, fp) != 1) {
+			return false;
+		}
+
+		float x, y, z, dist;
+		if (fread(&x, sizeof(x), 1, fp) != 1) {
+			return false;
+		}
+		if (fread(&y, sizeof(y), 1, fp) != 1) {
+			return false;
+		}
+		if (fread(&z, sizeof(z), 1, fp) != 1) {
+			return false;
+		}
+		if (fread(&dist, sizeof(dist), 1, fp) != 1) {
+			return false;
+		}
+
+		uint32 bsp_tree_size;
+		if (fread(&bsp_tree_size, sizeof(bsp_tree_size), 1, fp) != 1) {
+			return false;
+		}
+
+		LogWrite(REGION__DEBUG, 0, "RegionMap", "region x,y,z,dist = %f, %f, %f, %f, region bsp tree size: %u", x, y, z, dist, bsp_tree_size);
+
+		ZBSP_Node* BSP_Root = new ZBSP_Node[bsp_tree_size];
+		if (fread(BSP_Root, sizeof(ZBSP_Node), bsp_tree_size, fp) != bsp_tree_size) {
+			LogWrite(REGION__ERROR, 0, "RegionMap", "Failed to load region.");
+			return false;
+		}
+
+		Region_Node* tmpNode = new Region_Node;
+		tmpNode->x = x;
+		tmpNode->y = y;
+		tmpNode->z = z;
+		tmpNode->dist = dist;
+		tmpNode->region_type = region_type;
+		Regions.insert(make_pair(tmpNode, BSP_Root));
+	}
+
+	fclose(fp);
+
+	LogWrite(REGION__DEBUG, 0, "RegionMap", "completed load!");
+
+	return true;
+}
+
+WaterRegionType RegionMapV1::BSPReturnRegionType(int32 node_number, const glm::vec3& location) const {
+	map<Region_Node*, ZBSP_Node*>::const_iterator itr;
+	int region_num = 0;
+	for (itr = Regions.begin(); itr != Regions.end(); itr++)
+	{
+		Region_Node* node = itr->first;
+		ZBSP_Node* BSP_Root = itr->second;
+
+		float x1 = node->x - location.x;
+		float y1 = node->y - location.y;
+		float z1 = node->z - location.z;
+		float dist = sqrt(x1 * x1 + y1 * y1 + z1 * z1);
+
+#ifdef REGIONDEBUG
+		printf("Region %i (%i) dist %f / node dist %f.  NodeXYZ: %f %f %f, XYZ: %f %f %f.\n", region_num, node->region_type, dist, node->dist, node->x, node->y, node->z, location.x, location.y, location.z);
+#endif
+
+		if (dist <= node->dist)
+		{
+			ZBSP_Node* BSP_Root = itr->second;
+			const ZBSP_Node* current_node = &BSP_Root[node_number - 1];
+
+			WaterRegionType regionType = RegionTypeUntagged;
+
+			if (node->region_type == ClassWaterRegion)
+				regionType = BSPReturnRegionWaterRegion(node, BSP_Root, node_number, location, dist);
+			else
+				regionType = BSPReturnRegionTypeNode(node, BSP_Root, node_number, location, dist);
+
+			if (regionType != RegionTypeNormal)
+				return regionType;
+		}
+		region_num++;
+	}
+
+	return(RegionTypeNormal);
+}
+
+WaterRegionType RegionMapV1::BSPReturnRegionTypeNode(const Region_Node* region_node, const ZBSP_Node* BSP_Root, int32 node_number, const glm::vec3& location, float distToNode) const {
+	const ZBSP_Node* current_node = &BSP_Root[node_number - 1];
+	float distance;
+
+#ifdef REGIONDEBUG
+	printf("left = %u, right %u\n", current_node->left, current_node->right);
+#endif
+
+	if (region_node->region_type == ClassWaterRegion2)
+	{
+		distance = (location.x * current_node->normal[0]) +
+			(location.y * current_node->normal[1]) +
+			(location.z * current_node->normal[2]) +
+			current_node->splitdistance;
+	}
+	else {
+		distance = (location.x * current_node->normal[0]) +
+			(location.y * current_node->normal[1]) +
+			(location.z * current_node->normal[2]) -
+			current_node->splitdistance;
+	}
+
+	float absDistance = distance;
+	if (absDistance < 0.0f)
+		absDistance *= -1.0f;
+
+	float absSplitDist = current_node->splitdistance;
+	if (absSplitDist < 0.0f)
+		absSplitDist *= -1.0f;
+
+#ifdef REGIONDEBUG
+	printf("distance = %f, normals: %f %f %f, location: %f %f %f, split distance: %f\n", distance, current_node->left, current_node->right, current_node->normal[0], current_node->normal[1], current_node->normal[2],
+		location.x, location.y, location.z, current_node->splitdistance);
+#endif
+
+	if ((current_node->left == 4294967294) &&
+		(current_node->right == 4294967295)) {
+		if (region_node->region_type == ClassWaterOcean || region_node->region_type == ClassWaterOcean2)
+		{
+			return EstablishDistanceAtAngle(region_node, current_node, distance, absDistance, absSplitDist, false);
+		}
+		else
+		{
+			if (distance > 0)
+				return(RegionTypeWater);
+			else
+				return RegionTypeNormal;
+		}
+	}
+	else if ((region_node->region_type == ClassWaterOcean || region_node->region_type == ClassWaterOcean2) && current_node->normal[1] != 1.0f && current_node->normal[1] != -1.0f)
+	{
+		float fraction = abs(current_node->normal[0] * current_node->normal[2]);
+		float diff = distToNode / region_node->dist;
+		if (distance > 0)
+			diff = distance * diff;
+
+#ifdef REGIONDEBUG
+		printf("Diff: %f (%f + %f), fraction %f\n", diff, distToNode, distance, fraction);
+#endif
+		if ((abs(diff) / 2.0f) > (absSplitDist * (1.0f / fraction)) * 2.0f)
+			return RegionTypeNormal;
+	}
+
+	if (distance == 0.0f) {
+		return(RegionTypeNormal);
+	}
+
+	if (distance > 0.0f) {
+
+#ifdef REGIONDEBUG
+		printf("to left node %i\n", current_node->left);
+#endif
+		if (current_node->left == 4294967294 && ((region_node->region_type == ClassWaterVolume || region_node->region_type == ClassWaterOcean2) ||
+			(region_node->region_type == ClassWaterOcean && current_node->normal[1] == 1.0f)))
+			return RegionTypeWater;
+		else if (current_node->left == -1 || current_node->left == -2) {
+			if (current_node->left == -2 && region_node->region_type == ClassWaterCavern)
+				return EstablishDistanceAtAngle(region_node, current_node, distance, absDistance, absSplitDist, true);
+			else
+				return(RegionTypeNormal);
+		}
+		return BSPReturnRegionTypeNode(region_node, BSP_Root, current_node->left + 1, location, distToNode);
+	}
+
+#ifdef REGIONDEBUG
+	printf("to right node %i, sign bit %i\n", current_node->right, signbit(current_node->normal[1]));
+#endif
+	if (current_node->right == -1) {
+		if (region_node->region_type == ClassWaterOcean2 && signbit(current_node->normal[1]) == 0 && absDistance < absSplitDist)
+			return RegionTypeWater;
+		else if ((region_node->region_type == ClassWaterOcean || region_node->region_type == ClassWaterOcean2) &&
+			(current_node->normal[1] > 0.0f && distance < 0.0f && absDistance < absSplitDist))
+		{
+			return(RegionTypeWater);
+		}
+		return(RegionTypeNormal);
+	}
+
+	return BSPReturnRegionTypeNode(region_node, BSP_Root, current_node->right + 1, location, distToNode);
+}
+
+
+WaterRegionType RegionMapV1::BSPReturnRegionWaterRegion(const Region_Node* region_node, const ZBSP_Node* BSP_Root, int32 node_number, const glm::vec3& location, float distToNode) const {
+	const ZBSP_Node* current_node = &BSP_Root[node_number - 1];
+	float distance;
+
+#ifdef REGIONDEBUG
+	printf("left = %u, right %u\n", current_node->left, current_node->right);
+#endif
+
+	distance = (location.x * current_node->normal[0]) +
+		(location.y * current_node->normal[1]) +
+		(location.z * current_node->normal[2]) -
+		current_node->splitdistance;
+
+#ifdef REGIONDEBUG
+	printf("distance = %f, normals: %f %f %f, location: %f %f %f, split distance: %f\n", distance, current_node->left, current_node->right, current_node->normal[0], current_node->normal[1], current_node->normal[2],
+		location.x, location.y, location.z, current_node->splitdistance);
+#endif
+
+	if (distance > 0.0f) {
+#ifdef REGIONDEBUG
+		printf("to left node %i\n", current_node->left);
+#endif
+		if (current_node->left == -1) {
+			return(RegionTypeNormal);
+		}
+		else if (current_node->left == 4294967294) {
+			return(RegionTypeWater);
+		}
+		return BSPReturnRegionWaterRegion(region_node, BSP_Root, current_node->left + 1, location, distToNode);
+	}
+
+#ifdef REGIONDEBUG
+	printf("to right node %i, sign bit %i\n", current_node->right, signbit(current_node->normal[1]));
+#endif
+
+	if (current_node->right == -1) {
+		return(RegionTypeNormal);
+	}
+
+	return BSPReturnRegionWaterRegion(region_node, BSP_Root, current_node->right + 1, location, distToNode);
+}
+
+WaterRegionType RegionMapV1::EstablishDistanceAtAngle(const Region_Node* region_node, const ZBSP_Node* current_node, float distance, float absDistance, float absSplitDist, bool checkEdgedAngle) const {
+	float fraction = abs(current_node->normal[0] * current_node->normal[2]);
+#ifdef REGIONDEBUG
+	printf("Distcheck: %f < %f\n", absDistance, absSplitDist);
+#endif
+	if (absDistance < absSplitDist &&
+		(current_node->normal[0] == 1.0f || current_node->normal[0] == -1.0f ||
+			(current_node->normal[1] == 1.0f && distance < 0.0f) ||
+			(current_node->normal[1] == -1.0f && distance > 0.0f)))
+	{
+		return RegionTypeWater;
+	}
+	else if (region_node->region_type == ClassWaterOcean2 || checkEdgedAngle)
+	{
+		if (current_node->normal[2] == 1.0f || current_node->normal[2] == -1.0f)
+			return RegionTypeNormal;
+		else if (current_node->normal[1] == 0.0f && (current_node->normal[0] < -0.5f || current_node->normal[0] > 0.5f) &&
+			((abs(absDistance * current_node->normal[0]) / 2.0f) < ((abs(absSplitDist * (1.0f / fraction))))))
+		{
+			return RegionTypeWater;
+		}
+		else if (current_node->normal[1] == 0.0f && (current_node->normal[2] < -0.5f || current_node->normal[2] > 0.5f) &&
+			((abs(absDistance * current_node->normal[2]) / 2.0f) < ((abs(absSplitDist * (1.0f / fraction))))))
+		{
+			return RegionTypeWater;
+		}
+	}
+
+	return RegionTypeNormal;
+}

+ 52 - 0
EQ2/source/WorldServer/Zone/region_map_v1.h

@@ -0,0 +1,52 @@
+#ifndef EQ2EMU_REGION_MAP_V1_H
+#define EQ2EMU_REGION_MAP_V1_H
+
+#include "region_map.h"
+#include <map>
+
+#pragma pack(1)
+typedef struct ZBSP_Node {
+	int32 node_number;
+	float normal[3], splitdistance;
+	int32 region;
+	int32 special;
+	int32 left, right;
+} ZBSP_Node;
+
+typedef struct Region_Node {
+	int32 region_type;
+	float x;
+	float y;
+	float z;
+	float dist;
+} Region_Node;
+#pragma pack()
+
+class RegionMapV1 : public RegionMap
+{
+public:
+	RegionMapV1();
+	~RegionMapV1();
+	
+	virtual WaterRegionType ReturnRegionType(const glm::vec3& location, float belowY = -999999.0f) const;
+	virtual bool InWater(const glm::vec3& location, float belowY = -999999.0f) const;
+	virtual bool InLava(const glm::vec3& location) const;
+	virtual bool InLiquid(const glm::vec3& location) const;
+	virtual bool InPvP(const glm::vec3& location) const;
+	virtual bool InZoneLine(const glm::vec3& location) const;
+	
+protected:
+	virtual bool Load(FILE *fp);
+
+private:
+	WaterRegionType BSPReturnRegionType(int32 node_number, const glm::vec3& location) const;
+	WaterRegionType BSPReturnRegionTypeNode(const Region_Node* node, const ZBSP_Node* BSP_Root, int32 node_number, const glm::vec3& location, float distToNode=0.0f) const;
+
+	WaterRegionType BSPReturnRegionWaterRegion(const Region_Node* node, const ZBSP_Node* BSP_Root, int32 node_number, const glm::vec3& location, float distToNode=0.0f) const;
+	map<Region_Node*, ZBSP_Node*> Regions;
+
+	WaterRegionType EstablishDistanceAtAngle(const Region_Node* region_node, const ZBSP_Node* current_node, float distance, float absDistance, float absSplitDist, bool checkEdgedAngle=false) const;
+	friend class RegionMap;
+};
+
+#endif

+ 0 - 26
EQ2/source/WorldServer/Zone/water_map.cpp

@@ -1,26 +0,0 @@
-
-
-#include "water_map.h"
-
-#include <algorithm>
-#include <cctype>
-#include <stdio.h>
-#include <string.h>
-#include <fstream>
-
-/**
- * @param name
- * @return
- */
-inline bool file_exists(const std::string& name) {
-	std::ifstream f(name.c_str());
-	return f.good();
-}
-
-/**
- * @param zone_name
- * @return
- */
-WaterMap* WaterMap::LoadWaterMapfile(std::string zone_name) {
-	return nullptr;
-}

+ 0 - 40
EQ2/source/WorldServer/Zone/water_map.h

@@ -1,40 +0,0 @@
-#ifndef EQEMU_WATER_MAP_H
-#define EQEMU_WATER_MAP_H
-
-#include "../../common/types.h"
-#include "position.h"
-#include <string>
-
-enum WaterRegionType : int {
-	RegionTypeUnsupported = -2,
-	RegionTypeUntagged = -1,
-	RegionTypeNormal = 0,
-	RegionTypeWater = 1,
-	RegionTypeLava = 2,
-	RegionTypeZoneLine = 3,
-	RegionTypePVP = 4,
-	RegionTypeSlime = 5,
-	RegionTypeIce = 6,
-	RegionTypeVWater = 7
-};
-
-class WaterMap
-{
-public:
-	WaterMap() { }
-	virtual ~WaterMap() { }
-
-	static WaterMap* LoadWaterMapfile(std::string zone_name);
-	virtual WaterRegionType ReturnRegionType(const glm::vec3& location) const = 0;
-	virtual bool InWater(const glm::vec3& location) const = 0;
-	virtual bool InVWater(const glm::vec3& location) const = 0;
-	virtual bool InLava(const glm::vec3& location) const = 0;
-	virtual bool InLiquid(const glm::vec3& location) const = 0;
-	virtual bool InPvP(const glm::vec3& location) const = 0;
-	virtual bool InZoneLine(const glm::vec3& location) const = 0;
-
-protected:
-	virtual bool Load(FILE *fp) { return false; }
-};
-
-#endif

+ 7 - 0
EQ2/source/WorldServer/zoneserver.cpp

@@ -158,6 +158,7 @@ ZoneServer::ZoneServer(const char* name) {
 	Grid = nullptr;
 	zonemap = nullptr;
 	pathing = nullptr;
+	regionmap = nullptr;
 	strcpy(zonesky_file,"");
 	
 	reloading = true;
@@ -215,6 +216,8 @@ ZoneServer::~ZoneServer() {
 	if (movementMgr != nullptr)
 		delete movementMgr;
 
+	if (regionmap != nullptr)
+		delete regionmap;
 	LogWrite(ZONE__INFO, 0, "Zone", "Completed zone shutdown of '%s'", zone_name);
 	--numzones;
 	UpdateWindowTitle(0);
@@ -282,6 +285,10 @@ void ZoneServer::Init()
 	if (zonemap == nullptr) {
 		zonemap = Map::LoadMapFile(zoneName, Grid);
 	}
+	if (regionmap == nullptr) {
+		regionmap = RegionMap::LoadRegionMapfile(zoneName);
+	}
+
 	pathing = IPathfinder::Load(zoneName);
 	movementMgr = new MobMovementManager();
 //	else

+ 2 - 0
EQ2/source/WorldServer/zoneserver.h

@@ -45,6 +45,7 @@
 #include "Zone/map.h"
 #include "Zone/pathfinder_interface.h"
 #include "Zone/mob_movement_manager.h"
+#include "Zone/region_map.h"
 
 extern NetConnection net;		// needs to be here or compile errors in commands.cpp
 class SpellProcess;
@@ -444,6 +445,7 @@ public:
 
 	SPGrid* Grid;
 	Map* zonemap;
+	RegionMap* regionmap;
 	IPathfinder* pathing;
 	MobMovementManager* movementMgr;
 

+ 13 - 0
EQ2/source/common/LogTypes.h

@@ -499,6 +499,19 @@ LOG_TYPE(MAP, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLE
 LOG_TYPE(MAP, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W")
 LOG_TYPE(MAP, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E")
 LOG_TYPE(MAP, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D")
+LOG_TYPE(MAP, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P")
+LOG_TYPE(MAP, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T")
+
+
+/*** Region Map Loggers ********************************************************************************/
+// RegionMap-related events, status, messaging, access
+LOG_CATEGORY(REGION)
+LOG_TYPE(REGION, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I")
+LOG_TYPE(REGION, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W")
+LOG_TYPE(REGION, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E")
+LOG_TYPE(REGION, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D")
+LOG_TYPE(REGION, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P")
+LOG_TYPE(REGION, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T")
 
 #undef LOG_TYPE
 #undef LOG_CATEGORY

+ 2 - 2
EQ2/source/common/version.h

@@ -38,9 +38,9 @@
 #endif
 
 #if defined(LOGIN)
-#define CURRENT_VERSION	"0.8.1-libra1"
+#define CURRENT_VERSION	"0.9.0-libra2"
 #elif defined(WORLD)
-#define CURRENT_VERSION	"0.8.1-libra1"
+#define CURRENT_VERSION	"0.9.0-libra2"
 #else
 #define CURRENT_VERSION	"0.7.3-dev"
 #endif

+ 4 - 2
EQ2/win/VC10Projects/EQ2World.vcxproj

@@ -355,8 +355,9 @@
     <ClCompile Include="..\..\source\WorldServer\Zone\pathfinder_waypoint.cpp" />
     <ClCompile Include="..\..\source\WorldServer\Zone\position.cpp" />
     <ClCompile Include="..\..\source\WorldServer\Zone\raycast_mesh.cpp" />
+    <ClCompile Include="..\..\source\WorldServer\Zone\region_map.cpp" />
+    <ClCompile Include="..\..\source\WorldServer\Zone\region_map_v1.cpp" />
     <ClCompile Include="..\..\source\WorldServer\Zone\SPGrid.cpp" />
-    <ClCompile Include="..\..\source\WorldServer\Zone\water_map.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\source\common\DatabaseNew.h" />
@@ -500,8 +501,9 @@
     <ClInclude Include="..\..\source\WorldServer\Zone\pathfinder_waypoint.h" />
     <ClInclude Include="..\..\source\WorldServer\Zone\position.h" />
     <ClInclude Include="..\..\source\WorldServer\Zone\raycast_mesh.h" />
+    <ClInclude Include="..\..\source\WorldServer\Zone\region_map.h" />
+    <ClInclude Include="..\..\source\WorldServer\Zone\region_map_v1.h" />
     <ClInclude Include="..\..\source\WorldServer\Zone\SPGrid.h" />
-    <ClInclude Include="..\..\source\WorldServer\Zone\water_map.h" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

+ 12 - 6
EQ2/win/VC10Projects/EQ2World.vcxproj.filters

@@ -559,9 +559,6 @@
     <ClCompile Include="..\..\source\WorldServer\Zone\mob_movement_manager.cpp">
       <Filter>Source Files\Zone</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\source\WorldServer\Zone\water_map.cpp">
-      <Filter>Source Files\Zone</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\source\WorldServer\Zone\pathfinder_interface.cpp">
       <Filter>Source Files\Zone</Filter>
     </ClCompile>
@@ -577,6 +574,12 @@
     <ClCompile Include="..\..\source\WorldServer\Zone\pathfinder_waypoint.cpp">
       <Filter>Source Files\Zone</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\source\WorldServer\Zone\region_map.cpp">
+      <Filter>Source Files\Zone</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\WorldServer\Zone\region_map_v1.cpp">
+      <Filter>Source Files\Zone</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\source\common\Common_Defines.h">
@@ -993,9 +996,6 @@
     <ClInclude Include="..\..\source\WorldServer\Zone\mob_movement_manager.h">
       <Filter>Header Files\Zone</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\source\WorldServer\Zone\water_map.h">
-      <Filter>Header Files\Zone</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\source\WorldServer\Zone\pathfinder_interface.h">
       <Filter>Header Files\Zone</Filter>
     </ClInclude>
@@ -1008,5 +1008,11 @@
     <ClInclude Include="..\..\source\WorldServer\Zone\pathfinder_waypoint.h">
       <Filter>Header Files\Zone</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\source\WorldServer\Zone\region_map.h">
+      <Filter>Header Files\Zone</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\WorldServer\Zone\region_map_v1.h">
+      <Filter>Header Files\Zone</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

+ 8 - 0
server/log_config.xml

@@ -352,4 +352,12 @@
 		<ConfigType Type="PACKET" Level="9" Color="Yellow" Enabled="False" Logs="3" />
 		<ConfigType Type="TRACE" Level="9" Color="Yellow" Enabled="False" Logs="1" />
 	</LogConfig>
+	<LogConfig Category="REGION">
+		<ConfigType Type="INFO" Level="9" Color="WhiteBold" Enabled="True" Logs="3" />
+		<ConfigType Type="WARNING" Level="9" Color="YellowBold" Enabled="True" Logs="3" />
+		<ConfigType Type="ERROR" Level="9" Color="RedBold" Enabled="True" Logs="3" />
+		<ConfigType Type="DEBUG" Level="9" Color="GreenBold" Enabled="False" Logs="3" />
+		<ConfigType Type="PACKET" Level="9" Color="Yellow" Enabled="False" Logs="3" />
+		<ConfigType Type="TRACE" Level="9" Color="Yellow" Enabled="False" Logs="1" />
+	</LogConfig>
 </EQ2EmuLogConfigs>