diff options
Diffstat (limited to 'src')
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); |