diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 6f8adbfcfcf..1f4272c9d7e 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -44,6 +44,21 @@ enum BattlegroundBracketId // bracketId for lev // must be max value in PvPDificulty slot+1 #define MAX_BATTLEGROUND_BRACKETS 16 +#pragma pack(push, 1) +struct DBCPosition2D +{ + float X; + float Y; +}; + +struct DBCPosition3D +{ + float X; + float Y; + float Z; +}; +#pragma pack(pop) + enum AreaTeams { AREATEAM_NONE = 0, diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 63de6c7a581..d9a37fdb302 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -76,6 +76,7 @@ DBCStorage sChatChannelsStore(ChatChannelsEntryfmt); DBCStorage sChrClassesStore(ChrClassesEntryfmt); DBCStorage sChrRacesStore(ChrRacesEntryfmt); DBCStorage sChrPowerTypesStore(ChrClassesXPowerTypesfmt); +DBCStorage sCinematicCameraStore(CinematicCameraEntryfmt); DBCStorage sCinematicSequencesStore(CinematicSequencesEntryfmt); DBCStorage sCreatureDisplayInfoStore(CreatureDisplayInfofmt); DBCStorage sCreatureDisplayInfoExtraStore(CreatureDisplayInfoExtrafmt); @@ -388,6 +389,7 @@ void LoadDBCStores(const std::string& dataPath) for (uint32 i = 0; i < sEmotesTextSoundStore.GetNumRows(); ++i) if (EmotesTextSoundEntry const* entry = sEmotesTextSoundStore.LookupEntry(i)) sEmotesTextSoundMap[EmotesTextSoundKey(entry->EmotesTextId, entry->RaceId, entry->SexId)] = entry; + LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicCameraStore, dbcPath, "CinematicCamera.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicSequencesStore, dbcPath, "CinematicSequences.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoStore, dbcPath, "CreatureDisplayInfo.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoExtraStore, dbcPath, "CreatureDisplayInfoExtra.dbc");//15595 diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index a1f259b2cc8..e87ee9d9a06 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -109,6 +109,7 @@ extern DBCStorage sCharTitlesStore; extern DBCStorage sChrClassesStore; extern DBCStorage sChrRacesStore; extern DBCStorage sChrPowerTypesStore; +extern DBCStorage sCinematicCameraStore; extern DBCStorage sCinematicSequencesStore; extern DBCStorage sCreatureDisplayInfoStore; extern DBCStorage sCreatureDisplayInfoExtraStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index c4298fd720f..c38575cb43b 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -780,11 +780,20 @@ struct CinematicCameraEntry }; */ +struct CinematicCameraEntry +{ + uint32 ID; // 0 + char const* Model; // 1 Model filename (translate .mdx to .m2) + uint32 SoundID; // 2 Sound ID (voiceover for cinematic) + DBCPosition3D Origin; // 3-5 Position in map used for basis for M2 co-ordinates + float OriginFacing; // 4 Orientation in map used for basis for M2 co-ordinates +}; + struct CinematicSequencesEntry { uint32 Id; // 0 index //uint32 unk1; // 1 always 0 - //uint32 cinematicCamera; // 2 id in CinematicCamera.dbc + uint32 cinematicCamera; // 2 id in CinematicCamera.dbc // 3-9 always 0 }; diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index d36cfa0e3e7..52cf1520306 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -43,7 +43,8 @@ char const ChatChannelsEntryfmt[] = "nixsx"; char const ChrClassesEntryfmt[] = "nixsxxxixiiiii"; char const ChrRacesEntryfmt[] = "niixiixixxxxixsxxxxxixxx"; char const ChrClassesXPowerTypesfmt[] = "nii"; -char const CinematicSequencesEntryfmt[] = "nxxxxxxxxx"; +char const CinematicCameraEntryfmt[] = "nsiffff"; +char const CinematicSequencesEntryfmt[] = "nxixxxxxxx"; char const CreatureDisplayInfofmt[] = "nixifxxxxxxxxxxxx"; char const CreatureDisplayInfoExtrafmt[] = "diixxxxxxxxxxxxxxxxxx"; char const CreatureModelDatafmt[] = "nisxxxxxxxxxxxffxxxxxxxxxxxxxxx"; diff --git a/src/server/game/DataStores/M2Stores.cpp b/src/server/game/DataStores/M2Stores.cpp new file mode 100644 index 00000000000..8b68140c851 --- /dev/null +++ b/src/server/game/DataStores/M2Stores.cpp @@ -0,0 +1,266 @@ +/* +* Copyright (C) 2008-2016 TrinityCore +* +* 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 . +*/ + +#include "DBCStores.h" +#include "M2Structure.h" +#include "M2Stores.h" +#include "Common.h" +#include "Containers.h" +#include "Log.h" +#include "World.h" +#include +#include +#include +#include + +std::unordered_map 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 3.3.5 +bool readCamera(M2Camera const* cam, uint32 buffSize, M2Header const* header, CinematicCameraEntry const* dbcentry) +{ + char const* buffer = reinterpret_cast(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(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(buffer + targTsArray->offset_elements); + M2Array const* targArray = reinterpret_cast(buffer + cam->target_positions.values.offset_elements); + + if (targArray->offset_elements + sizeof(M2SplineKey) > buffSize) + return false; + M2SplineKey const* targPositions = reinterpret_cast 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) > 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); + } + } + + // 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(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(buffer + posTsArray->offset_elements); + M2Array const* posArray = reinterpret_cast(buffer + cam->positions.values.offset_elements); + if (posArray->offset_elements + sizeof(M2SplineKey) > buffSize) + return false; + M2SplineKey const* positions = reinterpret_cast 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) > 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); + } + } + + 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 (uint32 i = 0; i < sCinematicCameraStore.GetNumRows(); ++i) + { + if (CinematicCameraEntry const* dbcentry = sCinematicCameraStore.LookupEntry(i)) + { + std::string filenameWork = dataPath.c_str(); + filenameWork.append(dbcentry->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(fileSize) < sizeof(M2Header)) + { + 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 (MD20) + if (strcmp(fileCheck, "MD20")) + { + 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 buffer(fileSize); + m2file.seekg(0, std::ios::beg); + if (!m2file.read(buffer.data(), fileSize)) + { + m2file.close(); + continue; + } + m2file.close(); + + // Read header + M2Header const* header = reinterpret_cast(buffer.data()); + + if (header->ofsCameras + sizeof(M2Camera) > static_cast(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(buffer.data() + header->ofsCameras); + if (!readCamera(cam, fileSize, header, dbcentry)) + 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 %u cinematic waypoint sets in %u ms", (uint32)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..298deb00b26 --- /dev/null +++ b/src/server/game/DataStores/M2Stores.h @@ -0,0 +1,36 @@ +/* +* Copyright (C) 2008-2016 TrinityCore +* +* 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 . +*/ + +#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 FlyByCameraCollection; + +extern std::unordered_map sFlyByCameraStore; + +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..7dac01eb80f --- /dev/null +++ b/src/server/game/DataStores/M2Structure.h @@ -0,0 +1,137 @@ +/* +* Copyright (C) 2008-2016 TrinityCore +* +* 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 . +*/ + +#ifndef TRINITY_M2STRUCTURE_H +#define TRINITY_M2STRUCTURE_H + +#include "Common.h" +#include "Util.h" + +#include +#include + +// 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 +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 5d802bdc19c..3a9d4ab62bc 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1655,9 +1655,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; } @@ -1678,6 +1689,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(); } @@ -1687,6 +1700,9 @@ 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..73ad182ec78 --- /dev/null +++ b/src/server/game/Entities/Player/CinematicMgr.cpp @@ -0,0 +1,170 @@ +/* +* Copyright (C) 2008-2016 TrinityCore +* +* 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 . +*/ + +#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_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; + } + } +} + +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(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(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(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(nextTimestamp)) + workDiff = static_cast(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..15eec8399ec --- /dev/null +++ b/src/server/game/Entities/Player/CinematicMgr.h @@ -0,0 +1,58 @@ +/* +* Copyright (C) 2008-2016 TrinityCore +* +* 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 . +*/ + +#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 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 89b9f33569a..dcc4f66e24e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -536,6 +536,8 @@ Player::Player(WorldSession* session): Unit(true) memset(_voidStorageItems, 0, VOID_STORAGE_MAX_SLOT * sizeof(VoidStorageItem*)); memset(_CUFProfiles, 0, MAX_CUF_PROFILES * sizeof(CUFProfile*)); + _cinematicMgr = new CinematicMgr(this); + m_achievementMgr = new AchievementMgr(this); m_reputationMgr = new ReputationMgr(this); } @@ -570,6 +572,8 @@ 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]; @@ -1220,6 +1224,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); @@ -6341,6 +6353,8 @@ void Player::SendCinematicStart(uint32 CinematicSequenceId) const WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4); data << uint32(CinematicSequenceId); SendDirectMessage(&data); + if (CinematicSequencesEntry const* sequence = sCinematicSequencesStore.LookupEntry(CinematicSequenceId)) + _cinematicMgr->SetActiveCinematicCamera(sequence->cinematicCamera); } void Player::SendMovieStart(uint32 MovieId) const diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index b7644fdad96..1220f63cb96 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -30,6 +30,7 @@ #include "SpellHistory.h" #include "Unit.h" #include "TradeData.h" +#include "CinematicMgr.h" #include #include @@ -1166,6 +1167,7 @@ private: class Player : public Unit, public GridObject { friend class WorldSession; + friend class CinematicMgr; friend void Item::AddToUpdateQueueOf(Player* player); friend void Item::RemoveFromUpdateQueueOf(Player* player); public: @@ -1435,6 +1437,8 @@ class Player : public Unit, public GridObject 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); @@ -2756,6 +2760,8 @@ class Player : public Unit, public GridObject 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 071a8b5046d..a5944d5f534 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1095,12 +1095,14 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recvData) void WorldSession::HandleCompleteCinematic(WorldPacket& /*recvData*/) { - TC_LOG_DEBUG("network", "WORLD: Received CMSG_COMPLETE_CINEMATIC"); + // If player has sight bound to visual waypoint NPC we should remove it + GetPlayer()->GetCinematicMgr()->EndCinematic(); } void WorldSession::HandleNextCinematicCamera(WorldPacket& /*recvData*/) { - TC_LOG_DEBUG("network", "WORLD: Received CMSG_NEXT_CINEMATIC_CAMERA"); + // Sent by client when cinematic actually begun. So we begin the server side process + GetPlayer()->GetCinematicMgr()->BeginCinematic(); } void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index c049a369025..40d2ce2e53d 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -709,6 +709,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()) { @@ -2570,9 +2579,8 @@ void Map::UpdateObjectsVisibilityFor(Player* player, Cell cell, CellCoord cellpa cell.SetNoCreate(); TypeContainerVisitor world_notifier(notifier); TypeContainerVisitor 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 493bbaaffb0..9108e7a9330 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -65,6 +65,7 @@ #include "WaypointMovementGenerator.h" #include "WeatherMgr.h" #include "WorldSession.h" +#include "M2Stores.h" std::atomic World::m_stopEvent(false); @@ -1435,6 +1436,9 @@ void World::SetInitialWorldSettings() LoadDBCStores(m_dataPath); LoadDB2Stores(m_dataPath); + // Load M2 fly by cameras + LoadM2Cameras(m_dataPath); + std::unordered_map> mapData; for (MapEntry const* mapEntry : sMapStore) { diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 7620d81c755..5be4bde54f1 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -125,6 +125,23 @@ public: return false; } + // Dump camera locations + if (CinematicSequencesEntry const* cineSeq = sCinematicSequencesStore.LookupEntry(id)) + { + std::unordered_map::const_iterator itr = sFlyByCameraStore.find(cineSeq->cinematicCamera); + if (itr != sFlyByCameraStore.end()) + { + handler->PSendSysMessage("Waypoints for sequence %u, camera %u", id, cineSeq->cinematicCamera); + 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 bc98e289c6a..412e4bd58d6 100644 --- a/src/tools/map_extractor/System.cpp +++ b/src/tools/map_extractor/System.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -75,11 +76,12 @@ char input_path[128] = "."; enum Extract { EXTRACT_MAP = 1, - EXTRACT_DBC = 2 + EXTRACT_DBC = 2, + EXTRACT_CAMERA = 4 }; // Select data for extract -int CONF_extract = EXTRACT_MAP | EXTRACT_DBC; +int CONF_extract = EXTRACT_MAP | EXTRACT_DBC | EXTRACT_CAMERA; // This option allow limit minimum height to some value (Allow save some memory) bool CONF_allow_height_limit = true; @@ -169,7 +171,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) - standard: both(3)\n"\ + "-e extract only MAP(1)/DBC(2)/Camera(4) - standard: all(7)\n"\ "-f height stored as int (less map size but lost some accuracy) 1 by default\n"\ "-b target build (default %u)\n"\ "Example: %s -f 0 -i \"c:\\games\\game\"", prg, CONF_TargetBuild, prg); @@ -182,7 +184,7 @@ 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) - standard: all(7) // f - use float to int conversion // h - limit minimum height // b - target client build @@ -213,7 +215,7 @@ void HandleArgs(int argc, char* arg[]) if (c + 1 < argc) // all ok { CONF_extract = atoi(arg[c++ + 1]); - if (!(CONF_extract > 0 && CONF_extract < 4)) + if (!(CONF_extract > 0 && CONF_extract < 8)) Usage(arg[0]); } else @@ -1130,6 +1132,73 @@ void ExtractDB2Files(int l, bool basicLocale) printf("Extracted %u DB2 files\n\n", count); } +void ExtractCameraFiles(int locale, bool basicLocale) +{ + printf("Extracting camera files...\n"); + HANDLE dbcFile; + if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\CinematicCamera.dbc", SFILE_OPEN_PATCHED_FILE, &dbcFile)) + { + printf("Fatal error: Cannot find Map.dbc in archive!\n"); + exit(1); + } + + DBCFile camdbc(dbcFile); + + if (!camdbc.open()) + { + printf("Unable to open CinematicCamera.dbc. Camera extract aborted.\n"); + return; + } + + // get camera file list from DBC + std::vector camerafiles; + size_t cam_count = camdbc.getRecordCount(); + + for (size_t i = 0; i < cam_count; ++i) + { + std::string camFile(camdbc.getRecord(i).getString(1)); + size_t loc = camFile.find(".mdx"); + if (loc != std::string::npos) + camFile.replace(loc, 4, ".m2"); + camerafiles.push_back(std::string(camFile)); + } + SFileCloseFile(dbcFile); + + std::string path = output_path; + path += "/Cameras/"; + CreateDir(path); + if (!basicLocale) + { + path += Locales[locale]; + path += "/"; + CreateDir(path); + } + + // extract M2s + uint32 count = 0; + for (std::string thisFile : camerafiles) + { + std::string filename = path; + HANDLE dbcFile = NULL; + filename += (thisFile.c_str() + strlen("Cameras\\")); + + if (FileExists(filename.c_str())) + continue; + + if (!SFileOpenFileEx(WorldMpq, thisFile.c_str(), SFILE_OPEN_PATCHED_FILE, &dbcFile)) + { + printf("Unable to open file %s in the archive\n", thisFile.c_str()); + continue; + } + + if (ExtractFile(dbcFile, filename.c_str())) + ++count; + + SFileCloseFile(dbcFile); + } + printf("Extracted %u camera files\n", count); +} + bool LoadLocaleMPQFile(int locale) { TCHAR buff[512]; @@ -1312,6 +1381,22 @@ int main(int argc, char * arg[]) return 0; } + if (CONF_extract & EXTRACT_CAMERA) + { + printf("Using locale: %s\n", Locales[FirstLocale]); + + // Open MPQs + LoadLocaleMPQFile(FirstLocale); + LoadCommonMPQFiles(build); + + // Extract cameras + ExtractCameraFiles(FirstLocale, true); + + // Close MPQs + SFileCloseArchive(WorldMpq); + SFileCloseArchive(LocaleMpq); + } + if (CONF_extract & EXTRACT_MAP) { printf("Using locale: %s\n", Locales[FirstLocale]);