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)
This commit is contained in:
Image 2020-10-13 23:52:55 -04:00
parent cda936ad3f
commit 9290bceb60
27 changed files with 831 additions and 96 deletions

View file

@ -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})

View file

@ -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;
}

View file

@ -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();

View file

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

View file

@ -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;
}

View file

@ -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

View file

@ -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, ...) {

View file

@ -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;
}

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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; }

View file

@ -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"

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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">

View file

@ -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>

View file

@ -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>