Public release of the DLU server code!

Have fun!
This commit is contained in:
Unknown
2021-12-05 18:54:36 +01:00
parent 5f7270e4ad
commit 0545adfac3
1146 changed files with 368646 additions and 1 deletions

353
dZoneManager/Level.cpp Normal file
View File

@@ -0,0 +1,353 @@
#include "Game.h"
#include "Level.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "BinaryIO.h"
#include "dLogger.h"
#include "Spawner.h"
#include "dZoneManager.h"
#include "GeneralUtils.h"
#include "Entity.h"
#include "EntityManager.h"
Level::Level(Zone* parentZone, const std::string& filepath) {
m_ParentZone = parentZone;
std::ifstream file(filepath, std::ios_base::in | std::ios_base::binary);
if (file) {
//printf("Opened %s\n", filepath.c_str());
ReadChunks(file);
}
else {
Game::logger->Log("Level", "Failed to load %s\n", filepath.c_str());
}
file.close();
}
Level::~Level() {
for (std::map<uint32_t, Header>::iterator it = m_ChunkHeaders.begin(); it != m_ChunkHeaders.end(); ++it) {
if (it->second.id == Level::ChunkTypeID::FileInfo) delete it->second.fileInfo;
if (it->second.id == Level::ChunkTypeID::SceneObjectData) delete it->second.sceneObjects;
}
}
const void Level::PrintAllObjects() {
for (std::map<uint32_t, Header>::iterator it = m_ChunkHeaders.begin(); it != m_ChunkHeaders.end(); ++it) {
if (it->second.id == Level::ChunkTypeID::SceneObjectData) {
it->second.sceneObjects->PrintAllObjects();
}
}
}
void Level::ReadChunks(std::ifstream & file) {
const uint32_t CHNK_HEADER = ('C' + ('H' << 8) + ('N' << 16) + ('K' << 24));
while (!file.eof()) {
uint32_t initPos = uint32_t(file.tellg());
uint32_t header = 0;
BinaryIO::BinaryRead(file, header);
if (header == CHNK_HEADER) { //Make sure we're reading a valid CHNK
Header header;
BinaryIO::BinaryRead(file, header.id);
BinaryIO::BinaryRead(file, header.chunkVersion);
BinaryIO::BinaryRead(file, header.chunkType);
BinaryIO::BinaryRead(file, header.size);
BinaryIO::BinaryRead(file, header.startPosition);
uint32_t target = initPos + header.size;
file.seekg(header.startPosition);
//We're currently not loading env or particle data
if (header.id == ChunkTypeID::FileInfo) {
ReadFileInfoChunk(file, header);
}
else if (header.id == ChunkTypeID::SceneObjectData) {
ReadSceneObjectDataChunk(file, header);
}
m_ChunkHeaders.insert(std::make_pair(header.id, header));
file.seekg(target);
}
else {
if (initPos == std::streamoff(0)) { //Really old chunk version
file.seekg(0);
Header header;
header.id = ChunkTypeID::FileInfo; //I guess?
FileInfoChunk* fileInfo = new FileInfoChunk();
BinaryIO::BinaryRead(file, header.chunkVersion);
BinaryIO::BinaryRead(file, header.chunkType);
file.ignore(1);
BinaryIO::BinaryRead(file, fileInfo->revision);
if (header.chunkVersion >= 45) file.ignore(4);
file.ignore(4 * (4 * 3));
if (header.chunkVersion >= 31) {
if (header.chunkVersion >= 39) {
file.ignore(12 * 4);
if (header.chunkVersion >= 40) {
uint32_t s = 0;
BinaryIO::BinaryRead(file, s);
for (uint32_t i = 0; i < s; ++i) {
file.ignore(4); //a uint
file.ignore(4); //two floats
file.ignore(4);
}
}
}
else {
file.ignore(8);
}
file.ignore(3 * 4);
}
if (header.chunkVersion >= 36) {
file.ignore(3 * 4);
}
if (header.chunkVersion < 42) {
file.ignore(3 * 4);
if (header.chunkVersion >= 33) {
file.ignore(4 * 4);
}
}
for (uint32_t i = 0; i < 6; ++i) {
uint32_t count = 0;
BinaryIO::BinaryRead(file, count);
file.ignore(count);
}
file.ignore(4);
uint32_t count = 0;
BinaryIO::BinaryRead(file, count);
file.ignore(count * 12);
header.fileInfo = fileInfo;
m_ChunkHeaders.insert(std::make_pair(header.id, header));
//Now pretend to be a normal file and read Objects chunk:
Header hdr;
hdr.id = ChunkTypeID::SceneObjectData;
ReadSceneObjectDataChunk(file, hdr);
m_ChunkHeaders.insert(std::make_pair(hdr.id, hdr));
} break;
}
}
}
void Level::ReadFileInfoChunk(std::ifstream & file, Header & header) {
FileInfoChunk* fi = new FileInfoChunk;
BinaryIO::BinaryRead(file, fi->version);
BinaryIO::BinaryRead(file, fi->revision);
BinaryIO::BinaryRead(file, fi->enviromentChunkStart);
BinaryIO::BinaryRead(file, fi->objectChunkStart);
BinaryIO::BinaryRead(file, fi->particleChunkStart);
header.fileInfo = fi;
//PATCH FOR AG: (messed up file?)
if (header.fileInfo->revision == 3452816845 && m_ParentZone->GetZoneID().GetMapID() == 1100) header.fileInfo->revision = 26;
}
void Level::ReadSceneObjectDataChunk(std::ifstream & file, Header & header) {
SceneObjectDataChunk* chunk = new SceneObjectDataChunk;
uint32_t objectsCount = 0;
BinaryIO::BinaryRead(file, objectsCount);
CDFeatureGatingTable* featureGatingTable = CDClientManager::Instance()->GetTable<CDFeatureGatingTable>("FeatureGating");
for (uint32_t i = 0; i < objectsCount; ++i) {
SceneObject obj;
BinaryIO::BinaryRead(file, obj.id);
BinaryIO::BinaryRead(file, obj.lot);
/*if (header.fileInfo->version >= 0x26)*/ BinaryIO::BinaryRead(file, obj.value1);
/*if (header.fileInfo->version >= 0x20)*/ BinaryIO::BinaryRead(file, obj.value2);
BinaryIO::BinaryRead(file, obj.position);
BinaryIO::BinaryRead(file, obj.rotation);
BinaryIO::BinaryRead(file, obj.scale);
//This is a little bit of a bodge, but because the alpha client (HF) doesn't store the
//spawn position / rotation like the later versions do, we need to check the LOT for the spawn pos & set it.
if (obj.lot == LOT_MARKER_PLAYER_START) {
dZoneManager::Instance()->GetZone()->SetSpawnPos(obj.position);
dZoneManager::Instance()->GetZone()->SetSpawnRot(obj.rotation);
}
std::u16string ldfString = u"";
uint32_t length = 0;
BinaryIO::BinaryRead(file, length);
for (uint32_t i = 0; i < length; ++i) {
uint16_t data;
BinaryIO::BinaryRead(file, data);
ldfString.push_back(data);
}
std::string sData = GeneralUtils::UTF16ToWTF8(ldfString);
std::stringstream ssData(sData);
std::string token;
char deliminator = '\n';
while (std::getline(ssData, token, deliminator)) {
LDFBaseData * ldfData = LDFBaseData::DataFromString(token);
obj.settings.push_back(ldfData);
}
BinaryIO::BinaryRead(file, obj.value3);
// Feature gating
bool gated = false;
for (LDFBaseData* data : obj.settings) {
if (data->GetKey() == u"gatingOnFeature") {
std::string featureGate = data->GetValueAsString();
if (!featureGatingTable->FeatureUnlocked(featureGate)) {
gated = true;
break;
}
}
}
if (gated) {
for (auto* setting : obj.settings) {
delete setting;
}
obj.settings.clear();
continue;
}
if (obj.lot == 176) { //Spawner
SpawnerInfo spawnInfo = SpawnerInfo();
SpawnerNode* node = new SpawnerNode();
spawnInfo.templateID = obj.lot;
spawnInfo.spawnerID = obj.id;
spawnInfo.templateScale = obj.scale;
node->position = obj.position;
node->rotation = obj.rotation;
node->config = obj.settings;
spawnInfo.nodes.push_back(node);
for (LDFBaseData* data : obj.settings) {
if (data) {
if (data->GetKey() == u"spawntemplate") {
spawnInfo.templateID = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"spawner_node_id") {
node->nodeID = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"spawner_name") {
spawnInfo.name = data->GetValueAsString();
}
if (data->GetKey() == u"max_to_spawn") {
spawnInfo.maxToSpawn = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"spawner_active_on_load") {
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"active_on_load") {
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"respawn") {
if (data->GetValueType() == eLDFType::LDF_TYPE_FLOAT) // Floats are in seconds
{
spawnInfo.respawnTime = std::stof(data->GetValueAsString());
}
else if (data->GetValueType() == eLDFType::LDF_TYPE_U32) // Ints are in ms?
{
spawnInfo.respawnTime = std::stoi(data->GetValueAsString()) / 1000;
}
}
if (data->GetKey() == u"spawnsGroupOnSmash") {
spawnInfo.spawnsOnSmash = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"spawnNetNameForSpawnGroupOnSmash") {
spawnInfo.spawnOnSmashGroupName = data->GetValueAsString();
}
if (data->GetKey() == u"groupID") { // Load object groups
std::string groupStr = data->GetValueAsString();
spawnInfo.groups = GeneralUtils::SplitString(groupStr, ';');
spawnInfo.groups.erase(spawnInfo.groups.end() - 1);
}
if (data->GetKey() == u"no_auto_spawn") {
spawnInfo.noAutoSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
}
if (data->GetKey() == u"no_timed_spawn") {
spawnInfo.noTimedSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
}
if (data->GetKey() == u"spawnActivator") {
spawnInfo.spawnActivator = static_cast<LDFData<bool>*>(data)->GetValue();
}
}
}
Spawner* spawner = new Spawner(spawnInfo);
dZoneManager::Instance()->AddSpawner(obj.id, spawner);
} else { //Regular object
EntityInfo info;
info.spawnerID = 0;
info.id = obj.id;
info.lot = obj.lot;
info.pos = obj.position;
info.rot = obj.rotation;
info.settings = obj.settings;
info.scale = obj.scale;
//Check to see if we shouldn't be loading this:
bool clientOnly = false;
bool serverOnly = false;
std::string featureGate = "";
for (LDFBaseData* data : obj.settings) {
if (data) {
if (data->GetKey() == u"loadOnClientOnly") {
clientOnly = (bool)std::stoi(data->GetValueAsString());
break;
}
if (data->GetKey() == u"loadSrvrOnly") {
serverOnly = (bool)std::stoi(data->GetValueAsString());
break;
}
}
}
if (!clientOnly) {
// We should never have more than 1 zone control object
const auto zoneControlObject = dZoneManager::Instance()->GetZoneControlObject();
if (zoneControlObject != nullptr && info.lot == zoneControlObject->GetLOT())
goto deleteSettings;
EntityManager::Instance()->CreateEntity(info, nullptr);
}
else
{
deleteSettings:
for (auto* setting : info.settings) {
delete setting;
setting = nullptr;
}
info.settings.clear();
obj.settings.clear();
}
}
}
//printf("Loaded %u objects!\n", objectsCount);
header.sceneObjects = chunk;
}

