mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-08-04 09:44:10 +00:00
Public release of the DLU server code!
Have fun!
This commit is contained in:
353
dZoneManager/Level.cpp
Normal file
353
dZoneManager/Level.cpp
Normal 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
73
dZoneManager/Level.h
Normal 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
261
dZoneManager/Spawner.cpp
Normal 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
85
dZoneManager/Spawner.h
Normal 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
571
dZoneManager/Zone.cpp
Normal 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
231
dZoneManager/Zone.h
Normal 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
26
dZoneManager/dZMCommon.h
Normal 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
|
232
dZoneManager/dZoneManager.cpp
Normal file
232
dZoneManager/dZoneManager.cpp
Normal 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;
|
||||
}
|
51
dZoneManager/dZoneManager.h
Normal file
51
dZoneManager/dZoneManager.h
Normal 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;
|
||||
};
|
Reference in New Issue
Block a user