1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

Merge pull request #4588 from Laserlicht/intro_video_rework

Intro video rework
This commit is contained in:
Ivan Savenko 2024-09-19 16:18:31 +03:00 committed by GitHub
commit e3d365262e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 145 additions and 112 deletions

View File

@ -23,6 +23,7 @@
#include "lobby/CLobbyScreen.h"
#include "lobby/CBonusSelection.h"
#include "windows/InfoWindows.h"
#include "windows/GUIClasses.h"
#include "media/CMusicHandler.h"
#include "media/IVideoPlayer.h"
@ -661,10 +662,13 @@ void CServerHandler::endGameplay()
{
GH.curInt = CMM.get();
CMM->enable();
CMM->playMusic();
}
else
{
GH.curInt = CMainMenu::create().get();
auto mainMenu = CMainMenu::create();
GH.curInt = mainMenu.get();
mainMenu->playMusic();
}
}
@ -708,10 +712,10 @@ void CServerHandler::startCampaignScenario(HighScoreParameter param, std::shared
else
{
CMM->openCampaignScreen(ourCampaign->campaignSet);
if(!ourCampaign->getOutroVideo().empty() && CCS->videoh->open(ourCampaign->getOutroVideo(), false))
if(!ourCampaign->getOutroVideo().empty() && CCS->videoh->open(ourCampaign->getOutroVideo(), 1))
{
CCS->musich->stopMusic();
GH.windows().createAndPushWindow<CampaignRimVideo>(ourCampaign->getOutroVideo(), ourCampaign->getVideoRim().empty() ? ImagePath::builtin("INTRORIM") : ourCampaign->getVideoRim(), [campaignScoreCalculator, statistic](){
GH.windows().createAndPushWindow<VideoWindow>(ourCampaign->getOutroVideo(), ourCampaign->getVideoRim().empty() ? ImagePath::builtin("INTRORIM") : ourCampaign->getVideoRim(), false, 1, [campaignScoreCalculator, statistic](bool skipped){
GH.windows().createAndPushWindow<CHighScoreInputScreen>(true, *campaignScoreCalculator, statistic);
});
}

View File

@ -33,6 +33,7 @@
#include "widgets/TextControls.h"
#include "media/CMusicHandler.h"
#include "media/IVideoPlayer.h"
#include "windows/GUIClasses.h"
#include "../lib/CConfigHandler.h"
#include "../lib/texts/CGeneralTextHandler.h"
@ -207,10 +208,10 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState &
{
auto bonusSel = std::make_shared<CBonusSelection>();
lobby->bonusSel = bonusSel;
if(!handler.si->campState->conqueredScenarios().size() && !handler.si->campState->getIntroVideo().empty() && CCS->videoh->open(handler.si->campState->getIntroVideo(), false))
if(!handler.si->campState->conqueredScenarios().size() && !handler.si->campState->getIntroVideo().empty() && CCS->videoh->open(handler.si->campState->getIntroVideo(), 1))
{
CCS->musich->stopMusic();
GH.windows().createAndPushWindow<CampaignRimVideo>(handler.si->campState->getIntroVideo(), handler.si->campState->getVideoRim().empty() ? ImagePath::builtin("INTRORIM") : handler.si->campState->getVideoRim(), [bonusSel](){
GH.windows().createAndPushWindow<VideoWindow>(handler.si->campState->getIntroVideo(), handler.si->campState->getVideoRim().empty() ? ImagePath::builtin("INTRORIM") : handler.si->campState->getVideoRim(), false, 1, [bonusSel](bool skipped){
if(!CSH->si->campState->getMusic().empty())
CCS->musich->playMusic(CSH->si->campState->getMusic(), true, false);
GH.windows().pushWindow(bonusSel);

View File

@ -58,37 +58,6 @@
#include "../../lib/mapObjects/CGHeroInstance.h"
CampaignRimVideo::CampaignRimVideo(VideoPath video, ImagePath rim, std::function<void()> closeCb)
: CWindowObject(BORDERED), closeCb(closeCb)
{
OBJECT_CONSTRUCTION;
addUsedEvents(LCLICK | KEYBOARD);
pos = center(Rect(0, 0, 800, 600));
videoPlayer = std::make_shared<VideoWidgetOnce>(Point(80, 186), video, true, [this](){ exit(); });
setBackground(rim);
}
void CampaignRimVideo::exit()
{
close();
if(closeCb)
closeCb();
}
void CampaignRimVideo::clickPressed(const Point & cursorPosition)
{
exit();
}
void CampaignRimVideo::keyPressed(EShortcut key)
{
exit();
}
std::shared_ptr<CampaignState> CBonusSelection::getCampaign()
{
return CSH->si->campState;

View File

@ -33,20 +33,6 @@ class VideoWidgetOnce;
class CBonusSelection;
class CampaignRimVideo : public CWindowObject
{
std::shared_ptr<VideoWidgetOnce> videoPlayer;
std::function<void()> closeCb;
void exit();
public:
CampaignRimVideo(VideoPath video, ImagePath rim, std::function<void()> closeCb);
void clickPressed(const Point & cursorPosition) override;
void keyPressed(EShortcut key) override;
};
/// Campaign screen where you can choose one out of three starting bonuses
class CBonusSelection : public CWindowObject
{

View File

@ -12,6 +12,7 @@
#include "CHighScoreScreen.h"
#include "CStatisticScreen.h"
#include "CMainMenu.h"
#include "../gui/CGuiHandler.h"
#include "../gui/WindowHandler.h"
#include "../gui/Shortcut.h"
@ -170,6 +171,7 @@ void CHighScoreScreen::buttonResetClick()
void CHighScoreScreen::buttonExitClick()
{
close();
CMM->playMusic();
}
CHighScoreInputScreen::CHighScoreInputScreen(bool won, HighScoreCalculation calc, const StatisticDataSet & statistic)

View File

@ -18,6 +18,7 @@
#include "../lobby/CSelectionBase.h"
#include "../lobby/CLobbyScreen.h"
#include "../media/IMusicPlayer.h"
#include "../media/IVideoPlayer.h"
#include "../gui/CursorHandler.h"
#include "../windows/GUIClasses.h"
#include "../gui/CGuiHandler.h"
@ -117,7 +118,6 @@ void CMenuScreen::show(Canvas & to)
void CMenuScreen::activate()
{
CCS->musich->playMusic(AudioPath::builtin("Music/MainMenu"), true, true);
CIntObject::activate();
}
@ -300,6 +300,35 @@ CMainMenu::~CMainMenu()
GH.curInt = nullptr;
}
void CMainMenu::playIntroVideos()
{
auto playVideo = [](std::string video, bool rim, float scaleFactor, std::function<void(bool)> cb){
if(CCS->videoh->open(VideoPath::builtin(video), scaleFactor))
GH.windows().createAndPushWindow<VideoWindow>(VideoPath::builtin(video), rim ? ImagePath::builtin("INTRORIM") : ImagePath::builtin(""), true, scaleFactor, [cb](bool skipped){ cb(skipped); });
else
cb(true);
};
playVideo("3DOLOGO.SMK", false, 1.25, [playVideo, this](bool skipped){
if(!skipped)
playVideo("NWCLOGO.SMK", false, 2, [playVideo, this](bool skipped){
if(!skipped)
playVideo("H3INTRO.SMK", true, 1, [this](bool skipped){
playMusic();
});
else
playMusic();
});
else
playMusic();
});
}
void CMainMenu::playMusic()
{
CCS->musich->playMusic(AudioPath::builtin("Music/MainMenu"), true, true);
}
void CMainMenu::activate()
{
// check if screen was resized while main menu was inactive - e.g. in gameplay mode

View File

@ -142,6 +142,8 @@ class CMainMenu : public CIntObject, public IUpdateable, public std::enable_shar
{
std::shared_ptr<CFilledTexture> backgroundAroundMenu;
std::vector<VideoPath> videoPlayList;
CMainMenu(); //Use CMainMenu::create
public:
@ -162,6 +164,8 @@ public:
static std::shared_ptr<CPicture> createPicture(const JsonNode & config);
void playIntroVideos();
void playMusic();
};
/// Simple window to enter the server's address.

View File

@ -14,18 +14,12 @@
class CEmptyVideoPlayer final : public IVideoPlayer
{
public:
/// Plays video on top of the screen, returns only after playback is over
bool playIntroVideo(const VideoPath & name) override
{
return false;
};
void playSpellbookAnimation(const VideoPath & name, const Point & position) override
{
}
/// Load video from specified path
std::unique_ptr<IVideoInstance> open(const VideoPath & name, bool scaleToScreen) override
std::unique_ptr<IVideoInstance> open(const VideoPath & name, float scaleFactor) override
{
return nullptr;
};

View File

@ -173,18 +173,10 @@ void CVideoInstance::openVideo()
openCodec(findVideoStream());
}
void CVideoInstance::prepareOutput(bool scaleToScreenSize, bool useTextureOutput)
void CVideoInstance::prepareOutput(float scaleFactor, bool useTextureOutput)
{
//setup scaling
if(scaleToScreenSize)
{
dimensions.x = screen->w;
dimensions.y = screen->h;
}
else
{
dimensions = Point(getCodecContext()->width, getCodecContext()->height) * GH.screenHandler().getScalingFactor();
}
dimensions = Point(getCodecContext()->width * scaleFactor, getCodecContext()->height * scaleFactor) * GH.screenHandler().getScalingFactor();
// Allocate a place to put our YUV image on that screen
if (useTextureOutput)
@ -352,10 +344,7 @@ FFMpegStream::~FFMpegStream()
Point CVideoInstance::size()
{
if(!getCurrentFrame())
throw std::runtime_error("Invalid video frame!");
return Point(getCurrentFrame()->width, getCurrentFrame()->height);
return dimensions / GH.screenHandler().getScalingFactor();
}
void CVideoInstance::show(const Point & position, Canvas & canvas)
@ -575,7 +564,7 @@ std::pair<std::unique_ptr<ui8 []>, si64> CAudioInstance::extractAudio(const Vide
return dat;
}
bool CVideoPlayer::openAndPlayVideoImpl(const VideoPath & name, const Point & position, bool useOverlay, bool scale, bool stopOnKey)
bool CVideoPlayer::openAndPlayVideoImpl(const VideoPath & name, const Point & position, bool useOverlay, bool stopOnKey)
{
CVideoInstance instance;
CAudioInstance audio;
@ -587,7 +576,7 @@ bool CVideoPlayer::openAndPlayVideoImpl(const VideoPath & name, const Point & po
return true;
instance.openVideo();
instance.prepareOutput(scale, true);
instance.prepareOutput(1, true);
auto lastTimePoint = boost::chrono::steady_clock::now();
@ -633,17 +622,12 @@ bool CVideoPlayer::openAndPlayVideoImpl(const VideoPath & name, const Point & po
return true;
}
bool CVideoPlayer::playIntroVideo(const VideoPath & name)
{
return openAndPlayVideoImpl(name, Point(0, 0), true, true, true);
}
void CVideoPlayer::playSpellbookAnimation(const VideoPath & name, const Point & position)
{
openAndPlayVideoImpl(name, position * GH.screenHandler().getScalingFactor(), false, false, false);
openAndPlayVideoImpl(name, position * GH.screenHandler().getScalingFactor(), false, false);
}
std::unique_ptr<IVideoInstance> CVideoPlayer::open(const VideoPath & name, bool scaleToScreen)
std::unique_ptr<IVideoInstance> CVideoPlayer::open(const VideoPath & name, float scaleFactor)
{
auto result = std::make_unique<CVideoInstance>();
@ -651,7 +635,7 @@ std::unique_ptr<IVideoInstance> CVideoPlayer::open(const VideoPath & name, bool
return nullptr;
result->openVideo();
result->prepareOutput(scaleToScreen, false);
result->prepareOutput(scaleFactor, false);
result->loadNextFrame(); // prepare 1st frame
return result;

View File

@ -80,7 +80,7 @@ class CVideoInstance final : public IVideoInstance, public FFMpegStream
/// video playback current progress, in seconds
double frameTime = 0.0;
void prepareOutput(bool scaleToScreenSize, bool useTextureOutput);
void prepareOutput(float scaleFactor, bool useTextureOutput);
public:
~CVideoInstance();
@ -97,13 +97,12 @@ public:
class CVideoPlayer final : public IVideoPlayer
{
bool openAndPlayVideoImpl(const VideoPath & name, const Point & position, bool useOverlay, bool scale, bool stopOnKey);
bool openAndPlayVideoImpl(const VideoPath & name, const Point & position, bool useOverlay, bool stopOnKey);
void openVideoFile(CVideoInstance & state, const VideoPath & fname);
public:
bool playIntroVideo(const VideoPath & name) final;
void playSpellbookAnimation(const VideoPath & name, const Point & position) final;
std::unique_ptr<IVideoInstance> open(const VideoPath & name, bool scaleToScreen) final;
std::unique_ptr<IVideoInstance> open(const VideoPath & name, float scaleFactor) final;
std::pair<std::unique_ptr<ui8[]>, si64> getAudio(const VideoPath & videoToOpen) final;
};

View File

@ -38,14 +38,11 @@ public:
class IVideoPlayer : boost::noncopyable
{
public:
/// Plays video on top of the screen, returns only after playback is over, aborts on input event
virtual bool playIntroVideo(const VideoPath & name) = 0;
/// Plays video on top of the screen, returns only after playback is over
virtual void playSpellbookAnimation(const VideoPath & name, const Point & position) = 0;
/// Load video from specified path. Returns nullptr on failure
virtual std::unique_ptr<IVideoInstance> open(const VideoPath & name, bool scaleToScreen) = 0;
virtual std::unique_ptr<IVideoInstance> open(const VideoPath & name, float scaleFactor) = 0;
/// Extracts audio data from provided video in wav format
virtual std::pair<std::unique_ptr<ui8[]>, si64> getAudio(const VideoPath & videoToOpen) = 0;

View File

@ -17,7 +17,12 @@
#include "../render/Canvas.h"
VideoWidgetBase::VideoWidgetBase(const Point & position, const VideoPath & video, bool playAudio)
: playAudio(playAudio)
: VideoWidgetBase(position, video, playAudio, 1.0)
{
}
VideoWidgetBase::VideoWidgetBase(const Point & position, const VideoPath & video, bool playAudio, float scaleFactor)
: playAudio(playAudio), scaleFactor(scaleFactor)
{
addUsedEvents(TIME);
pos += position;
@ -28,7 +33,7 @@ VideoWidgetBase::~VideoWidgetBase() = default;
void VideoWidgetBase::playVideo(const VideoPath & fileToPlay)
{
videoInstance = CCS->videoh->open(fileToPlay, false);
videoInstance = CCS->videoh->open(fileToPlay, scaleFactor);
if (videoInstance)
{
pos.w = videoInstance->size().x;
@ -142,6 +147,12 @@ VideoWidgetOnce::VideoWidgetOnce(const Point & position, const VideoPath & video
{
}
VideoWidgetOnce::VideoWidgetOnce(const Point & position, const VideoPath & video, bool playAudio, float scaleFactor, const std::function<void()> & callback)
: VideoWidgetBase(position, video, playAudio, scaleFactor)
, callback(callback)
{
}
void VideoWidgetOnce::onPlaybackFinished()
{
callback();

View File

@ -22,6 +22,7 @@ class VideoWidgetBase : public CIntObject
std::pair<std::unique_ptr<ui8[]>, si64> audioData = {nullptr, 0};
int audioHandle = -1;
bool playAudio = false;
float scaleFactor = 1.0;
void loadAudio(const VideoPath & file);
void startAudio();
@ -29,6 +30,7 @@ class VideoWidgetBase : public CIntObject
protected:
VideoWidgetBase(const Point & position, const VideoPath & video, bool playAudio);
VideoWidgetBase(const Point & position, const VideoPath & video, bool playAudio, float scaleFactor);
virtual void onPlaybackFinished() = 0;
void playVideo(const VideoPath & video);
@ -62,4 +64,5 @@ class VideoWidgetOnce final: public VideoWidgetBase
void onPlaybackFinished() final;
public:
VideoWidgetOnce(const Point & position, const VideoPath & video, bool playAudio, const std::function<void()> & callback);
VideoWidgetOnce(const Point & position, const VideoPath & video, bool playAudio, float scaleFactor, const std::function<void()> & callback);
};

View File

@ -1608,3 +1608,50 @@ void CObjectListWindow::keyPressed(EShortcut key)
list->scrollTo(sel);
changeSelection(sel);
}
VideoWindow::VideoWindow(VideoPath video, ImagePath rim, bool showBackground, float scaleFactor, std::function<void(bool skipped)> closeCb)
: CWindowObject(BORDERED | SHADOW_DISABLED | NEEDS_ANIMATED_BACKGROUND), closeCb(closeCb)
{
OBJECT_CONSTRUCTION;
addUsedEvents(LCLICK | KEYBOARD);
if(!rim.empty())
{
videoPlayer = std::make_shared<VideoWidgetOnce>(Point(80, 186), video, true, [this](){ exit(false); });
pos = center(Rect(0, 0, 800, 600));
}
else
{
videoPlayer = std::make_shared<VideoWidgetOnce>(Point(0, 0), video, true, scaleFactor, [this](){ exit(false); });
pos = center(Rect(0, 0, videoPlayer->pos.w, videoPlayer->pos.h));
}
if(showBackground)
backgroundAroundWindow = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(-pos.x, -pos.y, GH.screenDimensions().x, GH.screenDimensions().y));
if(!rim.empty())
setBackground(rim);
}
void VideoWindow::exit(bool skipped)
{
close();
if(closeCb)
closeCb(skipped);
}
void VideoWindow::clickPressed(const Point & cursorPosition)
{
exit(true);
}
void VideoWindow::keyPressed(EShortcut key)
{
exit(true);
}
bool VideoWindow::receiveEvent(const Point & position, int eventType) const
{
return true; // capture click also outside of window
}

View File

@ -43,6 +43,7 @@ class CAnimImage;
class CFilledTexture;
class IImage;
class VideoWidget;
class VideoWidgetOnce;
enum class EUserEvent;
@ -501,3 +502,18 @@ public:
CThievesGuildWindow(const CGObjectInstance * _owner);
};
class VideoWindow : public CWindowObject
{
std::shared_ptr<VideoWidgetOnce> videoPlayer;
std::shared_ptr<CFilledTexture> backgroundAroundWindow;
std::function<void(bool)> closeCb;
void exit(bool skipped);
public:
VideoWindow(VideoPath video, ImagePath rim, bool showBackground, float scaleFactor, std::function<void(bool)> closeCb);
void clickPressed(const Point & cursorPosition) override;
void keyPressed(EShortcut key) override;
bool receiveEvent(const Point & position, int eventType) const override;
};

View File

@ -30,6 +30,7 @@
#include "../client/render/Graphics.h"
#include "../client/render/IRenderHandler.h"
#include "../client/render/IScreenHandler.h"
#include "../client/lobby/CBonusSelection.h"
#include "../client/windows/CMessage.h"
#include "../client/windows/InfoWindows.h"
@ -65,7 +66,6 @@ static std::optional<std::string> criticalInitializationError;
#ifndef VCMI_IOS
void processCommand(const std::string &message);
#endif
void playIntro();
[[noreturn]] static void quitApplication();
static void mainLoop();
@ -319,13 +319,6 @@ int main(int argc, char * argv[])
init();
#endif
if(!settings["session"]["headless"].Bool())
{
if(!vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool())
playIntro();
GH.screenHandler().clearScreen();
}
#ifndef VCMI_NO_THREADED_LOAD
#ifdef VCMI_ANDROID // android loads the data quite slowly so we display native progressbar to prevent having only black screen for few seconds
{
@ -381,6 +374,12 @@ int main(int argc, char * argv[])
{
auto mmenu = CMainMenu::create();
GH.curInt = mmenu.get();
bool playIntroVideo = !settings["session"]["headless"].Bool() && !vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool();
if(playIntroVideo)
mmenu->playIntroVideos();
else
mmenu->playMusic();
}
std::vector<std::string> names;
@ -402,18 +401,6 @@ int main(int argc, char * argv[])
return 0;
}
//plays intro, ends when intro is over or button has been pressed (handles events)
void playIntro()
{
if(!CCS->videoh->playIntroVideo(VideoPath::builtin("3DOLOGO.SMK")))
return;
if (!CCS->videoh->playIntroVideo(VideoPath::builtin("NWCLOGO.SMK")))
return;
CCS->videoh->playIntroVideo(VideoPath::builtin("H3INTRO.SMK"));
}
static void mainLoop()
{
#ifndef VCMI_UNIX