73
dZoneManager/Level.h Normal file
View File

@@ -0,0 +1,73 @@
#pragma once
#include "dZMCommon.h"
#include <map>
#include <iostream>
#include "Zone.h"
class Level {
public:
enum ChunkTypeID : uint16_t {
FileInfo = 1000,
SceneEnviroment = 2000,
SceneObjectData,
SceneParticleData
};
enum ChunkTypes {
Enviroment,
Objects,
Particles
};
struct FileInfoChunk {
uint32_t version;
uint32_t revision;
uint32_t enviromentChunkStart;
uint32_t objectChunkStart;
uint32_t particleChunkStart;
};
struct SceneObjectDataChunk {
std::map<LWOOBJID, SceneObject> objects;
SceneObject& GetObject(LWOOBJID id) {
for (std::map<LWOOBJID, SceneObject>::iterator it = objects.begin(); it != objects.end(); ++it) {
if (it->first == id) return it->second;
}
}
const void PrintAllObjects() {
for (std::map<LWOOBJID, SceneObject>::iterator it = objects.begin(); it != objects.end(); ++it) {
std::cout << "\t ID: " << it->first << " LOT: " << it->second.lot << std::endl;
}
}
uint32_t GetObjectCount() { return objects.size(); }
};
struct Header {
uint32_t id;
uint16_t chunkVersion;
ChunkTypeID chunkType;
uint32_t size;
uint32_t startPosition;
FileInfoChunk* fileInfo;
SceneObjectDataChunk* sceneObjects;
LWOSCENEID lwoSceneID;
};
public:
Level(Zone* parentZone, const std::string& filepath);
~Level();
const void PrintAllObjects();
std::map<uint32_t, Header> m_ChunkHeaders;
private:
Zone* m_ParentZone;
//private functions:
void ReadChunks(std::ifstream& file);
void ReadFileInfoChunk(std::ifstream& file, Header& header);
void ReadSceneObjectDataChunk(std::ifstream& file, Header& header);
};

261
dZoneManager/Spawner.cpp Normal file
View File

