Forráskód Böngészése

Merge pull request #4588 from Laserlicht/intro_video_rework

Intro video rework
Ivan Savenko 1 éve
szülő
commit
e3d365262e

+ 7 - 3
client/CServerHandler.cpp

@@ -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);
 				});
 			}

+ 3 - 2
client/NetPacksLobbyClient.cpp

@@ -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);

+ 0 - 31
client/lobby/CBonusSelection.cpp

@@ -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;

+ 0 - 14
client/lobby/CBonusSelection.h

@@ -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
 {

+ 2 - 0
client/mainmenu/CHighScoreScreen.cpp

@@ -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)

+ 30 - 1
client/mainmenu/CMainMenu.cpp

@@ -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

+ 4 - 0
client/mainmenu/CMainMenu.h

@@ -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.

+ 1 - 7
client/media/CEmptyVideoPlayer.h

@@ -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;
 	};

+ 8 - 24
client/media/CVideoHandler.cpp

@@ -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;

+ 3 - 4
client/media/CVideoHandler.h

@@ -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;
 };
 

+ 1 - 4
client/media/IVideoPlayer.h

@@ -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;

+ 13 - 2
client/widgets/VideoWidget.cpp

@@ -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();

+ 3 - 0
client/widgets/VideoWidget.h

@@ -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);
 };

+ 47 - 0
client/windows/GUIClasses.cpp

@@ -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
+}

+ 16 - 0
client/windows/GUIClasses.h

@@ -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;
+};

+ 7 - 20
clientapp/EntryPoint.cpp

@@ -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