/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* 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 "Containers.h"
#include "DB2Structure.h"
#include "M2Stores.h"
#include "Map.h"
#include "MotionMaster.h"
#include "Player.h"
#include "TemporarySummon.h"
CinematicMgr::CinematicMgr(Player* playerref)
{
player = playerref;
m_cinematicDiff = 0;
m_lastCinematicCheck = 0;
m_activeCinematic = nullptr;
m_activeCinematicCameraIndex = -1;
m_cinematicLength = 0;
m_cinematicCamera = nullptr;
m_remoteSightPosition = Position(0.0f, 0.0f, 0.0f);
m_CinematicObject = nullptr;
}
CinematicMgr::~CinematicMgr()
{
if (m_cinematicCamera && m_activeCinematic)
EndCinematic();
}
void CinematicMgr::NextCinematicCamera()
{
// Sanity check for active camera set
if (!m_activeCinematic || m_activeCinematicCameraIndex >= int32(std::size(m_activeCinematic->Camera)))
return;
uint32 cinematicCameraId = m_activeCinematic->Camera[++m_activeCinematicCameraIndex];
if (!cinematicCameraId)
return;
if (std::vector const* flyByCameras = GetFlyByCameras(cinematicCameraId))
{
// Initialize diff, and set camera
m_cinematicDiff = 0;
m_cinematicCamera = flyByCameras;
if (!m_cinematicCamera->empty())
{
FlyByCamera const& firstCamera = m_cinematicCamera->front();
Position const& pos = firstCamera.locations;
if (!pos.IsPositionValid())
return;
player->GetMap()->LoadGridForActiveObject(pos.GetPositionX(), pos.GetPositionY(), player);
m_CinematicObject = player->SummonCreature(VISUAL_WAYPOINT, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 5min);
if (m_CinematicObject)
{
m_CinematicObject->setActive(true);
player->SetViewpoint(m_CinematicObject, true);
}
// Get cinematic length
m_cinematicLength = m_cinematicCamera->back().timeStamp;
}
}
}
void CinematicMgr::EndCinematic()
{
if (!m_activeCinematic)
return;
m_cinematicDiff = 0;
m_cinematicCamera = nullptr;
m_activeCinematic = nullptr;
m_activeCinematicCameraIndex = -1;
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_activeCinematic || m_activeCinematicCameraIndex == -1 || !m_cinematicCamera || m_cinematicCamera->size() == 0)
return;
Position lastPosition;
uint32 lastTimestamp = 0;
Position nextPosition;
uint32 nextTimestamp = 0;
// Obtain direction of travel
for (FlyByCamera const& cam : *m_cinematicCamera)
{
if (cam.timeStamp > m_cinematicDiff)
{
nextPosition.Relocate(cam.locations);
nextTimestamp = cam.timeStamp;
break;
}
lastPosition.Relocate(cam.locations);
lastTimestamp = cam.timeStamp;
}
float angle = lastPosition.GetAbsoluteAngle(&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
auto 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 const& cam : *m_cinematicCamera)
{
if (static_cast(cam.timeStamp) >= workDiff)
{
nextPosition.Relocate(cam.locations);
nextTimestamp = cam.timeStamp;
break;
}
lastPosition.Relocate(cam.locations);
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();
}