@@ -0,0 +1,261 @@
#include "Spawner.h"
#include "EntityManager.h"
#include "dLogger.h"
#include "Game.h"
#include <sstream>
#include <functional>
#include "GeneralUtils.h"
#include "dZoneManager.h"
Spawner::Spawner(const SpawnerInfo info) {
m_Info = info;
m_Active = m_Info.activeOnLoad && info.spawnActivator;
m_EntityInfo = EntityInfo();
m_EntityInfo.spawner = this;
if (!m_Info.emulated) {
m_EntityInfo.spawnerID = m_Info.spawnerID;
}
else {
m_EntityInfo.spawnerID = m_Info.emulator;
m_Info.isNetwork = false;
}
m_EntityInfo.lot = m_Info.templateID;
m_EntityInfo.scale = m_Info.templateScale;
m_Start = m_Info.noTimedSpawn;
//ssssh...
if (m_EntityInfo.lot == 14718) { //AG - MAELSTROM SAMPLE
m_Info.groups.emplace_back("MaelstromSamples");
}
if (m_EntityInfo.lot == 14375) //AG - SPIDER BOSS EGG
{
m_Info.groups.emplace_back("EGG");
}
int timerCount = m_Info.amountMaintained;
if (m_Info.amountMaintained > m_Info.nodes.size()) {
timerCount = m_Info.nodes.size();
}
for (int i = 0; i < timerCount; ++i) {
m_WaitTimes.push_back(m_Info.respawnTime);
}
if (m_Info.spawnOnSmashGroupName != "")
{
std::vector<Entity*> spawnSmashEntities = EntityManager::Instance()->GetEntitiesInGroup(m_Info.spawnOnSmashGroupName);
std::vector<Spawner*> spawnSmashSpawners = dZoneManager::Instance()->GetSpawnersInGroup(m_Info.spawnOnSmashGroupName);
std::vector<Spawner*> spawnSmashSpawnersN = dZoneManager::Instance()->GetSpawnersByName(m_Info.spawnOnSmashGroupName);
for (Entity* ssEntity : spawnSmashEntities) {
m_SpawnSmashFoundGroup = true;
ssEntity->AddDieCallback([=]() {
Spawn();
});
}
for (Spawner* ssSpawner : spawnSmashSpawners) {
m_SpawnSmashFoundGroup = true;
ssSpawner->AddSpawnedEntityDieCallback([=]() {
Spawn();
});
}
for (Spawner* ssSpawner : spawnSmashSpawnersN) {
m_SpawnSmashFoundGroup = true;
m_SpawnOnSmash = ssSpawner;
ssSpawner->AddSpawnedEntityDieCallback([=]() {
Spawn();
});
}
}
}
Spawner::~Spawner() {
}
Entity* Spawner::Spawn()
{
std::vector<SpawnerNode*> freeNodes;
for (SpawnerNode* node : m_Info.nodes) {
if (node->entities.size() < node->nodeMax) {
freeNodes.push_back(node);
}
}
return Spawn(freeNodes);
}
Entity* Spawner::Spawn(std::vector<SpawnerNode*> freeNodes, const bool force) {
if (force || ((m_Entities.size() < m_Info.amountMaintained) && (freeNodes.size() > 0) && (m_AmountSpawned < m_Info.maxToSpawn || m_Info.maxToSpawn == -1))) {
SpawnerNode* spawnNode = freeNodes[GeneralUtils::GenerateRandomNumber<int>(0, freeNodes.size() - 1)];
++m_AmountSpawned;
m_EntityInfo.pos = spawnNode->position;
m_EntityInfo.rot = spawnNode->rotation;
m_EntityInfo.settings = spawnNode->config;
m_EntityInfo.id = 0;
m_EntityInfo.spawner = this;
if (!m_Info.emulated) {
m_EntityInfo.spawnerNodeID = spawnNode->nodeID;
m_EntityInfo.hasSpawnerNodeID = true;
m_EntityInfo.spawnerID = m_Info.spawnerID;
}
Entity* rezdE = EntityManager::Instance()->CreateEntity(m_EntityInfo, nullptr);
rezdE->GetGroups() = m_Info.groups;
EntityManager::Instance()->ConstructEntity(rezdE);
m_Entities.insert({ rezdE->GetObjectID(), spawnNode });
spawnNode->entities.push_back(rezdE->GetObjectID());
if (m_Entities.size() == m_Info.amountMaintained) {
m_NeedsUpdate = false;
}
for (const auto& cb : m_EntitySpawnedCallbacks) {
cb(rezdE);
}
return rezdE;
}
return nullptr;
}
void Spawner::AddSpawnedEntityDieCallback(std::function<void()> callback) {
m_SpawnedEntityDieCallbacks.push_back(callback);
}
void Spawner::AddEntitySpawnedCallback(std::function<void(Entity *)> callback) {
m_EntitySpawnedCallbacks.push_back(callback);
}
void Spawner::Reset()
{
m_Start = true;
for (auto* node : m_Info.nodes)
{
for (const auto& spawned : node->entities)
{
auto* entity = EntityManager::Instance()->GetEntity(spawned);
if (entity == nullptr) continue;
entity->Kill();
}
node->entities.clear();
}
m_Entities.clear();
m_AmountSpawned = 0;
m_NeedsUpdate = true;
}
void Spawner::SoftReset() {
m_Start = true;
m_AmountSpawned = 0;
m_NeedsUpdate = true;
}
void Spawner::SetRespawnTime(float time) {
m_Info.respawnTime = time;
for (size_t i = 0; i < m_WaitTimes.size(); ++i) {
m_WaitTimes[i] = 0;
};
m_Start = true;
m_NeedsUpdate = true;
}
void Spawner::SetNumToMaintain(int32_t value) {
m_Info.amountMaintained = value;
}
void Spawner::Update(const float deltaTime) {
if (m_Start && m_Active)
{
m_Start = false;
const auto toSpawn = m_Info.amountMaintained - m_AmountSpawned;
for (auto i = 0; i < toSpawn; ++i)
{
Spawn();
}
m_WaitTimes.clear();
return;
}
if (!m_NeedsUpdate) return;
if (!m_Active) return;
//if (m_Info.noTimedSpawn) return;
if (m_Info.spawnsOnSmash) {
if (!m_SpawnSmashFoundGroup) {
}
return;
}
for (size_t i = 0; i < m_WaitTimes.size(); ++i) {
m_WaitTimes[i] += deltaTime;
if (m_WaitTimes[i] >= m_Info.respawnTime) {
m_WaitTimes.erase(m_WaitTimes.begin() + i);
Spawn();
}
}
}
void Spawner::NotifyOfEntityDeath(const LWOOBJID& objectID) {
for (std::function<void()> cb : m_SpawnedEntityDieCallbacks) {
cb();
}
m_NeedsUpdate = true;
//m_RespawnTime = 10.0f;
m_WaitTimes.push_back(0.0f);
SpawnerNode* node;
auto it = m_Entities.find(objectID);
if (it != m_Entities.end()) node = it->second;
else return;
if (!node) {
return;
}
for (size_t i = 0; i < node->entities.size(); ++i) {
if (node->entities[i] && node->entities[i] == objectID)
node->entities.erase(node->entities.begin() + i);
}
m_Entities.erase(objectID);
if (m_SpawnOnSmash != nullptr)
{
m_SpawnOnSmash->Reset();
}
}
void Spawner::Activate()
{
m_Active = true;
m_NeedsUpdate = true;
for (auto& time : m_WaitTimes)
{
time = 0;
}
}
void Spawner::SetSpawnLot(LOT lot) {
m_EntityInfo.lot = lot;
}

85
dZoneManager/Spawner.h Normal file
View File

@@ -0,0 +1,85 @@
#ifndef SPAWNER_H
#define SPAWNER_H
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "Entity.h"
#include "dZMCommon.h"
#include <vector>
#include <string>
#include <functional>
#include "LDFFormat.h"
struct SpawnerNode {
NiPoint3 position = NiPoint3::ZERO;
NiQuaternion rotation = NiQuaternion::IDENTITY;
uint32_t nodeID = 0;
uint32_t nodeMax = 1;
std::vector<LWOOBJID> entities;
std::vector<LDFBaseData*> config;
};
struct SpawnerInfo {
LWOOBJID spawnerID = LWOOBJID_EMPTY;
LOT templateID = -1;
std::string name = "";
float templateScale = 1;
float respawnTime = 0.0f;
int32_t maxToSpawn = -1;
uint32_t amountMaintained = 1;
bool activeOnLoad = true;
std::vector<SpawnerNode*> nodes = {};
bool isNetwork = false;
bool spawnsOnSmash = false;
std::string spawnOnSmashGroupName = "";
std::vector<std::string> groups = {};
bool noAutoSpawn = false;
bool noTimedSpawn = false;
std::string grpNameQBShowBricks = "";
bool spawnActivator = true;
bool emulated = false;
LWOOBJID emulator = LWOOBJID_EMPTY;
};
class Spawner {
public:
Spawner(SpawnerInfo info);
~Spawner();
Entity* Spawn();
Entity* Spawn(std::vector<SpawnerNode*> freeNodes, bool force = false);
void Update(float deltaTime);
void NotifyOfEntityDeath(const LWOOBJID& objectID);
void Activate();
void Deactivate() { m_Active = false; };
int32_t GetAmountSpawned() { return m_AmountSpawned; };
std::string GetName() { return m_Info.name; };
std::vector<std::string> GetGroups() { return m_Info.groups; };
void AddSpawnedEntityDieCallback(std::function<void()> callback);
void AddEntitySpawnedCallback(std::function<void(Entity*)> callback);
void SetSpawnLot(LOT lot);
void Reset();
void SoftReset();
void SetRespawnTime(float time);
void SetNumToMaintain(int32_t value);
bool GetIsSpawnSmashGroup() const { return m_SpawnSmashFoundGroup; };
SpawnerInfo m_Info;
bool m_Active = true;
private:
std::vector<std::function<void()>> m_SpawnedEntityDieCallbacks = {};
std::vector<std::function<void(Entity*)>> m_EntitySpawnedCallbacks = {};
bool m_SpawnSmashFoundGroup = false;
std::vector<float> m_WaitTimes = {};
bool m_NeedsUpdate = true;
std::map<LWOOBJID, SpawnerNode*> m_Entities = {};
EntityInfo m_EntityInfo;
int32_t m_AmountSpawned = 0;
bool m_Start = false;
Spawner* m_SpawnOnSmash = nullptr;
};
#endif // SPAWNER_H

