aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp4
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h2
-rw-r--r--src/server/game/DataStores/DB2LoadInfo.h18
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp2
-rw-r--r--src/server/game/DataStores/DB2Stores.h1
-rw-r--r--src/server/game/DataStores/DB2Structure.h9
-rw-r--r--src/server/game/DataStores/M2Stores.cpp283
-rw-r--r--src/server/game/DataStores/M2Stores.h36
-rw-r--r--src/server/game/DataStores/M2Structure.h137
-rw-r--r--src/server/game/Entities/Object/Object.cpp18
-rw-r--r--src/server/game/Entities/Player/CinematicMgr.cpp173
-rw-r--r--src/server/game/Entities/Player/CinematicMgr.h58
-rw-r--r--src/server/game/Entities/Player/Player.cpp13
-rw-r--r--src/server/game/Entities/Player/Player.h6
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp4
-rw-r--r--src/server/game/Maps/Map.cpp13
-rw-r--r--src/server/game/World/World.cpp3
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp18
-rw-r--r--src/tools/map_extractor/System.cpp96
19 files changed, 885 insertions, 9 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp
index fd8db617efd..d4a5637487a 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.cpp
+++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp
@@ -171,6 +171,10 @@ void HotfixDatabaseConnection::DoPrepareStatements()
PREPARE_LOCALE_STMT(HOTFIX_SEL_CHR_SPECIALIZATION, "SELECT ID, Name_lang, Name2_lang, Description_lang FROM chr_specialization_locale"
" WHERE locale = ?", CONNECTION_SYNCH);
+ // CinematicCamera.db2
+ PrepareStatement(HOTFIX_SEL_CINEMATIC_CAMERA, "SELECT ID, Model, OriginX, OriginY, OriginZ, OriginFacing, SoundID FROM cinematic_camera"
+ " ORDER BY ID DESC", CONNECTION_SYNCH);
+
// CinematicSequences.db2
PrepareStatement(HOTFIX_SEL_CINEMATIC_SEQUENCES, "SELECT ID, SoundID, Camera1, Camera2, Camera3, Camera4, Camera5, Camera6, Camera7, Camera8"
" FROM cinematic_sequences ORDER BY ID DESC", CONNECTION_SYNCH);
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h
index 2415be46874..5438e648d3e 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.h
+++ b/src/server/database/Database/Implementation/HotfixDatabase.h
@@ -111,6 +111,8 @@ enum HotfixDatabaseStatements
HOTFIX_SEL_CHR_SPECIALIZATION,
HOTFIX_SEL_CHR_SPECIALIZATION_LOCALE,
+ HOTFIX_SEL_CINEMATIC_CAMERA,
+
HOTFIX_SEL_CINEMATIC_SEQUENCES,
HOTFIX_SEL_CREATURE_DISPLAY_INFO,
diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h
index aa1e507da16..e8e93a0d87e 100644
--- a/src/server/game/DataStores/DB2LoadInfo.h
+++ b/src/server/game/DataStores/DB2LoadInfo.h
@@ -727,6 +727,24 @@ struct ChrSpecializationLoadInfo
}
};
+struct CinematicCameraLoadInfo
+{
+ static DB2LoadInfo Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { false, FT_STRING_NOT_LOCALIZED, "Model" },
+ { false, FT_FLOAT, "OriginX" },
+ { false, FT_FLOAT, "OriginY" },
+ { false, FT_FLOAT, "OriginZ" },
+ { false, FT_FLOAT, "OriginFacing" },
+ { false, FT_SHORT, "SoundID" },
+ };
+ return { &fields[0], std::extent<decltype(fields)>::value, CinematicCameraMeta::Instance(), HOTFIX_SEL_CINEMATIC_CAMERA };
+ }
+};
+
struct CinematicSequencesLoadInfo
{
static DB2LoadInfo Instance()
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index 98d2ca3189e..c156a11d5ec 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -56,6 +56,7 @@ DB2Storage<ChrClassesEntry> sChrClassesStore("ChrClasses.db2
DB2Storage<ChrClassesXPowerTypesEntry> sChrClassesXPowerTypesStore("ChrClassesXPowerTypes.db2", ChrClassesXPowerTypesLoadInfo::Instance());
DB2Storage<ChrRacesEntry> sChrRacesStore("ChrRaces.db2", ChrRacesLoadInfo::Instance());
DB2Storage<ChrSpecializationEntry> sChrSpecializationStore("ChrSpecialization.db2", ChrSpecializationLoadInfo::Instance());
+DB2Storage<CinematicCameraEntry> sCinematicCameraStore("CinematicCamera.db2", CinematicCameraLoadInfo::Instance());
DB2Storage<CinematicSequencesEntry> sCinematicSequencesStore("CinematicSequences.db2", CinematicSequencesLoadInfo::Instance());
DB2Storage<CreatureDisplayInfoEntry> sCreatureDisplayInfoStore("CreatureDisplayInfo.db2", CreatureDisplayInfoLoadInfo::Instance());
DB2Storage<CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore("CreatureDisplayInfoExtra.db2", CreatureDisplayInfoExtraLoadInfo::Instance());
@@ -353,6 +354,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sChrClassesXPowerTypesStore);
LOAD_DB2(sChrRacesStore);
LOAD_DB2(sChrSpecializationStore);
+ LOAD_DB2(sCinematicCameraStore);
LOAD_DB2(sCinematicSequencesStore);
LOAD_DB2(sCreatureDisplayInfoStore);
LOAD_DB2(sCreatureDisplayInfoExtraStore);
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index 94999b10ba6..7a8e2dc7e19 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -49,6 +49,7 @@ TC_GAME_API extern DB2Storage<ChatChannelsEntry> sChatChannel
TC_GAME_API extern DB2Storage<ChrClassesEntry> sChrClassesStore;
TC_GAME_API extern DB2Storage<ChrRacesEntry> sChrRacesStore;
TC_GAME_API extern DB2Storage<ChrSpecializationEntry> sChrSpecializationStore;
+TC_GAME_API extern DB2Storage<CinematicCameraEntry> sCinematicCameraStore;
TC_GAME_API extern DB2Storage<CinematicSequencesEntry> sCinematicSequencesStore;
TC_GAME_API extern DB2Storage<CreatureDisplayInfoEntry> sCreatureDisplayInfoStore;
TC_GAME_API extern DB2Storage<CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore;
diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h
index 180960aec97..b4534b63248 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -444,6 +444,15 @@ struct ChrSpecializationEntry
uint32 AnimReplacementSetID;
};
+struct CinematicCameraEntry
+{
+ uint32 ID;
+ char const* Model; // Model filename (translate .mdx to .m2)
+ DBCPosition3D Origin; // Position in map used for basis for M2 co-ordinates
+ float OriginFacing; // Orientation in map used for basis for M2 co-ordinates
+ uint16 SoundID; // Sound ID (voiceover for cinematic)
+};
+
struct CinematicSequencesEntry
{
uint32 ID;
diff --git a/src/server/game/DataStores/M2Stores.cpp b/src/server/game/DataStores/M2Stores.cpp
new file mode 100644
index 00000000000..171cd26f061
--- /dev/null
+++ b/src/server/game/DataStores/M2Stores.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "DB2Stores.h"
+#include "M2Structure.h"
+#include "M2Stores.h"
+#include "Common.h"
+#include "Containers.h"
+#include "Log.h"
+#include "World.h"
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <boost/filesystem/path.hpp>
+
+std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore;
+
+// Convert the geomoetry from a spline value, to an actual WoW XYZ
+G3D::Vector3 translateLocation(G3D::Vector4 const* dbcLocation, G3D::Vector3 const* basePosition, G3D::Vector3 const* splineVector)
+{
+ G3D::Vector3 work;
+ float x = basePosition->x + splineVector->x;
+ float y = basePosition->y + splineVector->y;
+ float z = basePosition->z + splineVector->z;
+ float const distance = sqrt((x * x) + (y * y));
+ float angle = std::atan2(x, y) - dbcLocation->w;
+
+ if (angle < 0)
+ angle += 2 * float(M_PI);
+
+ work.x = dbcLocation->x + (distance * sin(angle));
+ work.y = dbcLocation->y + (distance * cos(angle));
+ work.z = dbcLocation->z + z;
+ return work;
+}
+
+// Number of cameras not used. Multiple cameras never used in 7.1.5
+bool readCamera(M2Camera const* cam, uint32 buffSize, M2Header const* header, CinematicCameraEntry const* dbcentry)
+{
+ char const* buffer = reinterpret_cast<char const*>(header);
+
+ FlyByCameraCollection cameras;
+ FlyByCameraCollection targetcam;
+
+ G3D::Vector4 dbcData;
+ dbcData.x = dbcentry->Origin.X;
+ dbcData.y = dbcentry->Origin.Y;
+ dbcData.z = dbcentry->Origin.Z;
+ dbcData.w = dbcentry->OriginFacing;
+
+ // Read target locations, only so that we can calculate orientation
+ for (uint32 k = 0; k < cam->target_positions.timestamps.number; ++k)
+ {
+ // Extract Target positions
+ if (cam->target_positions.timestamps.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ M2Array const* targTsArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.timestamps.offset_elements);
+ if (targTsArray->offset_elements + sizeof(uint32) > buffSize || cam->target_positions.values.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ uint32 const* targTimestamps = reinterpret_cast<uint32 const*>(buffer + targTsArray->offset_elements);
+ M2Array const* targArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.values.offset_elements);
+
+ if (targArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ M2SplineKey<G3D::Vector3> const* targPositions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + targArray->offset_elements);
+
+ // Read the data for this set
+ uint32 currPos = targArray->offset_elements;
+ for (uint32 i = 0; i < targTsArray->number; ++i)
+ {
+ if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ // Translate co-ordinates
+ G3D::Vector3 newPos = translateLocation(&dbcData, &cam->target_position_base, &targPositions->p0);
+
+ // Add to vector
+ FlyByCamera thisCam;
+ thisCam.timeStamp = targTimestamps[i];
+ thisCam.locations.x = newPos.x;
+ thisCam.locations.y = newPos.y;
+ thisCam.locations.z = newPos.z;
+ thisCam.locations.w = 0.0f;
+ targetcam.push_back(thisCam);
+ targPositions++;
+ currPos += sizeof(M2SplineKey<G3D::Vector3>);
+ }
+ }
+
+ // Read camera positions and timestamps (translating first position of 3 only, we don't need to translate the whole spline)
+ for (uint32 k = 0; k < cam->positions.timestamps.number; ++k)
+ {
+ // Extract Camera positions for this set
+ if (cam->positions.timestamps.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ M2Array const* posTsArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.timestamps.offset_elements);
+ if (posTsArray->offset_elements + sizeof(uint32) > buffSize || cam->positions.values.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ uint32 const* posTimestamps = reinterpret_cast<uint32 const*>(buffer + posTsArray->offset_elements);
+ M2Array const* posArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.values.offset_elements);
+ if (posArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ M2SplineKey<G3D::Vector3> const* positions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + posArray->offset_elements);
+
+ // Read the data for this set
+ uint32 currPos = posArray->offset_elements;
+ for (uint32 i = 0; i < posTsArray->number; ++i)
+ {
+ if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ // Translate co-ordinates
+ G3D::Vector3 newPos = translateLocation(&dbcData, &cam->position_base, &positions->p0);
+
+ // Add to vector
+ FlyByCamera thisCam;
+ thisCam.timeStamp = posTimestamps[i];
+ thisCam.locations.x = newPos.x;
+ thisCam.locations.y = newPos.y;
+ thisCam.locations.z = newPos.z;
+
+ if (targetcam.size() > 0)
+ {
+ // Find the target camera before and after this camera
+ FlyByCamera lastTarget;
+ FlyByCamera nextTarget;
+
+ // Pre-load first item
+ lastTarget = targetcam[0];
+ nextTarget = targetcam[0];
+ for (uint32 j = 0; j < targetcam.size(); ++j)
+ {
+ nextTarget = targetcam[j];
+ if (targetcam[j].timeStamp > posTimestamps[i])
+ break;
+
+ lastTarget = targetcam[j];
+ }
+
+ float x = lastTarget.locations.x;
+ float y = lastTarget.locations.y;
+ float z = lastTarget.locations.z;
+
+ // Now, the timestamps for target cam and position can be different. So, if they differ we interpolate
+ if (lastTarget.timeStamp != posTimestamps[i])
+ {
+ uint32 timeDiffTarget = nextTarget.timeStamp - lastTarget.timeStamp;
+ uint32 timeDiffThis = posTimestamps[i] - lastTarget.timeStamp;
+ float xDiff = nextTarget.locations.x - lastTarget.locations.x;
+ float yDiff = nextTarget.locations.y - lastTarget.locations.y;
+ float zDiff = nextTarget.locations.z - lastTarget.locations.z;
+ x = lastTarget.locations.x + (xDiff * (float(timeDiffThis) / float(timeDiffTarget)));
+ y = lastTarget.locations.y + (yDiff * (float(timeDiffThis) / float(timeDiffTarget)));
+ z = lastTarget.locations.z + (zDiff * (float(timeDiffThis) / float(timeDiffTarget)));
+ }
+ float xDiff = x - thisCam.locations.x;
+ float yDiff = y - thisCam.locations.y;
+ thisCam.locations.w = std::atan2(yDiff, xDiff);
+
+ if (thisCam.locations.w < 0)
+ thisCam.locations.w += 2 * float(M_PI);
+ }
+
+ cameras.push_back(thisCam);
+ positions++;
+ currPos += sizeof(M2SplineKey<G3D::Vector3>);
+ }
+ }
+
+ sFlyByCameraStore[dbcentry->ID] = cameras;
+ return true;
+}
+
+void LoadM2Cameras(std::string const& dataPath)
+{
+ sFlyByCameraStore.clear();
+ TC_LOG_INFO("server.loading", ">> Loading Cinematic Camera files");
+
+ uint32 oldMSTime = getMSTime();
+ for (CinematicCameraEntry const* cameraEntry : sCinematicCameraStore)
+ {
+ std::string filenameWork = dataPath;
+ filenameWork.append(cameraEntry->Model);
+
+ // Replace slashes (always to forward slash, because boost!)
+ std::replace(filenameWork.begin(), filenameWork.end(), '\\', '/');
+
+ boost::filesystem::path filename = filenameWork;
+
+ // Convert to native format
+ filename.make_preferred();
+
+ // Replace mdx to .m2
+ filename.replace_extension("m2");
+
+ std::ifstream m2file(filename.string().c_str(), std::ios::in | std::ios::binary);
+ if (!m2file.is_open())
+ continue;
+
+ // Get file size
+ m2file.seekg(0, std::ios::end);
+ std::streamoff const fileSize = m2file.tellg();
+
+ // Reject if not at least the size of the header
+ if (static_cast<uint32 const>(fileSize) < sizeof(M2Header) + 4)
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File is smaller than header size", filename.string().c_str());
+ m2file.close();
+ continue;
+ }
+
+ // Read 4 bytes (signature)
+ m2file.seekg(0, std::ios::beg);
+ char fileCheck[5];
+ m2file.read(fileCheck, 4);
+ fileCheck[4] = '\0';
+
+ // Check file has correct magic (MD21)
+ if (strcmp(fileCheck, "MD21"))
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File identifier not found.", filename.string().c_str());
+ m2file.close();
+ continue;
+ }
+
+ // Now we have a good file, read it all into a vector of char's, then close the file.
+ std::vector<char> buffer(fileSize);
+ m2file.seekg(0, std::ios::beg);
+ if (!m2file.read(buffer.data(), fileSize))
+ {
+ m2file.close();
+ continue;
+ }
+ m2file.close();
+
+ bool fileValid = true;
+ uint32 m2start = 0;
+ char const* ptr = buffer.data();
+ while (m2start + 4 < buffer.size() && *reinterpret_cast<uint32 const*>(ptr) != '02DM')
+ {
+ ++m2start;
+ ++ptr;
+ if (m2start + sizeof(M2Header) > buffer.size())
+ {
+ fileValid = false;
+ break;
+ }
+ }
+
+ if (!fileValid)
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File is smaller than header size.", filename.string().c_str());
+ continue;
+ }
+
+ // Read header
+ M2Header const* header = reinterpret_cast<M2Header const*>(buffer.data() + m2start);
+
+ if (m2start + header->ofsCameras + sizeof(M2Camera) > static_cast<uint32 const>(fileSize))
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.string().c_str());
+ continue;
+ }
+
+ // Get camera(s) - Main header, then dump them.
+ M2Camera const* cam = reinterpret_cast<M2Camera const*>(buffer.data() + m2start + header->ofsCameras);
+ if (!readCamera(cam, fileSize - m2start, header, cameraEntry))
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.string().c_str());
+ }
+ TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " cinematic waypoint sets in %u ms", sFlyByCameraStore.size(), GetMSTimeDiffToNow(oldMSTime));
+}
diff --git a/src/server/game/DataStores/M2Stores.h b/src/server/game/DataStores/M2Stores.h
new file mode 100644
index 00000000000..11d7f2a2c65
--- /dev/null
+++ b/src/server/game/DataStores/M2Stores.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITY_M2STORES_H
+#define TRINITY_M2STORES_H
+
+#include "Common.h"
+#include "G3D/Vector4.h"
+
+struct FlyByCamera
+{
+ uint32 timeStamp;
+ G3D::Vector4 locations;
+};
+
+typedef std::vector<FlyByCamera> FlyByCameraCollection;
+
+TC_GAME_API extern std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore;
+
+TC_GAME_API void LoadM2Cameras(std::string const& dataPath);
+
+#endif
diff --git a/src/server/game/DataStores/M2Structure.h b/src/server/game/DataStores/M2Structure.h
new file mode 100644
index 00000000000..7e7af4b618b
--- /dev/null
+++ b/src/server/game/DataStores/M2Structure.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITY_M2STRUCTURE_H
+#define TRINITY_M2STRUCTURE_H
+
+#include "Common.h"
+#include "Util.h"
+
+#include <G3D/Vector3.h>
+#include <G3D/AABox.h>
+
+// Structures using to access raw DBC data and required packing to portability
+#pragma pack(push, 1)
+// Structures for M2 file. Source: https://wowdev.wiki
+template<typename T>
+struct M2SplineKey
+{
+ T p0;
+ T p1;
+ T p2;
+};
+
+struct M2Header
+{
+ char Magic[4]; // "MD20"
+ uint32 Version; // The version of the format.
+ uint32 lName; // Length of the model's name including the trailing \0
+ uint32 ofsName; // Offset to the name, it seems like models can get reloaded by this name.should be unique, i guess.
+ uint32 GlobalModelFlags; // 0x0001: tilt x, 0x0002: tilt y, 0x0008: add 2 fields in header, 0x0020: load .phys data (MoP+), 0x0080: has _lod .skin files (MoP?+), 0x0100: is camera related.
+ uint32 nGlobalSequences;
+ uint32 ofsGlobalSequences; // A list of timestamps.
+ uint32 nAnimations;
+ uint32 ofsAnimations; // Information about the animations in the model.
+ uint32 nAnimationLookup;
+ uint32 ofsAnimationLookup; // Mapping of global IDs to the entries in the Animation sequences block.
+ uint32 nBones; // MAX_BONES = 0x100
+ uint32 ofsBones; // Information about the bones in this model.
+ uint32 nKeyBoneLookup;
+ uint32 ofsKeyBoneLookup; // Lookup table for key skeletal bones.
+ uint32 nVertices;
+ uint32 ofsVertices; // Vertices of the model.
+ uint32 nViews; // Views (LOD) are now in .skins.
+ uint32 nSubmeshAnimations;
+ uint32 ofsSubmeshAnimations; // Submesh color and alpha animations definitions.
+ uint32 nTextures;
+ uint32 ofsTextures; // Textures of this model.
+ uint32 nTransparency;
+ uint32 ofsTransparency; // Transparency of textures.
+ uint32 nUVAnimation;
+ uint32 ofsUVAnimation;
+ uint32 nTexReplace;
+ uint32 ofsTexReplace; // Replaceable Textures.
+ uint32 nRenderFlags;
+ uint32 ofsRenderFlags; // Blending modes / render flags.
+ uint32 nBoneLookupTable;
+ uint32 ofsBoneLookupTable; // A bone lookup table.
+ uint32 nTexLookup;
+ uint32 ofsTexLookup; // The same for textures.
+ uint32 nTexUnits; // possibly removed with cata?!
+ uint32 ofsTexUnits; // And texture units. Somewhere they have to be too.
+ uint32 nTransLookup;
+ uint32 ofsTransLookup; // Everything needs its lookup. Here are the transparencies.
+ uint32 nUVAnimLookup;
+ uint32 ofsUVAnimLookup;
+ G3D::AABox BoundingBox; // min/max( [1].z, 2.0277779f ) - 0.16f seems to be the maximum camera height
+ float BoundingSphereRadius;
+ G3D::AABox CollisionBox;
+ float CollisionSphereRadius;
+ uint32 nBoundingTriangles;
+ uint32 ofsBoundingTriangles; // Our bounding volumes. Similar structure like in the old ofsViews.
+ uint32 nBoundingVertices;
+ uint32 ofsBoundingVertices;
+ uint32 nBoundingNormals;
+ uint32 ofsBoundingNormals;
+ uint32 nAttachments;
+ uint32 ofsAttachments; // Attachments are for weapons etc.
+ uint32 nAttachLookup;
+ uint32 ofsAttachLookup; // Of course with a lookup.
+ uint32 nEvents;
+ uint32 ofsEvents; // Used for playing sounds when dying and a lot else.
+ uint32 nLights;
+ uint32 ofsLights; // Lights are mainly used in loginscreens but in wands and some doodads too.
+ uint32 nCameras; // Format of Cameras changed with version 271!
+ uint32 ofsCameras; // The cameras are present in most models for having a model in the Character-Tab.
+ uint32 nCameraLookup;
+ uint32 ofsCameraLookup; // And lookup-time again.
+ uint32 nRibbonEmitters;
+ uint32 ofsRibbonEmitters; // Things swirling around. See the CoT-entrance for light-trails.
+ uint32 nParticleEmitters;
+ uint32 ofsParticleEmitters; // Spells and weapons, doodads and loginscreens use them. Blood dripping of a blade? Particles.
+ uint32 nBlendMaps; // This has to deal with blending. Exists IFF (flags & 0x8) != 0. When set, textures blending is overriden by the associated array. See M2/WotLK#Blend_mode_overrides
+ uint32 ofsBlendMaps; // Same as above. Points to an array of uint16 of nBlendMaps entries -- From WoD information.};
+};
+
+struct M2Array
+{
+ uint32_t number;
+ uint32 offset_elements;
+};
+struct M2Track
+{
+ uint16_t interpolation_type;
+ uint16_t global_sequence;
+ M2Array timestamps;
+ M2Array values;
+};
+
+struct M2Camera
+{
+ uint32_t type; // 0: portrait, 1: characterinfo; -1: else (flyby etc.); referenced backwards in the lookup table.
+ float far_clip;
+ float near_clip;
+ M2Track positions; // How the camera's position moves. Should be 3*3 floats.
+ G3D::Vector3 position_base;
+ M2Track target_positions; // How the target moves. Should be 3*3 floats.
+ G3D::Vector3 target_position_base;
+ M2Track rolldata; // The camera can have some roll-effect. Its 0 to 2*Pi.
+ M2Track fovdata; // FoV for this segment
+};
+#pragma pack(pop)
+
+#endif
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 3d6f7498d9b..044d9fc7c62 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -2004,9 +2004,20 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
float WorldObject::GetGridActivationRange() const
{
if (ToPlayer())
+ {
+ if (ToPlayer()->GetCinematicMgr()->IsOnCinematic())
+ return DEFAULT_VISIBILITY_INSTANCE;
return GetMap()->GetVisibilityRange();
+ }
else if (ToCreature())
return ToCreature()->m_SightDistance;
+ else if (ToDynObject())
+ {
+ if (isActiveObject())
+ return GetMap()->GetVisibilityRange();
+ else
+ return 0.0f;
+ }
else
return 0.0f;
}
@@ -2027,6 +2038,8 @@ float WorldObject::GetSightRange(const WorldObject* target) const
{
if (target && target->isActiveObject() && !target->ToPlayer())
return MAX_VISIBILITY_DISTANCE;
+ else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic())
+ return DEFAULT_VISIBILITY_INSTANCE;
else
return GetMap()->GetVisibilityRange();
}
@@ -2036,6 +2049,11 @@ float WorldObject::GetSightRange(const WorldObject* target) const
return SIGHT_RANGE_UNIT;
}
+ if (ToDynObject() && isActiveObject())
+ {
+ return GetMap()->GetVisibilityRange();
+ }
+
return 0.0f;
}
diff --git a/src/server/game/Entities/Player/CinematicMgr.cpp b/src/server/game/Entities/Player/CinematicMgr.cpp
new file mode 100644
index 00000000000..f0da551c43b
--- /dev/null
+++ b/src/server/game/Entities/Player/CinematicMgr.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "CinematicMgr.h"
+#include "Creature.h"
+#include "Player.h"
+#include "TemporarySummon.h"
+
+CinematicMgr::CinematicMgr(Player* playerref)
+{
+ player = playerref;
+ m_cinematicDiff = 0;
+ m_lastCinematicCheck = 0;
+ m_activeCinematicCameraId = 0;
+ m_cinematicLength = 0;
+ m_cinematicCamera = nullptr;
+ m_remoteSightPosition = Position(0.0f, 0.0f, 0.0f);
+ m_CinematicObject = nullptr;
+}
+
+CinematicMgr::~CinematicMgr()
+{
+ if (m_cinematicCamera && m_activeCinematicCameraId)
+ EndCinematic();
+}
+
+void CinematicMgr::BeginCinematic()
+{
+ // Sanity check for active camera set
+ if (m_activeCinematicCameraId == 0)
+ return;
+
+ auto itr = sFlyByCameraStore.find(m_activeCinematicCameraId);
+ if (itr != sFlyByCameraStore.end())
+ {
+ // Initialize diff, and set camera
+ m_cinematicDiff = 0;
+ m_cinematicCamera = &itr->second;
+
+ FlyByCameraCollection::const_iterator camitr = m_cinematicCamera->begin();
+ if (camitr != m_cinematicCamera->end())
+ {
+ Position pos(camitr->locations.x, camitr->locations.y, camitr->locations.z, camitr->locations.w);
+ if (!pos.IsPositionValid())
+ return;
+
+ player->GetMap()->LoadGrid(camitr->locations.x, camitr->locations.y);
+ m_CinematicObject = player->SummonCreature(VISUAL_WAYPOINT, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 5 * MINUTE * IN_MILLISECONDS);
+ if (m_CinematicObject)
+ {
+ m_CinematicObject->setActive(true);
+ player->SetViewpoint(m_CinematicObject, true);
+ }
+
+ // Get cinematic length
+ FlyByCameraCollection::const_reverse_iterator camrevitr = m_cinematicCamera->rbegin();
+ if (camrevitr != m_cinematicCamera->rend())
+ m_cinematicLength = camrevitr->timeStamp;
+ else
+ m_cinematicLength = 0;
+ }
+ }
+}
+
+void CinematicMgr::EndCinematic()
+{
+ if (m_activeCinematicCameraId == 0)
+ return;
+
+ m_cinematicDiff = 0;
+ m_cinematicCamera = nullptr;
+ m_activeCinematicCameraId = 0;
+ if (m_CinematicObject)
+ {
+ if (WorldObject* vpObject = player->GetViewpoint())
+ if (vpObject == m_CinematicObject)
+ player->SetViewpoint(m_CinematicObject, false);
+
+ m_CinematicObject->AddObjectToRemoveList();
+ }
+}
+
+void CinematicMgr::UpdateCinematicLocation(uint32 /*diff*/)
+{
+ if (m_activeCinematicCameraId == 0 || !m_cinematicCamera || m_cinematicCamera->size() == 0)
+ return;
+
+ Position lastPosition;
+ uint32 lastTimestamp = 0;
+ Position nextPosition;
+ uint32 nextTimestamp = 0;
+
+ // Obtain direction of travel
+ for (FlyByCamera cam : *m_cinematicCamera)
+ {
+ if (cam.timeStamp > m_cinematicDiff)
+ {
+ nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ nextTimestamp = cam.timeStamp;
+ break;
+ }
+ lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ lastTimestamp = cam.timeStamp;
+ }
+ float angle = lastPosition.GetAngle(&nextPosition);
+ angle -= lastPosition.GetOrientation();
+ if (angle < 0)
+ angle += 2 * float(M_PI);
+
+ // Look for position around 2 second ahead of us.
+ int32 workDiff = m_cinematicDiff;
+
+ // Modify result based on camera direction (Humans for example, have the camera point behind)
+ workDiff += static_cast<int32>(float(CINEMATIC_LOOKAHEAD) * cos(angle));
+
+ // Get an iterator to the last entry in the cameras, to make sure we don't go beyond the end
+ FlyByCameraCollection::const_reverse_iterator endItr = m_cinematicCamera->rbegin();
+ if (endItr != m_cinematicCamera->rend() && workDiff > static_cast<int32>(endItr->timeStamp))
+ workDiff = endItr->timeStamp;
+
+ // Never try to go back in time before the start of cinematic!
+ if (workDiff < 0)
+ workDiff = m_cinematicDiff;
+
+ // Obtain the previous and next waypoint based on timestamp
+ for (FlyByCamera cam : *m_cinematicCamera)
+ {
+ if (static_cast<int32>(cam.timeStamp) >= workDiff)
+ {
+ nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ nextTimestamp = cam.timeStamp;
+ break;
+ }
+ lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ lastTimestamp = cam.timeStamp;
+ }
+
+ // Never try to go beyond the end of the cinematic
+ if (workDiff > static_cast<int32>(nextTimestamp))
+ workDiff = static_cast<int32>(nextTimestamp);
+
+ // Interpolate the position for this moment in time (or the adjusted moment in time)
+ uint32 timeDiff = nextTimestamp - lastTimestamp;
+ uint32 interDiff = workDiff - lastTimestamp;
+ float xDiff = nextPosition.m_positionX - lastPosition.m_positionX;
+ float yDiff = nextPosition.m_positionY - lastPosition.m_positionY;
+ float zDiff = nextPosition.m_positionZ - lastPosition.m_positionZ;
+ Position interPosition(lastPosition.m_positionX + (xDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionY +
+ (yDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionZ + (zDiff * (float(interDiff) / float(timeDiff))));
+
+ // Advance (at speed) to this position. The remote sight object is used
+ // to send update information to player in cinematic
+ if (m_CinematicObject && interPosition.IsPositionValid())
+ m_CinematicObject->MonsterMoveWithSpeed(interPosition.m_positionX, interPosition.m_positionY, interPosition.m_positionZ, 500.0f, false, true);
+
+ // If we never received an end packet 10 seconds after the final timestamp then force an end
+ if (m_cinematicDiff > m_cinematicLength + 10 * IN_MILLISECONDS)
+ EndCinematic();
+}
diff --git a/src/server/game/Entities/Player/CinematicMgr.h b/src/server/game/Entities/Player/CinematicMgr.h
new file mode 100644
index 00000000000..e27369f6bd5
--- /dev/null
+++ b/src/server/game/Entities/Player/CinematicMgr.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CinematicMgr_h__
+#define CinematicMgr_h__
+
+#include "Define.h"
+#include "Object.h"
+#include "M2Stores.h"
+
+#define CINEMATIC_LOOKAHEAD (2 * IN_MILLISECONDS)
+#define CINEMATIC_UPDATEDIFF 500
+
+class Player;
+
+class TC_GAME_API CinematicMgr
+{
+ friend class Player;
+public:
+ explicit CinematicMgr(Player* playerref);
+ ~CinematicMgr();
+ // Cinematic camera data and remote sight functions
+ uint32 GetActiveCinematicCamera() const { return m_activeCinematicCameraId; }
+ void SetActiveCinematicCamera(uint32 cinematicCameraId = 0) { m_activeCinematicCameraId = cinematicCameraId; }
+ bool IsOnCinematic() const { return (m_cinematicCamera != nullptr); }
+ void BeginCinematic();
+ void EndCinematic();
+ void UpdateCinematicLocation(uint32 diff);
+
+private:
+ // Remote location information
+ Player* player;
+
+protected:
+ uint32 m_cinematicDiff;
+ uint32 m_lastCinematicCheck;
+ uint32 m_activeCinematicCameraId;
+ uint32 m_cinematicLength;
+ FlyByCameraCollection* m_cinematicCamera;
+ Position m_remoteSightPosition;
+ TempSummon* m_CinematicObject;
+};
+
+#endif // CinematicMgr_h__
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 999dfbde7b4..2e8c08a0246 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -334,6 +334,8 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this)
memset(_voidStorageItems, 0, VOID_STORAGE_MAX_SLOT * sizeof(VoidStorageItem*));
+ _cinematicMgr = new CinematicMgr(this);
+
m_achievementMgr = new PlayerAchievementMgr(this);
m_reputationMgr = new ReputationMgr(this);
@@ -371,6 +373,7 @@ Player::~Player()
delete m_runes;
delete m_achievementMgr;
delete m_reputationMgr;
+ delete _cinematicMgr;
for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
delete _voidStorageItems[i];
@@ -1042,6 +1045,14 @@ void Player::Update(uint32 p_time)
m_spellModTakingSpell = nullptr;
}
+ // Update cinematic location, if 500ms have passed and we're doing a cinematic now.
+ _cinematicMgr->m_cinematicDiff += p_time;
+ if (_cinematicMgr->m_activeCinematicCameraId != 0 && GetMSTimeDiffToNow(_cinematicMgr->m_lastCinematicCheck) > CINEMATIC_UPDATEDIFF)
+ {
+ _cinematicMgr->m_lastCinematicCheck = getMSTime();
+ _cinematicMgr->UpdateCinematicLocation(p_time);
+ }
+
//used to implement delayed far teleport
SetCanDelayTeleport(true);
Unit::Update(p_time);
@@ -5912,6 +5923,8 @@ void Player::SendCinematicStart(uint32 CinematicSequenceId) const
WorldPackets::Misc::TriggerCinematic packet;
packet.CinematicID = CinematicSequenceId;
SendDirectMessage(packet.Write());
+ if (CinematicSequencesEntry const* sequence = sCinematicSequencesStore.LookupEntry(CinematicSequenceId))
+ _cinematicMgr->SetActiveCinematicCamera(sequence->Camera[0]);
}
void Player::SendMovieStart(uint32 movieId)
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index c182e92364c..21aa89bf12b 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -32,6 +32,7 @@
#include "WorldSession.h"
#include "PlayerTaxi.h"
#include "TradeData.h"
+#include "CinematicMgr.h"
#include "SceneMgr.h"
struct CreatureTemplate;
@@ -1212,6 +1213,7 @@ private:
class TC_GAME_API Player : public Unit, public GridObject<Player>
{
friend class WorldSession;
+ friend class CinematicMgr;
friend void Item::AddToUpdateQueueOf(Player* player);
friend void Item::RemoveFromUpdateQueueOf(Player* player);
public:
@@ -1482,6 +1484,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
TradeData* GetTradeData() const { return m_trade; }
void TradeCancel(bool sendback);
+ CinematicMgr* GetCinematicMgr() const { return _cinematicMgr; }
+
void UpdateEnchantTime(uint32 time);
void UpdateSoulboundTradeItems();
void AddTradeableItem(Item* item);
@@ -2789,6 +2793,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
Item* _StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update);
Item* _LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields);
+ CinematicMgr* _cinematicMgr;
+
GuidSet m_refundableItems;
void SendRefundInfo(Item* item);
void RefundItem(Item* item);
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 036c4a01fe6..dd6e67cea27 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -720,10 +720,14 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPackets::Spells::SetActionBu
void WorldSession::HandleCompleteCinematic(WorldPackets::Misc::CompleteCinematic& /*packet*/)
{
+ // If player has sight bound to visual waypoint NPC we should remove it
+ GetPlayer()->GetCinematicMgr()->EndCinematic();
}
void WorldSession::HandleNextCinematicCamera(WorldPackets::Misc::NextCinematicCamera& /*packet*/)
{
+ // Sent by client when cinematic actually begun. So we begin the server side process
+ GetPlayer()->GetCinematicMgr()->BeginCinematic();
}
void WorldSession::HandleCompleteMovie(WorldPackets::Misc::CompleteMovie& /*packet*/)
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 4a6860d4875..b8b539ad91f 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -737,6 +737,15 @@ void Map::Update(const uint32 t_diff)
VisitNearbyCellsOf(player, grid_object_update, world_object_update);
+ // If player is using far sight, visit that object too
+ if (WorldObject* viewPoint = player->GetViewpoint())
+ {
+ if (Creature* viewCreature = viewPoint->ToCreature())
+ VisitNearbyCellsOf(viewCreature, grid_object_update, world_object_update);
+ else if (DynamicObject* viewObject = viewPoint->ToDynObject())
+ VisitNearbyCellsOf(viewObject, grid_object_update, world_object_update);
+ }
+
// Handle updates for creatures in combat with player and are more than 60 yards away
if (player->IsInCombat())
{
@@ -2835,8 +2844,8 @@ void Map::UpdateObjectsVisibilityFor(Player* player, Cell cell, CellCoord cellpa
cell.SetNoCreate();
TypeContainerVisitor<Trinity::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
TypeContainerVisitor<Trinity::VisibleNotifier, GridTypeMapContainer > grid_notifier(notifier);
- cell.Visit(cellpair, world_notifier, *this, *player, player->GetSightRange());
- cell.Visit(cellpair, grid_notifier, *this, *player, player->GetSightRange());
+ cell.Visit(cellpair, world_notifier, *this, *player->m_seer, player->GetSightRange());
+ cell.Visit(cellpair, grid_notifier, *this, *player->m_seer, player->GetSightRange());
// send data
notifier.SendToSelf();
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 2addacc2051..845565a1e50 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -80,6 +80,7 @@
#include "WorldSession.h"
#include "ChatPackets.h"
#include "WorldSocket.h"
+#include "M2Stores.h"
#include <boost/algorithm/string.hpp>
@@ -1548,6 +1549,8 @@ void World::SetInitialWorldSettings()
sDB2Manager.LoadHotfixData();
///- Close hotfix database - it is only used during DB2 loading
HotfixDatabase.Close();
+ ///- Load M2 fly by cameras
+ LoadM2Cameras(m_dataPath);
///- Load GameTables
LoadGameTables(m_dataPath);
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index d4be66d1373..a56e687757c 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -32,6 +32,7 @@ EndScriptData */
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "GossipDef.h"
+#include "M2Stores.h"
#include "Transport.h"
#include "Language.h"
#include "MapManager.h"
@@ -123,13 +124,28 @@ public:
uint32 id = atoi((char*)args);
- if (!sCinematicSequencesStore.LookupEntry(id))
+ CinematicSequencesEntry const* cineSeq = sCinematicSequencesStore.LookupEntry(id);
+ if (!cineSeq)
{
handler->PSendSysMessage(LANG_CINEMATIC_NOT_EXIST, id);
handler->SetSentErrorMessage(true);
return false;
}
+ // Dump camera locations
+ std::unordered_map<uint32, FlyByCameraCollection>::const_iterator itr = sFlyByCameraStore.find(cineSeq->Camera[0]);
+ if (itr != sFlyByCameraStore.end())
+ {
+ handler->PSendSysMessage("Waypoints for sequence %u, camera %u", id, cineSeq->Camera[0]);
+ uint32 count = 1;
+ for (FlyByCamera cam : itr->second)
+ {
+ handler->PSendSysMessage("%02u - %7ums [%f, %f, %f] Facing %f (%f degrees)", count, cam.timeStamp, cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w, cam.locations.w * (180 / M_PI));
+ count++;
+ }
+ handler->PSendSysMessage("%u waypoints dumped", itr->second.size());
+ }
+
handler->GetSession()->GetPlayer()->SendCinematicStart(id);
return true;
}
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index 4205c26c5b2..d313117b950 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -78,9 +78,21 @@ typedef struct
std::vector<map_id> map_ids;
std::vector<uint16> LiqType;
+std::set<std::string> CameraFileNames;
boost::filesystem::path input_path;
boost::filesystem::path output_path;
+struct CinematicCameraMeta
+{
+ static DB2Meta const* Instance()
+ {
+ static char const* types = "sffh";
+ static uint8 const arraySizes[4] = { 1, 3, 1, 1 };
+ static DB2Meta instance(-1, 4, 0xA7B95349, types, arraySizes);
+ return &instance;
+ }
+};
+
struct LiquidTypeMeta
{
static DB2Meta const* Instance()
@@ -110,11 +122,12 @@ enum Extract
{
EXTRACT_MAP = 1,
EXTRACT_DBC = 2,
+ EXTRACT_CAMERA = 4,
EXTRACT_GT = 8
};
// Select data for extract
-int CONF_extract = EXTRACT_MAP | EXTRACT_DBC | EXTRACT_GT;
+int CONF_extract = EXTRACT_MAP | EXTRACT_DBC | EXTRACT_CAMERA | EXTRACT_GT;
// This option allow limit minimum height to some value (Allow save some memory)
bool CONF_allow_height_limit = true;
@@ -177,7 +190,7 @@ void Usage(char const* prg)
"%s -[var] [value]\n"\
"-i set input path\n"\
"-o set output path\n"\
- "-e extract only MAP(1)/DBC(2)/gt(8) - standard: all(11)\n"\
+ "-e extract only MAP(1)/DBC(2)/Camera(4)/gt(8) - standard: all(11)\n"\
"-f height stored as int (less map size but lost some accuracy) 1 by default\n"\
"-l dbc locale\n"\
"Example: %s -f 0 -i \"c:\\games\\game\"\n", prg, prg);
@@ -190,10 +203,10 @@ void HandleArgs(int argc, char* arg[])
{
// i - input path
// o - output path
- // e - extract only MAP(1)/DBC(2) - standard both(3)
+ // e - extract only MAP(1)/DBC(2)/Camera(4)/gt(8) - standard: all(11)
// f - use float to int conversion
// h - limit minimum height
- // b - target client build
+ // l - dbc locale
if (arg[c][0] != '-')
Usage(arg[0]);
@@ -294,7 +307,7 @@ uint32 ReadBuild(int locale)
void ReadMapDBC()
{
- printf("Read Map.dbc file... ");
+ printf("Read Map.db2 file...\n");
CASC::FileHandle dbcFile = CASC::OpenFile(CascStorage, "DBFilesClient\\Map.db2", CASC_LOCALE_NONE, true);
if (!dbcFile)
@@ -350,7 +363,7 @@ void ReadMapDBC()
void ReadLiquidTypeTableDBC()
{
- printf("Read LiquidType.dbc file...");
+ printf("Read LiquidType.db2 file...\n");
CASC::FileHandle dbcFile = CASC::OpenFile(CascStorage, "DBFilesClient\\LiquidType.db2", CASC_LOCALE_NONE, true);
if (!dbcFile)
{
@@ -383,6 +396,38 @@ void ReadLiquidTypeTableDBC()
printf("Done! (" SZFMTD " LiqTypes loaded)\n", LiqType.size());
}
+bool ReadCinematicCameraDBC()
+{
+ printf("Read CinematicCamera.db2 file...\n");
+
+ CASC::FileHandle dbcFile = CASC::OpenFile(CascStorage, "DBFilesClient\\CinematicCamera.db2", CASC_LOCALE_NONE, true);
+ if (!dbcFile)
+ {
+ printf("Unable to open CinematicCamera.db2. Camera extract aborted.\n");
+ return false;
+ }
+
+ DB2FileLoader db2;
+ if (!db2.Load(dbcFile, CinematicCameraMeta::Instance()))
+ {
+ printf("Invalid CinematicCamera.db2 file format. Camera extract aborted. %s\n", HumanReadableCASCError(GetLastError()));
+ return false;
+ }
+
+ // get camera file list from DB2
+ for (size_t i = 0; i < db2.GetNumRows(); ++i)
+ {
+ std::string camFile(db2.getRecord(i).getString(0, 0));
+ size_t loc = camFile.find(".mdx");
+ if (loc != std::string::npos)
+ camFile.replace(loc, 4, ".m2");
+ CameraFileNames.insert(camFile);
+ }
+
+ printf("Done! (" SZFMTD " CinematicCameras loaded)\n", CameraFileNames.size());
+ return true;
+}
+
//
// Adt file convertor function and data
//
@@ -1192,6 +1237,38 @@ void ExtractDBFilesClient(int l)
printf("Extracted %u files\n\n", count);
}
+void ExtractCameraFiles()
+{
+ printf("Extracting camera files...\n");
+
+ if (!ReadCinematicCameraDBC())
+ return;
+
+ boost::filesystem::path outputPath = output_path / "cameras";
+
+ CreateDir(outputPath);
+
+ printf("output path %s\n", outputPath.string().c_str());
+
+ // extract M2s
+ uint32 count = 0;
+ for (std::string const& cameraFileName : CameraFileNames)
+ {
+ if (CASC::FileHandle dbcFile = CASC::OpenFile(CascStorage, cameraFileName.c_str(), CASC_LOCALE_NONE))
+ {
+ boost::filesystem::path filePath = outputPath / GetCascFilenamePart(cameraFileName.c_str());
+
+ if (!boost::filesystem::exists(filePath))
+ if (ExtractFile(dbcFile, filePath.string()))
+ ++count;
+ }
+ else
+ printf("Unable to open file %s in the archive: %s\n", cameraFileName.c_str(), HumanReadableCASCError(GetLastError()));
+ }
+
+ printf("Extracted %u camera files\n", count);
+}
+
void ExtractGameTables()
{
printf("Extracting game tables...\n");
@@ -1343,6 +1420,13 @@ int main(int argc, char * arg[])
return 0;
}
+ if (CONF_extract & EXTRACT_CAMERA)
+ {
+ OpenCascStorage(FirstLocale);
+ ExtractCameraFiles();
+ CascStorage.reset();
+ }
+
if (CONF_extract & EXTRACT_GT)
{
OpenCascStorage(FirstLocale);