eqemu-server/zone/map.cpp

269 lines
5.3 KiB
C++

#include "../common/debug.h"
#include "../common/MiscFunctions.h"
#include "map.h"
#include "RaycastMesh.h"
#include "zone.h"
#include <stdint.h>
#include <algorithm>
#include <locale>
#include <vector>
struct Map::impl
{
RaycastMesh *rm;
};
Map::Map() {
imp = nullptr;
}
Map::~Map() {
if(imp) {
imp->rm->release();
}
}
float Map::FindBestZ(Vertex &start, Vertex *result) const {
if (!imp)
return false;
Vertex tmp;
if(!result)
result = &tmp;
start.z += RuleI(Map, FindBestZHeightAdjust);
Vertex from(start.x, start.y, start.z);
Vertex to(start.x, start.y, BEST_Z_INVALID);
float hit_distance;
bool hit = false;
hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance);
if(hit) {
return result->z;
}
// Find nearest Z above us
to.z = -BEST_Z_INVALID;
hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance);
if (hit)
{
return result->z;
}
return BEST_Z_INVALID;
}
bool Map::LineIntersectsZone(Vertex start, Vertex end, float step, Vertex *result) const {
if(!imp)
return false;
return imp->rm->raycast((const RmReal*)&start, (const RmReal*)&end, (RmReal*)result, nullptr, nullptr);
}
bool Map::LineIntersectsZoneNoZLeaps(Vertex start, Vertex end, float step_mag, Vertex *result) const {
if (!imp)
return false;
float z = BEST_Z_INVALID;
Vertex step;
Vertex cur;
cur.x = start.x;
cur.y = start.y;
cur.z = start.z;
step.x = end.x - start.x;
step.y = end.y - start.y;
step.z = end.z - start.z;
float factor = step_mag / sqrt(step.x*step.x + step.y*step.y + step.z*step.z);
step.x *= factor;
step.y *= factor;
step.z *= factor;
int steps = 0;
if (step.x > 0 && step.x < 0.001f)
step.x = 0.001f;
if (step.y > 0 && step.y < 0.001f)
step.y = 0.001f;
if (step.z > 0 && step.z < 0.001f)
step.z = 0.001f;
if (step.x < 0 && step.x > -0.001f)
step.x = -0.001f;
if (step.y < 0 && step.y > -0.001f)
step.y = -0.001f;
if (step.z < 0 && step.z > -0.001f)
step.z = -0.001f;
//while we are not past end
//always do this once, even if start == end.
while(cur.x != end.x || cur.y != end.y || cur.z != end.z)
{
steps++;
Vertex me;
me.x = cur.x;
me.y = cur.y;
me.z = cur.z;
Vertex hit;
float best_z = FindBestZ(me, &hit);
float diff = best_z - z;
diff = diff < 0 ? -diff : diff;
if (z == -999999 || best_z == -999999 || diff < 12.0)
z = best_z;
else
return true;
//look at current location
if(LineIntersectsZone(start, end, step_mag, result))
{
return true;
}
//move 1 step
if (cur.x != end.x)
cur.x += step.x;
if (cur.y != end.y)
cur.y += step.y;
if (cur.z != end.z)
cur.z += step.z;
//watch for end conditions
if ( (cur.x > end.x && end.x >= start.x) || (cur.x < end.x && end.x <= start.x) || (step.x == 0) ) {
cur.x = end.x;
}
if ( (cur.y > end.y && end.y >= start.y) || (cur.y < end.y && end.y <= start.y) || (step.y == 0) ) {
cur.y = end.y;
}
if ( (cur.z > end.z && end.z >= start.z) || (cur.z < end.z && end.z < start.z) || (step.z == 0) ) {
cur.z = end.z;
}
}
//walked entire line and didnt run into anything...
return false;
}
bool Map::CheckLoS(Vertex myloc, Vertex oloc) const {
if(!imp)
return false;
return !imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, nullptr, nullptr);
}
Map *Map::LoadMapFile(std::string file) {
std::string filename = MAP_DIR;
filename += "/";
std::transform(file.begin(), file.end(), file.begin(), ::tolower);
filename += file;
filename += ".map";
Map *m = new Map();
if (m->Load(filename)) {
return m;
}
delete m;
return nullptr;
}
bool Map::Load(std::string filename) {
FILE *f = fopen(filename.c_str(), "rb");
if(f) {
uint32_t version;
if(fread(&version, sizeof(version), 1, f) != 1) {
fclose(f);
return false;
}
if(version == 0x01000000) {
bool v = LoadV1(f);
fclose(f);
return v;
} else if(version == 0x02000000) {
bool v = LoadV2(f);
fclose(f);
return v;
} else {
fclose(f);
return false;
}
}
return false;
}
bool Map::LoadV1(FILE *f) {
uint32_t face_count;
uint16_t node_count;
uint32_t facelist_count;
if(fread(&face_count, sizeof(face_count), 1, f) != 1) {
return false;
}
if(fread(&node_count, sizeof(node_count), 1, f) != 1) {
return false;
}
if(fread(&facelist_count, sizeof(facelist_count), 1, f) != 1) {
return false;
}
std::vector<Vertex> verts;
std::vector<uint32_t> indices;
for(uint32_t i = 0; i < face_count; ++i) {
Vertex a;
Vertex b;
Vertex c;
float normals[4];
if(fread(&a, sizeof(Vertex), 1, f) != 1) {
return false;
}
if(fread(&b, sizeof(Vertex), 1, f) != 1) {
return false;
}
if(fread(&c, sizeof(Vertex), 1, f) != 1) {
return false;
}
if(fread(normals, sizeof(normals), 1, f) != 1) {
return false;
}
size_t sz = verts.size();
verts.push_back(a);
indices.push_back((uint32_t)sz);
verts.push_back(b);
indices.push_back((uint32_t)sz + 1);
verts.push_back(c);
indices.push_back((uint32_t)sz + 2);
}
if(imp) {
imp->rm->release();
imp->rm = nullptr;
} else {
imp = new impl;
}
imp->rm = createRaycastMesh((RmUint32)verts.size(), (const RmReal*)&verts[0], face_count, &indices[0]);
if(!imp->rm) {
delete imp;
imp = nullptr;
return false;
}
return true;
}
bool Map::LoadV2(FILE *f) {
return false;
}