571
dZoneManager/Zone.cpp Normal file
View File

@@ -0,0 +1,571 @@
#include "Zone.h"
#include "Level.h"
#include <fstream>
#include <sstream>
#include "Game.h"
#include "dLogger.h"
#include "GeneralUtils.h"
#include "BinaryIO.h"
#include "CDClientManager.h"
#include "CDZoneTableTable.h"
#include "Spawner.h"
#include "dZoneManager.h"
Zone::Zone(const LWOMAPID & mapID, const LWOINSTANCEID & instanceID, const LWOCLONEID & cloneID) :
m_ZoneID(mapID, instanceID, cloneID)
{
m_NumberOfScenesLoaded = 0;
m_NumberOfObjectsLoaded = 0;
m_NumberOfSceneTransitionsLoaded = 0;
m_CheckSum = 0;
m_WorldID = 0;
m_SceneCount = 0;
}
Zone::~Zone() {
Game::logger->Log("Zone", "Destroying zone %i\n", m_ZoneID.GetMapID());
for (std::map<LWOSCENEID, SceneRef>::iterator it = m_Scenes.begin(); it != m_Scenes.end(); ++it) {
if (it->second.level != nullptr) delete it->second.level;
}
}
void Zone::Initalize()
{
LoadZoneIntoMemory();
LoadLevelsIntoMemory();
m_CheckSum = CalculateChecksum();
}
void Zone::LoadZoneIntoMemory() {
m_ZoneFilePath = GetFilePathForZoneID();
m_ZonePath = m_ZoneFilePath.substr(0, m_ZoneFilePath.rfind('/') + 1);
if (m_ZoneFilePath == "ERR") return;
std::ifstream file(m_ZoneFilePath, std::ios::binary);
if (file) {
BinaryIO::BinaryRead(file, m_ZoneFileFormatVersion);
uint32_t mapRevision = 0;
if (m_ZoneFileFormatVersion >= Zone::ZoneFileFormatVersion::Alpha) BinaryIO::BinaryRead(file, mapRevision);
BinaryIO::BinaryRead(file, m_WorldID);
if ((uint16_t)m_WorldID != m_ZoneID.GetMapID()) Game::logger->Log("Zone", "WorldID: %i doesn't match MapID %i! Is this intended?\n", m_WorldID, m_ZoneID.GetMapID());
AddRevision(LWOSCENEID_INVALID, mapRevision);
if (m_ZoneFileFormatVersion >= Zone::ZoneFileFormatVersion::Beta) {
BinaryIO::BinaryRead(file, m_Spawnpoint);
BinaryIO::BinaryRead(file, m_SpawnpointRotation);
}
if (m_ZoneFileFormatVersion <= Zone::ZoneFileFormatVersion::LateAlpha) {
uint8_t sceneCount;
BinaryIO::BinaryRead(file, sceneCount);
m_SceneCount = sceneCount;
}
else BinaryIO::BinaryRead(file, m_SceneCount);
for (uint32_t i = 0; i < m_SceneCount; ++i) {
LoadScene(file);
}
//Read generic zone info:
uint8_t stringLength;
BinaryIO::BinaryRead(file, stringLength);
m_ZonePath = BinaryIO::ReadString(file, stringLength);
BinaryIO::BinaryRead(file, stringLength);
m_ZoneRawPath = BinaryIO::ReadString(file, stringLength);
BinaryIO::BinaryRead(file, stringLength);
m_ZoneName = BinaryIO::ReadString(file, stringLength);
BinaryIO::BinaryRead(file, stringLength);
m_ZoneDesc = BinaryIO::ReadString(file, stringLength);
if (m_ZoneFileFormatVersion >= Zone::ZoneFileFormatVersion::PreAlpha) {
BinaryIO::BinaryRead(file, m_NumberOfSceneTransitionsLoaded);
for (uint32_t i = 0; i < m_NumberOfSceneTransitionsLoaded; ++i) {
LoadSceneTransition(file);
}
}
if (m_ZoneFileFormatVersion >= Zone::ZoneFileFormatVersion::EarlyAlpha) {
BinaryIO::BinaryRead(file, m_PathDataLength);
uint32_t unknown;
uint32_t pathCount;
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, pathCount);
for (uint32_t i = 0; i < pathCount; ++i) {
LoadPath(file);
}
for (Path path : m_Paths) {
if (path.pathType == PathType::Spawner) {
SpawnerInfo info = SpawnerInfo();
for (PathWaypoint waypoint : path.pathWaypoints) {
SpawnerNode* node = new SpawnerNode();
node->position = waypoint.position;
node->rotation = waypoint.rotation;
node->nodeID = 0;
node->config = waypoint.config;
for (LDFBaseData* data : waypoint.config) {
if (data) {
if (data->GetKey() == u"spawner_node_id") {
node->nodeID = std::stoi(data->GetValueAsString());
}
else if (data->GetKey() == u"spawner_max_per_node") {
node->nodeMax = std::stoi(data->GetValueAsString());
}
else if (data->GetKey() == u"groupID") { // Load object group
std::string groupStr = data->GetValueAsString();
info.groups = GeneralUtils::SplitString(groupStr, ';');
info.groups.erase(info.groups.end() - 1);
}
else if (data->GetKey() == u"grpNameQBShowBricks") {
if (data->GetValueAsString() == "") continue;
/*std::string groupStr = data->GetValueAsString();
info.groups.push_back(groupStr);*/
info.grpNameQBShowBricks = data->GetValueAsString();
}
else if (data->GetKey() == u"spawner_name")
{
info.name = data->GetValueAsString();
}
}
}
info.nodes.push_back(node);
}
info.templateID = path.spawner.spawnedLOT;
info.spawnerID = path.spawner.spawnerObjID;
info.respawnTime = path.spawner.respawnTime;
info.amountMaintained = path.spawner.amountMaintained;
info.maxToSpawn = path.spawner.maxToSpawn;
info.activeOnLoad = path.spawner.spawnerNetActive;
info.isNetwork = true;
Spawner* spawner = new Spawner(info);
dZoneManager::Instance()->AddSpawner(info.spawnerID, spawner);
}
}
//m_PathData.resize(m_PathDataLength);
//file.read((char*)&m_PathData[0], m_PathDataLength);
}
}
else {
Game::logger->Log("Zone", "Failed to open: %s\n", m_ZoneFilePath.c_str());
}
m_ZonePath = m_ZoneFilePath.substr(0, m_ZoneFilePath.rfind('/') + 1);
file.close();
}
std::string Zone::GetFilePathForZoneID() {
//We're gonna go ahead and presume we've got the db loaded already:
CDZoneTableTable * zoneTable = CDClientManager::Instance()->GetTable<CDZoneTableTable>("ZoneTable");
const CDZoneTable* zone = zoneTable->Query(this->GetZoneID().GetMapID());
if (zone != nullptr) {
std::string toReturn = "./res/maps/" + zone->zoneName;
std::transform(toReturn.begin(), toReturn.end(), toReturn.begin(), ::tolower);
return toReturn;
}
return std::string("ERR");
}
//Based off code from: https://www.liquisearch.com/fletchers_checksum/implementation/optimizations
uint32_t Zone::CalculateChecksum() {
uint32_t sum1 = 0xffff, sum2 = 0xffff;
for (std::map<LWOSCENEID, uint32_t>::const_iterator it = m_MapRevisions.cbegin(); it != m_MapRevisions.cend(); ++it) {
uint32_t sceneID = it->first.GetSceneID();
sum2 += sum1 += (sceneID >> 16);
sum2 += sum1 += (sceneID & 0xffff);
uint32_t layerID = it->first.GetLayerID();
sum2 += sum1 += (layerID >> 16);
sum2 += sum1 += (layerID & 0xffff);
uint32_t revision = it->second;
sum2 += sum1 += (revision >> 16);
sum2 += sum1 += (revision & 0xffff);
}
sum1 = (sum1 & 0xffff) + (sum1 >> 16);
sum2 = (sum2 & 0xffff) + (sum2 >> 16);
return sum2 << 16 | sum1;
}
void Zone::LoadLevelsIntoMemory() {
for (std::map<LWOSCENEID, SceneRef>::iterator it = m_Scenes.begin(); it != m_Scenes.end(); ++it) {
if (it->second.level == nullptr) {
it->second.level = new Level(this, m_ZonePath + it->second.filename);
if (it->second.level->m_ChunkHeaders.size() > 0) {
it->second.level->m_ChunkHeaders.begin()->second.lwoSceneID = it->first;
AddRevision(it->second.level->m_ChunkHeaders.begin()->second.lwoSceneID, it->second.level->m_ChunkHeaders.begin()->second.fileInfo->revision);
}
}
}
}
void Zone::AddRevision(LWOSCENEID sceneID, uint32_t revision) {
for (std::pair<LWOSCENEID, uint32_t> item : m_MapRevisions) {
if (item.first == sceneID) return;
}
m_MapRevisions[LWOSCENEID(sceneID)] = revision;
}
const void Zone::PrintAllGameObjects() {
for (std::pair<LWOSCENEID, SceneRef> scene : m_Scenes) {
Game::logger->Log("Zone", "\nIn sceneID: %i\n\n", scene.first.GetSceneID());
scene.second.level->PrintAllObjects();
}
}
void Zone::LoadScene(std::ifstream & file) {
SceneRef scene;
scene.level = nullptr;
LWOSCENEID lwoSceneID(LWOZONEID_INVALID, 0);
uint8_t sceneFilenameLength;
BinaryIO::BinaryRead(file, sceneFilenameLength);
scene.filename = BinaryIO::ReadString(file, sceneFilenameLength);
std::string luTriggersPath = scene.filename.substr(0, scene.filename.size() - 4) + ".lutriggers";
std::vector<LUTriggers::Trigger*> triggers = LoadLUTriggers(luTriggersPath, scene.id);
for (LUTriggers::Trigger* trigger : triggers) {
scene.triggers.insert({ trigger->id, trigger });
}
BinaryIO::BinaryRead(file, scene.id);
BinaryIO::BinaryRead(file, scene.sceneType);
lwoSceneID.SetSceneID(scene.id);
uint8_t sceneNameLength;
BinaryIO::BinaryRead(file, sceneNameLength);
scene.name = BinaryIO::ReadString(file, sceneNameLength);
file.ignore(3);
/*
if (m_Scenes.find(scene.id) != m_Scenes.end()) {
//Extract the layer id from the filename (bad I know, but it's reliable at least):
std::string layer = scene.filename.substr(scene.filename.rfind('x') + 1);
layer = layer.substr(0, layer.find('_'));
lwoSceneID.SetLayerID(std::atoi(layer.c_str()));
}
*/
lwoSceneID.SetLayerID(scene.sceneType);
m_Scenes.insert(std::make_pair(lwoSceneID, scene));
m_NumberOfScenesLoaded++;
}
std::vector<LUTriggers::Trigger*> Zone::LoadLUTriggers(std::string triggerFile, LWOSCENEID sceneID) {
std::vector<LUTriggers::Trigger*> lvlTriggers;
std::ifstream file(m_ZonePath + triggerFile);
std::stringstream data;
data << file.rdbuf();
if (data.str().size() == 0) return lvlTriggers;
tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument();
if (!doc) return lvlTriggers;
if (doc->Parse(data.str().c_str(), data.str().size()) == 0) {
//Game::logger->Log("Zone", "Loaded LUTriggers from file %s!\n", triggerFile.c_str());
}
else {
Game::logger->Log("Zone", "Failed to load LUTriggers from file %s\n", triggerFile.c_str());
return lvlTriggers;
}
tinyxml2::XMLElement* triggers = doc->FirstChildElement("triggers");
if (!triggers) return lvlTriggers;
auto currentTrigger = triggers->FirstChildElement("trigger");
while (currentTrigger) {
LUTriggers::Trigger *newTrigger = new LUTriggers::Trigger();
currentTrigger->QueryAttribute("enabled", &newTrigger->enabled);
currentTrigger->QueryAttribute("id", &newTrigger->id);
auto currentEvent = currentTrigger->FirstChildElement("event");
while (currentEvent) {
LUTriggers::Event* newEvent = new LUTriggers::Event();
newEvent->eventID = currentEvent->Attribute("id");
auto currentCommand = currentEvent->FirstChildElement("command");
while (currentCommand) {
LUTriggers::Command* newCommand = new LUTriggers::Command();
newCommand->id = currentCommand->Attribute("id");
newCommand->target = currentCommand->Attribute("target");
if (currentCommand->Attribute("targetName") != NULL) {
newCommand->targetName = currentCommand->Attribute("targetName");
}
if (currentCommand->Attribute("args") != NULL) {
newCommand->args = currentCommand->Attribute("args");
}
newEvent->commands.push_back(newCommand);
currentCommand = currentCommand->NextSiblingElement("command");
}
newTrigger->events.push_back(newEvent);
currentEvent = currentEvent->NextSiblingElement("event");
}
currentTrigger = currentTrigger->NextSiblingElement("trigger");
lvlTriggers.push_back(newTrigger);
}
delete doc;
return lvlTriggers;
}
LUTriggers::Trigger* Zone::GetTrigger(uint32_t sceneID, uint32_t triggerID) {
if (m_Scenes.find(sceneID) == m_Scenes.end()) return nullptr;
if (m_Scenes[sceneID].triggers.find(triggerID) == m_Scenes[sceneID].triggers.end()) return nullptr;
return m_Scenes[sceneID].triggers[triggerID];
}
const Path* Zone::GetPath(std::string name) const
{
for (const auto& path : m_Paths)
{
if (name == path.pathName)
{
return &path;
}
}
return nullptr;
}
void Zone::LoadSceneTransition(std::ifstream & file) {
SceneTransition sceneTrans;
if (m_ZoneFileFormatVersion < Zone::ZoneFileFormatVersion::LateAlpha) {
uint8_t length;
BinaryIO::BinaryRead(file, length);
sceneTrans.name = BinaryIO::ReadString(file, length);
}
//BR<42>THER MAY I HAVE SOME L<><4C>PS?
uint8_t loops = (m_ZoneFileFormatVersion < Zone::ZoneFileFormatVersion::EarlyAlpha || m_ZoneFileFormatVersion >= Zone::ZoneFileFormatVersion::Launch) ? 2 : 5;
for (uint8_t i = 0; i < loops; ++i) {
sceneTrans.points.push_back(LoadSceneTransitionInfo(file));
}
m_SceneTransitions.push_back(sceneTrans);
}
SceneTransitionInfo Zone::LoadSceneTransitionInfo(std::ifstream & file) {
SceneTransitionInfo info;
BinaryIO::BinaryRead(file, info.sceneID);
BinaryIO::BinaryRead(file, info.position);
return info;
}
void Zone::LoadPath(std::ifstream & file) {
// Currently only spawner (type 4) paths are supported
Path path = Path();
uint32_t unknown1;
uint32_t pathType;
uint32_t pathBehavior;
BinaryIO::BinaryRead(file, path.pathVersion);
uint8_t stringLength;
BinaryIO::BinaryRead(file, stringLength);
for (uint8_t i = 0; i < stringLength; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.pathName.push_back(character);
}
BinaryIO::BinaryRead(file, pathType);
path.pathType = PathType(pathType);
BinaryIO::BinaryRead(file, unknown1);
BinaryIO::BinaryRead(file, pathBehavior);
path.pathType = PathType(pathType);
if (path.pathType == PathType::MovingPlatform) {
if (path.pathVersion >= 18) {
uint8_t unknown;
BinaryIO::BinaryRead(file, unknown);
}
else if (path.pathVersion >= 13) {
uint8_t count;
BinaryIO::BinaryRead(file, count);
for (uint8_t i = 0; i < count; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.movingPlatform.platformTravelSound.push_back(character);
}
}
}
else if (path.pathType == PathType::Property) {
int32_t unknown;
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, path.property.price);
BinaryIO::BinaryRead(file, path.property.rentalTime);
BinaryIO::BinaryRead(file, path.property.associatedZone);
uint8_t count1;
BinaryIO::BinaryRead(file, count1);
for (uint8_t i = 0; i < count1; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.property.displayName.push_back(character);
}
uint32_t count2;
BinaryIO::BinaryRead(file, count2);
for (uint8_t i = 0; i < count2; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.property.displayDesc.push_back(character);
}
int32_t unknown1;
BinaryIO::BinaryRead(file, unknown1);
BinaryIO::BinaryRead(file, path.property.cloneLimit);
BinaryIO::BinaryRead(file, path.property.repMultiplier);
BinaryIO::BinaryRead(file, path.property.rentalTimeUnit);
BinaryIO::BinaryRead(file, path.property.achievementRequired);
BinaryIO::BinaryRead(file, path.property.playerZoneCoords.x);
BinaryIO::BinaryRead(file, path.property.playerZoneCoords.y);
BinaryIO::BinaryRead(file, path.property.playerZoneCoords.z);
BinaryIO::BinaryRead(file, path.property.maxBuildHeight);
}
else if (path.pathType == PathType::Camera) {
uint8_t count;
BinaryIO::BinaryRead(file, count);
for (uint8_t i = 0; i < count; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.camera.nextPath.push_back(character);
}
if (path.pathVersion >= 14) {
uint8_t unknown;
BinaryIO::BinaryRead(file, unknown);
}
} else if (path.pathType == PathType::Spawner) {
//SpawnerPath* path = static_cast<SpawnerPath*>(path); // Convert to a spawner path
BinaryIO::BinaryRead(file, path.spawner.spawnedLOT);
BinaryIO::BinaryRead(file, path.spawner.respawnTime);
BinaryIO::BinaryRead(file, path.spawner.maxToSpawn);
BinaryIO::BinaryRead(file, path.spawner.amountMaintained);
BinaryIO::BinaryRead(file, path.spawner.spawnerObjID);
BinaryIO::BinaryRead(file, path.spawner.spawnerNetActive);
}
// Read waypoints
BinaryIO::BinaryRead(file, path.waypointCount);
for (uint32_t i = 0; i < path.waypointCount; ++i) {
PathWaypoint waypoint = PathWaypoint();
BinaryIO::BinaryRead(file, waypoint.position.x);
BinaryIO::BinaryRead(file, waypoint.position.y);
BinaryIO::BinaryRead(file, waypoint.position.z);
if (path.pathType == PathType::Spawner || path.pathType == PathType::MovingPlatform || path.pathType == PathType::Race) {
BinaryIO::BinaryRead(file, waypoint.rotation.w);
BinaryIO::BinaryRead(file, waypoint.rotation.x);
BinaryIO::BinaryRead(file, waypoint.rotation.y);
BinaryIO::BinaryRead(file, waypoint.rotation.z);
}
if (path.pathType == PathType::MovingPlatform) {
BinaryIO::BinaryRead(file, waypoint.movingPlatform.lockPlayer);
BinaryIO::BinaryRead(file, waypoint.movingPlatform.speed);
BinaryIO::BinaryRead(file, waypoint.movingPlatform.wait);
if (path.pathVersion >= 13) {
uint8_t count1;
BinaryIO::BinaryRead(file, count1);
for (uint8_t i = 0; i < count1; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
waypoint.movingPlatform.departSound.push_back(character);
}
uint8_t count2;
BinaryIO::BinaryRead(file, count2);
for (uint8_t i = 0; i < count2; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
waypoint.movingPlatform.arriveSound.push_back(character);
}
}
}
else if (path.pathType == PathType::Camera) {
float unknown;
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, waypoint.camera.time);
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, waypoint.camera.tension);
BinaryIO::BinaryRead(file, waypoint.camera.continuity);
BinaryIO::BinaryRead(file, waypoint.camera.bias);
}
else if (path.pathType == PathType::Race) {
uint8_t unknown;
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, unknown);
float unknown1;
BinaryIO::BinaryRead(file, unknown1);
BinaryIO::BinaryRead(file, unknown1);
BinaryIO::BinaryRead(file, unknown1);
}
else if (path.pathType == PathType::Rail) {
float unknown;
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, unknown);
BinaryIO::BinaryRead(file, unknown);
if (path.pathVersion >= 17) {
BinaryIO::BinaryRead(file, unknown);
}
}
// object LDF configs
if (path.pathType == PathType::Movement || path.pathType == PathType::Spawner || path.pathType == PathType::Rail) {
uint32_t count;
BinaryIO::BinaryRead(file, count);
for (uint32_t i = 0; i < count; ++i) {
uint8_t count1;
std::string parameter;
std::string value;
BinaryIO::BinaryRead(file, count1);
for (uint8_t i = 0; i < count1; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
parameter.push_back(character);
}
uint8_t count2;
BinaryIO::BinaryRead(file, count2);
for (uint8_t i = 0; i < count2; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
value.push_back(character);
}
LDFBaseData* ldfConfig = LDFBaseData::DataFromString(parameter + "=" + value);
waypoint.config.push_back(ldfConfig);
}
}
path.pathWaypoints.push_back(waypoint);
}
m_Paths.push_back(path);
}

231
dZoneManager/Zone.h Normal file
View File

@@ -0,0 +1,231 @@
#pragma once
#include "dZMCommon.h"
#include "LDFFormat.h"
#include "../thirdparty/tinyxml2/tinyxml2.h"
#include <string>
#include <vector>
#include <map>
class Level;
class LUTriggers {
public:
struct Command {
std::string id;
std::string target;
std::string targetName;
std::string args;
};
struct Event {
std::string eventID;
std::vector<Command*> commands;
};
struct Trigger {
uint32_t id;
bool enabled;
std::vector<Event*> events;
};
};
struct SceneRef {
std::string filename;
uint32_t id;
uint32_t sceneType; //0 = general, 1 = audio?
std::string name;
Level* level;
std::map<uint32_t, LUTriggers::Trigger*> triggers;
};
struct SceneTransitionInfo {
uint64_t sceneID; //id of the scene being transitioned to.
NiPoint3 position;
};
struct SceneTransition {
std::string name;
std::vector<SceneTransitionInfo> points;
};
struct MovingPlatformPathWaypoint {
uint8_t lockPlayer;
float speed;
float wait;
std::string departSound;
std::string arriveSound;
};
struct CameraPathWaypoint {
float time;
float tension;
float continuity;
float bias;
};
struct PathWaypoint {
NiPoint3 position;
NiQuaternion rotation; // not included in all, but it's more convenient here
MovingPlatformPathWaypoint movingPlatform;
CameraPathWaypoint camera;
std::vector<LDFBaseData*> config;
};
enum class PathType : uint32_t {
Movement = 0,
MovingPlatform = 1,
Property = 2,
Camera = 3,
Spawner = 4,
Showcase = 5,
Race = 6,
Rail = 7
};
enum class PathBehavior : uint32_t {
Loop = 0,
Bounce = 1,
Once = 2
};
enum class PropertyRentalTimeUnit : int32_t{
Forever = 0,
Seconds = 1,
Minutes = 2,
Hours = 3,
Days = 4,
Weeks = 5,
Months = 6,
Years = 7
};
enum class PropertyAchievmentRequired : int32_t {
None = 0,
Builder = 1,
Craftsman = 2,
SeniorBuilder = 3,
JourneyMan = 4,
MasterBuilder = 5,
Architect = 6,
SeniorArchitect = 7,
MasterArchitect = 8,
Visionary = 9,
Exemplar = 10
};
struct MovingPlatformPath {
std::string platformTravelSound;
};
struct PropertyPath {
int32_t price;
int32_t rentalTime;
uint64_t associatedZone;
std::string displayName;
std::string displayDesc;
int32_t cloneLimit;
float repMultiplier;
PropertyRentalTimeUnit rentalTimeUnit;
PropertyAchievmentRequired achievementRequired;
NiPoint3 playerZoneCoords;
float maxBuildHeight;
};
struct CameraPath {
std::string nextPath;
};
struct SpawnerPath {
LOT spawnedLOT;
uint32_t respawnTime;
int32_t maxToSpawn;
uint32_t amountMaintained;
LWOOBJID spawnerObjID;
uint8_t spawnerNetActive;
};
struct Path {
uint32_t pathVersion;
PathType pathType;
std::string pathName;
PathBehavior pathBehavior;
uint32_t waypointCount;
std::vector<PathWaypoint> pathWaypoints;
SpawnerPath spawner;
MovingPlatformPath movingPlatform;
PropertyPath property;
CameraPath camera;
};
class Zone {
public:
enum class ZoneFileFormatVersion : uint32_t { //Times are guessed.
PreAlpha = 0x20,
EarlyAlpha = 0x23,
Alpha = 0x24,
LateAlpha = 0x25,
Beta = 0x26,
Launch = 0x27,
Auramar = 0x28,
Latest = 0x29
};
public:
Zone(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID);
~Zone();
void Initalize();
void LoadZoneIntoMemory();
std::string GetFilePathForZoneID();
uint32_t CalculateChecksum();
void LoadLevelsIntoMemory();
void AddRevision(LWOSCENEID sceneID, uint32_t revision);
const LWOZONEID& GetZoneID() const { return m_ZoneID; }
const uint32_t GetChecksum() const { return m_CheckSum; }
const void PrintAllGameObjects();
LUTriggers::Trigger* GetTrigger(uint32_t sceneID, uint32_t triggerID);
const Path* GetPath(std::string name) const;
uint32_t GetWorldID() const { return m_WorldID; }
[[nodiscard]] std::string GetZoneName() const { return m_ZoneName; }
const NiPoint3& GetSpawnPos() const { return m_Spawnpoint; }
const NiQuaternion& GetSpawnRot() const { return m_SpawnpointRotation; }
void SetSpawnPos(const NiPoint3& pos) { m_Spawnpoint = pos; }
void SetSpawnRot(const NiQuaternion& rot) { m_SpawnpointRotation = rot; }
private:
LWOZONEID m_ZoneID;
std::string m_ZoneFilePath;
uint32_t m_NumberOfScenesLoaded;
uint32_t m_NumberOfObjectsLoaded;
uint32_t m_NumberOfSceneTransitionsLoaded;
ZoneFileFormatVersion m_ZoneFileFormatVersion;
uint32_t m_CheckSum;
uint32_t m_WorldID; //should be equal to the MapID
NiPoint3 m_Spawnpoint;
NiQuaternion m_SpawnpointRotation;
uint32_t m_SceneCount;
std::string m_ZonePath; //Path to the .luz's folder
std::string m_ZoneName; //Name given to the zone by a level designer
std::string m_ZoneDesc; //Description of the zone by a level designer
std::string m_ZoneRawPath; //Path to the .raw file of this zone.
std::map<LWOSCENEID, SceneRef, mapCompareLwoSceneIDs> m_Scenes;
std::vector<SceneTransition> m_SceneTransitions;
uint32_t m_PathDataLength;
//std::vector<char> m_PathData; //Binary path data
std::vector<Path> m_Paths;
std::map<LWOSCENEID, uint32_t, mapCompareLwoSceneIDs> m_MapRevisions; //rhs is the revision!
//private ("helper") functions:
void LoadScene(std::ifstream& file);
std::vector<LUTriggers::Trigger*> LoadLUTriggers(std::string triggerFile, LWOSCENEID sceneID);
void LoadSceneTransition(std::ifstream& file);
SceneTransitionInfo LoadSceneTransitionInfo(std::ifstream& file);
void LoadPath(std::ifstream& file);
};

26
dZoneManager/dZMCommon.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include "dCommonVars.h"
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "LDFFormat.h"
#include <vector>
struct mapCompareLwoSceneIDs {
bool operator()(const LWOSCENEID& lhs, const LWOSCENEID& rhs) const { return lhs < rhs; }
};
struct SceneObject {
LWOOBJID id;
LOT lot;
uint32_t value1;
uint32_t value2;
NiPoint3 position;
NiQuaternion rotation;
float scale = 1.0f;
//std::string settings;
uint32_t value3;
std::vector<LDFBaseData*> settings;
};
#define LOT_MARKER_PLAYER_START 1931
#define LOT_MARKET_CAMERA_TARGET 2182

View File

@@ -0,0 +1,232 @@
#include "Game.h"
#include "dCommonVars.h"
#include "dZoneManager.h"
#include "EntityManager.h"
#include "dLogger.h"
#include "dConfig.h"
#include "InventoryComponent.h"
#include "DestroyableComponent.h"
#include "GameMessages.h"
#include "VanityUtilities.h"
#include <chrono>
#include "../dWorldServer/ObjectIDManager.h"
dZoneManager* dZoneManager::m_Address = nullptr;
void dZoneManager::Initialize(const LWOZONEID& zoneID) {
Game::logger->Log("dZoneManager", "Preparing zone: %i/%i/%i\n", zoneID.GetMapID(), zoneID.GetInstanceID(), zoneID.GetCloneID());
int64_t startTime = 0;
int64_t endTime = 0;
startTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
LoadZone(zoneID);
LOT zoneControlTemplate = 2365;
std::stringstream query;
auto result = CDClientDatabase::ExecuteQuery("SELECT zoneControlTemplate, ghostdistance_min, ghostdistance FROM ZoneTable WHERE zoneID = " + std::to_string(zoneID.GetMapID()));
if (!result.eof()) {
zoneControlTemplate = result.getIntField("zoneControlTemplate", 2365);
const auto min = result.getIntField("ghostdistance_min", 100);
const auto max = result.getIntField("ghostdistance", 100);
EntityManager::Instance()->SetGhostDistanceMax(max + min);
EntityManager::Instance()->SetGhostDistanceMin(max);
}
result.finalize();
Game::logger->Log("dZoneManager", "Creating zone control object %i\n", zoneControlTemplate);
// Create ZoneControl object
EntityInfo info;
info.lot = zoneControlTemplate;
info.id = 70368744177662;
Entity* zoneControl = EntityManager::Instance()->CreateEntity(info, nullptr, nullptr, true);
m_ZoneControlObject = zoneControl;
m_pZone->Initalize();
endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
Game::logger->Log("dZoneManager", "Zone prepared in: %llu ms\n", (endTime - startTime));
VanityUtilities::SpawnVanity();
}
dZoneManager::~dZoneManager() {
if (m_pZone) delete m_pZone;
for (std::pair<LWOOBJID, Spawner*> p : m_Spawners) {
if (p.second) {
delete p.second;
p.second = nullptr;
}
m_Spawners.erase(p.first);
}
}
Zone * dZoneManager::GetZone() {
return m_pZone;
}
void dZoneManager::LoadZone(const LWOZONEID& zoneID) {
if (m_pZone) delete m_pZone;
m_ZoneID = zoneID;
m_pZone = new Zone(zoneID.GetMapID(), zoneID.GetInstanceID(), zoneID.GetCloneID());
}
void dZoneManager::NotifyZone(const dZoneNotifier & notifier, const LWOOBJID& objectID) {
switch (notifier) {
case dZoneNotifier::SpawnedObjectDestroyed:
break;
case dZoneNotifier::SpawnedChildObjectDestroyed:
break;
case dZoneNotifier::ReloadZone:
Game::logger->Log("dZoneManager", "Forcing reload of zone %i\n", m_ZoneID.GetMapID());
LoadZone(m_ZoneID);
m_pZone->Initalize();
break;
case dZoneNotifier::UserJoined:
break;
case dZoneNotifier::UserMoved:
break;
case dZoneNotifier::PrintAllGameObjects:
m_pZone->PrintAllGameObjects();
break;
case dZoneNotifier::InvalidNotifier:
Game::logger->Log("dZoneManager", "Got an invalid zone notifier.\n");
break;
default:
Game::logger->Log("dZoneManager", "Unknown zone notifier: %i\n", int(notifier));
}
}
void dZoneManager::AddSpawner(LWOOBJID id, Spawner* spawner)
{
m_Spawners.insert_or_assign(id, spawner);
}
LWOZONEID dZoneManager::GetZoneID() const
{
return m_ZoneID;
}
void dZoneManager::Update(float deltaTime) {
for (auto spawner : m_Spawners) {
spawner.second->Update(deltaTime);
}
}
LWOOBJID dZoneManager::MakeSpawner(SpawnerInfo info)
{
auto objectId = info.spawnerID;
if (objectId == LWOOBJID_EMPTY)
{
objectId = ObjectIDManager::Instance()->GenerateObjectID();
objectId = GeneralUtils::SetBit(objectId, OBJECT_BIT_CLIENT);
info.spawnerID = objectId;
}
auto* spawner = new Spawner(info);
EntityInfo entityInfo{};
entityInfo.id = objectId;
entityInfo.lot = 176;
auto* entity = EntityManager::Instance()->CreateEntity(entityInfo, nullptr, nullptr, false, objectId);
EntityManager::Instance()->ConstructEntity(entity);
AddSpawner(objectId, spawner);
return objectId;
}
Spawner* dZoneManager::GetSpawner(const LWOOBJID id)
{
const auto& index = m_Spawners.find(id);
if (index == m_Spawners.end()) {
return nullptr;
}
return index->second;
}
void dZoneManager::RemoveSpawner(const LWOOBJID id)
{
auto* spawner = GetSpawner(id);
if (spawner == nullptr) {
Game::logger->Log("dZoneManager", "Failed to find spawner (%llu)\n", id);
return;
}
auto* entity = EntityManager::Instance()->GetEntity(id);
if (entity != nullptr) {
entity->Kill();
}
else {
Game::logger->Log("dZoneManager", "Failed to find spawner entity (%llu)\n", id);
}
for (auto* node : spawner->m_Info.nodes)
{
for (const auto& element : node->entities)
{
auto* nodeEntity = EntityManager::Instance()->GetEntity(element);
if (nodeEntity == nullptr) continue;
nodeEntity->Kill();
}
node->entities.clear();
}
spawner->Deactivate();
Game::logger->Log("dZoneManager", "Destroying spawner (%llu)\n", id);
m_Spawners.erase(id);
delete spawner;
}
std::vector<Spawner*> dZoneManager::GetSpawnersByName(std::string spawnerName) {
std::vector<Spawner*> spawners;
for (const auto& spawner : m_Spawners) {
if (spawner.second->GetName() == spawnerName) {
spawners.push_back(spawner.second);
}
}
return spawners;
}
std::vector<Spawner*> dZoneManager::GetSpawnersInGroup(std::string group) {
std::vector<Spawner*> spawnersInGroup;
for (auto spawner : m_Spawners) {
for (std::string entityGroup : spawner.second->m_Info.groups) {
if (entityGroup == group) {
spawnersInGroup.push_back(spawner.second);
}
}
}
return spawnersInGroup;
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include "dZMCommon.h"
#include "Zone.h"
#include "Spawner.h"
#include <map>
class dZoneManager {
public:
enum class dZoneNotifier {
SpawnedObjectDestroyed,
SpawnedChildObjectDestroyed, //Used for when an object (like a stromling) needs to notify the spawner to respawn a new enemy.
ReloadZone, //Forces the server and all connects clients to reload the map
UserJoined,
UserMoved,
PrintAllGameObjects, //Using this is a BAD idea in production
InvalidNotifier
};
public:
static dZoneManager* Instance() {
if (!m_Address) {
m_Address = new dZoneManager();
}
return m_Address;
}
void Initialize(const LWOZONEID& zoneID);
~dZoneManager();
Zone* GetZone(); //Gets a pointer to the currently loaded zone.
void LoadZone(const LWOZONEID& zoneID); //Discard the current zone (if any) and loads a new zone.
void NotifyZone(const dZoneNotifier& notifier, const LWOOBJID& objectID); //Notifies the zone of a certain event or command.
void AddSpawner(LWOOBJID id, Spawner* spawner);
LWOZONEID GetZoneID() const;
LWOOBJID MakeSpawner(SpawnerInfo info);
Spawner* GetSpawner(LWOOBJID id);
void RemoveSpawner(LWOOBJID id);
std::vector<Spawner*> GetSpawnersByName(std::string spawnerName);
std::vector<Spawner*> GetSpawnersInGroup(std::string group);
void Update(float deltaTime);
Entity* GetZoneControlObject() { return m_ZoneControlObject; }
private:
static dZoneManager* m_Address; //Singleton
Zone* m_pZone;
LWOZONEID m_ZoneID;
std::map<LWOOBJID, Spawner*> m_Spawners;
Entity* m_ZoneControlObject;
};