diff --git a/CMakeLists.txt b/CMakeLists.txt index a87638037..e067df0b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ set(Impacto_Src src/modelviewer.cpp src/characterviewer.cpp src/spriteanimation.cpp + src/pathanimation.cpp src/background2d.cpp src/mask2d.cpp src/character2d.cpp @@ -334,6 +335,7 @@ set(Impacto_Header src/characterviewer.h src/spritesheet.h src/spriteanimation.h + src/pathanimation.h src/font.h src/background2d.h src/mask2d.h diff --git a/profiles/chlcc/hud/titlemenu.lua b/profiles/chlcc/hud/titlemenu.lua index bd95d403f..ce65e71ff 100644 --- a/profiles/chlcc/hud/titlemenu.lua +++ b/profiles/chlcc/hud/titlemenu.lua @@ -6,8 +6,16 @@ root.TitleMenu = { PressToStartAnimDurationIn = 0.5, PressToStartAnimDurationOut = 0.5, PressToStartSprite = "TitleMenuPressToStart", - IntroBackgroundSprite = "TitleMenuIntroBackground", BackgroundSprite = "TitleMenuBackground", + IntroBackgroundSprite = "TitleMenuIntroBackground", + IntroBouncingStarSprite = "StarLogo", + IntroSmallStarSprite = "IntroSmallStar", + IntroExplodingStarAnimationDuration = 1.5, + IntroExplodingStarAnimationRotationDuration = 0.5, + IntroExplodingStarAnimationDistance = 315, + IntroPanningAnimationDuration = 2.1, + IntroAfterPanningWaitDuration = 0.8, + IntroBigStarSprite = "IntroBigStar", DelusionADVUnderSprite = "DelusionADVUnder", -- "DelusionADVUnderEnglish" with the TLed assets, "DelusionADVUnder" with the original ones DelusionADVUnderX = 78, --74 with the TLed assets, 78 with the original ones DelusionADVUnderY = 394, --396 with the TLed assets, 394 with the original ones @@ -101,7 +109,27 @@ root.TitleMenu = { MenuEntriesSprites = {}, MenuEntriesHighlightedSprites = {}, LineNum = 6, - LineEntriesSprites = {} + LineEntriesSprites = {}, + IntroStarBounceAnimationSegmentCount = 7 +}; + +root.TitleMenu.IntroHighlightSprites = { + "IntroBrightGreenHighlight", + "IntroSunHighlight", + "IntroGrayHighlight", + "IntroCrescentRainbowHighlight", + "IntroBlueHighlight", + "IntroWhiteHighlight", + "IntroBrownHighlight", + "IntroDiamondHighlight", + "IntroDarkGreenHighlight", + "IntroCircularRainbowHighlight" +}; + +-- Positions along the diagonal normalized between -1 and 1 +root.TitleMenu.IntroHighlightPositions = { + -1.13, -1.00, -0.49, 0.00, 0.17, + 0.30, 0.58, 0.69, 0.91, 1.12 }; for i = 0, 3 do @@ -241,6 +269,66 @@ root.Sprites["TitleMenuIntroBackground"] = { Bounds = { X = 0, Y = 0, Width = 1280, Height = 720 }, }; +root.Sprites["IntroSmallStar"] = { + Sheet = "Title", + Bounds = { X = 1153, Y = 534, Width = 45, Height = 44 }, +}; + +root.Sprites["IntroBigStar"] = { + Sheet = "Title", + Bounds = { X = 1156, Y = 345, Width = 178, Height = 170 }, +}; + +root.Sprites["IntroBrightGreenHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 1536, Y = 0, Width = 256, Height = 256 }, +}; + +root.Sprites["IntroSunHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 0, Y = 0, Width = 512, Height = 512 }, +}; + +root.Sprites["IntroGrayHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 1536, Y = 256, Width = 256, Height = 256 }, +}; + +root.Sprites["IntroCrescentRainbowHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 0, Y = 512, Width = 512, Height = 512 }, +}; + +root.Sprites["IntroBlueHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 1280, Y = 256, Width = 256, Height = 256 }, +}; + +root.Sprites["IntroWhiteHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 1280, Y = 0, Width = 256, Height = 256 }, +}; + +root.Sprites["IntroBrownHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 1024, Y = 256, Width = 256, Height = 256 }, +}; + +root.Sprites["IntroDiamondHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 512, Y = 512, Width = 512, Height = 512 }, +}; + +root.Sprites["IntroDarkGreenHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 1024, Y = 0, Width = 256, Height = 256 }, +}; + +root.Sprites["IntroCircularRainbowHighlight"] = { + Sheet = "Highlights", + Bounds = { X = 512, Y = 0, Width = 512, Height = 512 }, +}; + root.Sprites["TitleMenuBackground"] = { Sheet = "TitleBg2", Bounds = { X = 0, Y = 0, Width = 1280, Height = 720 }, @@ -312,3 +400,55 @@ root.Sprites["TitleMenuSecondaryItemHighlight"] = { Sheet = "Title", Bounds = { X = 915, Y = 989, Width = 285, Height = 34 }, }; + +root.TitleMenu.IntroStarBounceAnimationPath = { + { + StartPosition = { X = 1344, Y = 144 }, + EndPosition = { X = 1152, Y = 576 }, + Duration = 0.72, + EasingX = EasingFunction.Linear, + EasingY = EasingFunction.QuadraticIn, + }, + { + StartPosition = { X = 1152, Y = 576 }, + EndPosition = { X = 1050, Y = 504 }, + Duration = 0.3, + EasingX = EasingFunction.Linear, + EasingY = EasingFunction.QuadraticOut, + }, + { + StartPosition = { X = 1050, Y = 504 }, + EndPosition = { X = 960, Y = 576 }, + Duration = 0.3, + EasingX = EasingFunction.Linear, + EasingY = EasingFunction.QuadraticIn, + }, + { + StartPosition = { X = 960, Y = 576 }, + EndPosition = { X = 870, Y = 504 }, + Duration = 0.3, + EasingX = EasingFunction.Linear, + EasingY = EasingFunction.QuadraticOut, + }, + { + StartPosition = { X = 870, Y = 504 }, + EndPosition = { X = 780, Y = 576 }, + Duration = 0.3, + EasingX = EasingFunction.Linear, + EasingY = EasingFunction.QuadraticIn, + }, + { + StartPosition = { X = 780, Y = 576 }, + EndPosition = { X = 704, Y = 252 }, + Duration = 0.66, + EasingX = EasingFunction.Linear, + EasingY = EasingFunction.QuadraticOut, + }, + { + StartPosition = { X = 704, Y = 252 }, + EndPosition = { X = 640, Y = 360 }, + Duration = 0.4, + EasingX = EasingFunction.Linear, + EasingY = EasingFunction.QuadraticIn, + }, +}; diff --git a/profiles/chlcc/sprites.lua b/profiles/chlcc/sprites.lua index 6785aa523..9102b3d4c 100644 --- a/profiles/chlcc/sprites.lua +++ b/profiles/chlcc/sprites.lua @@ -5,12 +5,12 @@ root.SpriteSheets = { DesignHeight = 1024 }, ["AlbumThumbnailSheet"] = { - Path = {Mount = "system", Id = 1 }, + Path = { Mount = "system", Id = 1 }, DesignWidth = 2048, DesignHeight = 1024 }, ["AlbumThumbnailSheet2"] = { - Path = {Mount = "system", Id = 2 }, + Path = { Mount = "system", Id = 2 }, DesignWidth = 2048, DesignHeight = 1024 }, @@ -29,6 +29,11 @@ root.SpriteSheets = { DesignWidth = 2048, DesignHeight = 720 }, + ["Highlights"] = { + Path = { Mount = "system", Id = 7 }, + DesignWidth = 2048, + DesignHeight = 1024 + }, ["Menu"] = { Path = { Mount = "system", Id = 8 }, DesignWidth = 2048, @@ -101,4 +106,4 @@ root.SpriteSheets = { }, }; -root.Sprites = {}; \ No newline at end of file +root.Sprites = {}; diff --git a/src/audio/audiochannel.h b/src/audio/audiochannel.h index 5991367da..8f7ad2e07 100644 --- a/src/audio/audiochannel.h +++ b/src/audio/audiochannel.h @@ -27,7 +27,8 @@ class AudioChannel { virtual void Stop(float fadeOutDuration){}; virtual void Pause() { - if (State == ACS_Playing) State = ACS_Paused; + if (State == ACS_Playing || State == ACS_FadingIn || State == ACS_FadingOut) + State = ACS_Paused; }; virtual void Resume() { if (State == ACS_Paused) State = ACS_Playing; diff --git a/src/audio/openal/audiochannel.cpp b/src/audio/openal/audiochannel.cpp index 37ee0af86..a9a3fd98c 100644 --- a/src/audio/openal/audiochannel.cpp +++ b/src/audio/openal/audiochannel.cpp @@ -102,7 +102,7 @@ void AudioChannel::Stop(float fadeOutDuration) { } void AudioChannel::Pause() { - if (State == ACS_Playing) { + if (State == ACS_Playing || State == ACS_FadingIn || State == ACS_FadingOut) { alSourcePause(Source); State = ACS_Paused; } diff --git a/src/games/chlcc/titlemenu.cpp b/src/games/chlcc/titlemenu.cpp index 9911ebd24..441edd620 100644 --- a/src/games/chlcc/titlemenu.cpp +++ b/src/games/chlcc/titlemenu.cpp @@ -12,6 +12,8 @@ #include "../../profile/games/chlcc/titlemenu.h" #include "../../profile/scriptvars.h" #include "../../profile/game.h" +#include "../../profile/profile.h" +#include namespace Impacto { namespace UI { @@ -20,6 +22,8 @@ namespace CHLCC { using namespace Impacto::Profile::TitleMenu; using namespace Impacto::Profile::CHLCC::TitleMenu; using namespace Impacto::Profile::ScriptVars; +using namespace Impacto::Profile; +using namespace Impacto::Audio; using namespace Impacto::UI::Widgets::CHLCC; @@ -257,6 +261,11 @@ void TitleMenu::Hide() { void TitleMenu::Update(float dt) { UpdateInput(); + IntroPanningAnimation.Update(dt); + IntroAfterPanningWaitAnimation.Update(dt); + IntroStarBounceAnimation.Update(dt); + IntroExplodingStarAnimation.Update(dt); + IntroExplodingStarRotationAnimation.Update(dt); PressToStartAnimation.Update(dt); SpinningCircleAnimation.Update(dt); PrimaryFadeAnimation.Update(dt); @@ -377,7 +386,7 @@ void TitleMenu::Render() { if (ScrWork[SW_MENUCT] < 64) { switch (ScrWork[SW_TITLEDISPCT]) { case 0: { // Initial animation - Renderer->DrawSprite(IntroBackgroundSprite, glm::vec2(0.0f)); + DrawIntroAnimation(); } break; case 1: { // Press to start DrawTitleMenuBackGraphics(); @@ -443,7 +452,136 @@ void TitleMenu::Render() { } } -inline void TitleMenu::DrawTitleMenuBackGraphics() { +void TitleMenu::DrawIntroAnimation() { + switch (IntroAnimationState) { + case TitleMenuIntroAnimationState::Out: { + IntroPanningAnimation.StartIn(); + IntroAnimationState = TitleMenuIntroAnimationState::Panning; + return; + } + case TitleMenuIntroAnimationState::Panning: { + DrawIntroBackground(); + + if (IntroPanningAnimation.IsIn()) { + IntroAfterPanningWaitAnimation.StartIn(); + IntroAnimationState = TitleMenuIntroAnimationState::AfterPanningWaiting; + } + + return; + } + case TitleMenuIntroAnimationState::AfterPanningWaiting: { + DrawIntroBackground(); + + if (IntroAfterPanningWaitAnimation.IsIn()) { + IntroStarBounceAnimation.StartIn(); + IntroAnimationState = TitleMenuIntroAnimationState::BouncingStar; + } + + return; + } + case TitleMenuIntroAnimationState::BouncingStar: { + DrawIntroBackground(); + + if (IntroStarBounceAnimation.GetCurrentSegmentIndex() == 1 && + Audio::Channels[Audio::AC_SE0]->State == ACS_Paused) { + Audio::Channels[Audio::AC_SE0]->Resume(); + } + + glm::vec2 position = IntroStarBounceAnimation.GetPosition() - + IntroBouncingStarSprite.Bounds.Dimensions() / 2.0f; + + Renderer->DrawSprite(StarLogoSprite, position); + + if (IntroStarBounceAnimation.IsIn()) { + IntroAnimationState = TitleMenuIntroAnimationState::ExplodingStar; + IntroExplodingStarAnimation.StartIn(); + IntroExplodingStarRotationAnimation.StartIn(); + } + + return; + } + case TitleMenuIntroAnimationState::ExplodingStar: { + DrawIntroBackground(); + + glm::vec2 origin = IntroStarBounceAnimation.GetPosition() - + IntroBouncingStarSprite.Bounds.Dimensions() / 2.0f; + + constexpr size_t NUM_STARS = 5; + for (size_t i = 0; i < NUM_STARS; i++) { + float rayAngle = M_PI_2 - M_PI * 2 / NUM_STARS * i; + glm::vec2 directionVector(std::cos(rayAngle), -std::sin(rayAngle)); + glm::vec2 displacement = directionVector * + IntroExplodingStarAnimation.Progress * + IntroExplodingStarAnimationDistance; + glm::vec2 position = origin + displacement; + + float opacity = 1 - IntroExplodingStarAnimation.Progress; + float angle = M_PI * 2 * IntroExplodingStarRotationAnimation.Progress; + if (i >= 3) angle = -angle; + + Renderer->DrawSprite(IntroSmallStarSprite, position, + {1.0f, 1.0f, 1.0f, opacity}, {1.0f, 1.0f}, angle); + } + + if (IntroExplodingStarAnimation.IsIn()) { + IntroAnimationState = TitleMenuIntroAnimationState::FallingStars; + IntroExplodingStarRotationAnimation.State = AS_Stopped; + } + + return; + } + case TitleMenuIntroAnimationState::FallingStars: { + Renderer->DrawSprite(IntroBackgroundSprite, glm::vec2(0.0f)); + DrawIntroBackground(); + + return; + } + } +} + +void TitleMenu::DrawIntroBackground() const { + float progress = std::sin(IntroPanningAnimation.Progress * M_PI_2); + glm::vec2 designDimensions(DesignWidth, DesignHeight); + + Renderer->DrawRect({0, 0, DesignWidth, DesignHeight}, glm::vec4(1.0f)); + Renderer->DrawSprite(IntroBackgroundSprite, glm::vec2(0.0f), + {1.0f, 1.0f, 1.0f, IntroPanningAnimation.Progress}); + + glm::vec2 zoomFactor = + designDimensions / 16.0f + designDimensions / 16.0f * 7.0f * progress; + + Renderer->SetBlendMode(RendererBlendMode::Additive); + + for (size_t i = 0; i < IntroHighlightCount; i++) { + const Sprite& sprite = IntroHighlightSprites[i]; + + constexpr float scale = 1.5f; + float offset = IntroHighlightPositions[i]; + glm::vec2 position = offset * zoomFactor + zoomFactor - + sprite.Bounds.Dimensions() / 2.0f * scale; + + Renderer->DrawSprite(sprite, position, glm::vec4(1.0f), glm::vec2(scale)); + } + + Renderer->SetBlendMode(RendererBlendMode::Normal); + + Renderer->CaptureScreencap(ShaderScreencapture.BgSprite); + + // Hack because the screencap is drawn flipped along the y-axis + // so draw it again to flip it again, effectively unflipping it + Renderer->DrawSprite(ShaderScreencapture.BgSprite, glm::vec2(0.0f)); + Renderer->CaptureScreencap(ShaderScreencapture.BgSprite); + + // Cross-fade from black + Renderer->DrawRect({0.0f, 0.0f, DesignWidth, DesignHeight}, + {0.0f, 0.0f, 0.0f, 1.0f}); + + glm::vec2 scale(4 / (progress * 3 + 1)); + Renderer->DrawSprite(ShaderScreencapture.BgSprite, glm::vec2(0.0f), + {1.0f, 1.0f, 1.0f, progress}, scale); +} + +void TitleMenu::DrawTitleMenuBackGraphics() const { Renderer->DrawSprite(BackgroundSprite, glm::vec2(0.0f)); Renderer->DrawSprite(SpinningCircleSprite, glm::vec2(SpinningCircleX, SpinningCircleY), @@ -472,4 +610,4 @@ inline void TitleMenu::DrawTitleMenuBackGraphics() { } // namespace CHLCC } // namespace UI -} // namespace Impacto \ No newline at end of file +} // namespace Impacto diff --git a/src/games/chlcc/titlemenu.h b/src/games/chlcc/titlemenu.h index 9c8c10de2..21531116c 100644 --- a/src/games/chlcc/titlemenu.h +++ b/src/games/chlcc/titlemenu.h @@ -1,15 +1,20 @@ #pragma once #include "../../animation.h" +#include "../../pathanimation.h" #include "../../ui/menu.h" #include "../../ui/widgets/group.h" #include "../../ui/widgets/button.h" #include "../../ui/widgets/chlcc/titlebutton.h" +#include "../../background2d.h" namespace Impacto { namespace UI { namespace CHLCC { +BETTER_ENUM(TitleMenuIntroAnimationState, int, Out, Panning, + AfterPanningWaiting, BouncingStar, ExplodingStar, FallingStars, In) + class TitleMenu : public Menu { public: TitleMenu(); @@ -26,10 +31,18 @@ class TitleMenu : public Menu { Animation SecondaryItemsFadeInAnimation; Animation SpinningCircleAnimation; + Animation IntroPanningAnimation; + Animation IntroAfterPanningWaitAnimation; + PathAnimation IntroStarBounceAnimation; + Animation IntroExplodingStarAnimation; + Animation IntroExplodingStarRotationAnimation; + void MenuButtonOnClick(Widgets::Button* target); void SecondaryButtonOnClick(Widgets::Button* target); - void DrawTitleMenuBackGraphics(); + void DrawIntroAnimation(); + void DrawIntroBackground() const; + void DrawTitleMenuBackGraphics() const; private: Widgets::Group* MainItems; @@ -55,6 +68,9 @@ class TitleMenu : public Menu { Widgets::Group* SystemItems; Widgets::CHLCC::TitleButton* Config; Widgets::CHLCC::TitleButton* SystemSave; + + TitleMenuIntroAnimationState IntroAnimationState = + TitleMenuIntroAnimationState::Out; }; } // namespace CHLCC diff --git a/src/io/physicalfilestream.cpp b/src/io/physicalfilestream.cpp index 24500e18e..b2a2a9e9c 100644 --- a/src/io/physicalfilestream.cpp +++ b/src/io/physicalfilestream.cpp @@ -74,8 +74,9 @@ IoError PhysicalFileStream::Create(std::string const& fileName, Stream** out, if (result->ErrorCode != IoError_OK) { ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\"\n", result->SourceFileName.c_str()); + IoError error = result->ErrorCode; delete result; - return result->ErrorCode; + return error; } if (!result->FileStream) { ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", diff --git a/src/pathanimation.cpp b/src/pathanimation.cpp new file mode 100644 index 000000000..566470bb9 --- /dev/null +++ b/src/pathanimation.cpp @@ -0,0 +1,82 @@ +#include "pathanimation.h" +#include + +namespace Impacto { + +float Ease(float progress, EasingFunction function) { + switch (function) { + case EasingFunction::Linear: { + return progress; + } + case EasingFunction::QuadraticIn: { + return progress * progress; + } + case EasingFunction::QuadraticOut: { + return 1 - (1 - progress) * (1 - progress); + } + case EasingFunction::CubicIn: { + return progress * progress * progress; + } + case EasingFunction::CubicOut: { + return 1 - std::pow(1 - progress, 3); + } + } +} + +PathAnimation::PathAnimation(std::vector path) + : Path(std::move(path)) { + float totalDuration = 0; + for (const PathSegment& segment : Path) { + totalDuration += segment.Duration; + } + + this->DurationIn = totalDuration; + this->DurationOut = totalDuration; +} + +float PathAnimation::GetCurrentSegmentProgress() const { + return CurrentSegmentProgress; +} + +size_t PathAnimation::GetCurrentSegmentIndex() const { + return CurrentSegmentIndex; +} + +glm::vec2 PathAnimation::GetPosition() const { + const PathSegment& segment = Path[CurrentSegmentIndex]; + glm::vec2 direction = segment.EndPosition - segment.StartPosition; + + float progress = GetCurrentSegmentProgress(); + return segment.StartPosition + glm::vec2(Ease(progress, segment.EasingX), + Ease(progress, segment.EasingY)) * + direction; +} + +void PathAnimation::UpdateImpl(float dt) { + Animation::UpdateImpl(dt); + + float segmentEndProgress = Direction == 1 ? 0 : 1; + size_t segment = Direction == 1 ? 0 : Path.size() - 1; + size_t end = Direction == 1 ? Path.size() : -1; + + while (segment != end) { + float segmentStartProgress = segmentEndProgress; + segmentEndProgress += Path[segment].Duration / this->DurationIn * Direction; + + if ((Direction == 1 && this->Progress < segmentEndProgress) || + (Direction == -1 && this->Progress > segmentEndProgress)) { + CurrentSegmentIndex = segment; + + float segmentDuration = Path[segment].Duration; + CurrentSegmentProgress = (this->Progress - std::min(segmentStartProgress, + segmentEndProgress)) / + (segmentDuration / this->DurationIn); + + break; + } + + segment += Direction; + } +} + +} // namespace Impacto diff --git a/src/pathanimation.h b/src/pathanimation.h new file mode 100644 index 000000000..e4f546dfc --- /dev/null +++ b/src/pathanimation.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include "enum.h" +#include "animation.h" + +namespace Impacto { + +BETTER_ENUM(EasingFunction, int, Linear, QuadraticIn, QuadraticOut, CubicIn, + CubicOut) + +float Ease(float progress, EasingFunction function = EasingFunction::Linear); + +struct PathSegment { + glm::vec2 StartPosition; + glm::vec2 EndPosition; + float Duration = 0; + EasingFunction EasingX = EasingFunction::Linear; + EasingFunction EasingY = EasingX; +}; + +class PathAnimation : public Animation { + public: + PathAnimation() : PathAnimation(std::vector()){}; + PathAnimation(std::vector segments); + + std::vector Path; + + glm::vec2 GetPosition() const; + size_t GetCurrentSegmentIndex() const; + float GetCurrentSegmentProgress() const; + + protected: + void UpdateImpl(float dt) override; + + private: + size_t CurrentSegmentIndex = 0; + float CurrentSegmentProgress = 0; +}; + +} // namespace Impacto \ No newline at end of file diff --git a/src/profile/games/chlcc/titlemenu.cpp b/src/profile/games/chlcc/titlemenu.cpp index 757716a29..82187794a 100644 --- a/src/profile/games/chlcc/titlemenu.cpp +++ b/src/profile/games/chlcc/titlemenu.cpp @@ -1,6 +1,5 @@ #include "titlemenu.h" #include "../../../log.h" -//#include "../../../window.h" #include "../../../renderer/renderer.h" #include "../../profile_internal.h" @@ -16,6 +15,17 @@ namespace TitleMenu { void Configure() { IntroBackgroundSprite = EnsureGetMemberSprite("IntroBackgroundSprite"); + IntroPanningAnimationDuration = + EnsureGetMemberFloat("IntroPanningAnimationDuration"); + IntroBouncingStarSprite = EnsureGetMemberSprite("IntroBouncingStarSprite"); + IntroSmallStarSprite = EnsureGetMemberSprite("IntroSmallStarSprite"); + IntroBigStarSprite = EnsureGetMemberSprite("IntroBigStarSprite"); + IntroExplodingStarAnimationDuration = + EnsureGetMemberFloat("IntroExplodingStarAnimationDuration"); + IntroExplodingStarAnimationRotationDuration = + EnsureGetMemberFloat("IntroExplodingStarAnimationRotationDuration"); + IntroExplodingStarAnimationDistance = + EnsureGetMemberFloat("IntroExplodingStarAnimationDistance"); BackgroundSprite = EnsureGetMemberSprite("BackgroundSprite"); DelusionADVUnderSprite = EnsureGetMemberSprite("DelusionADVUnderSprite"); DelusionADVUnderX = EnsureGetMemberFloat("DelusionADVUnderX"); @@ -111,6 +121,20 @@ void Configure() { EnsureGetMemberFloat("SecondaryMenuSystemConfigY"); SecondaryMenuSystemSaveY = EnsureGetMemberFloat("SecondaryMenuSystemSaveY"); + IntroStarBounceAnimationSegmentCount = + EnsureGetMemberInt("IntroStarBounceAnimationSegmentCount"); + IntroStarBounceAnimationPath = + new PathSegment[IntroStarBounceAnimationSegmentCount]; + GetMemberPathSegmentArray(IntroStarBounceAnimationPath, + IntroStarBounceAnimationSegmentCount, + "IntroStarBounceAnimationPath"); + + GetMemberSpriteArray(IntroHighlightSprites, IntroHighlightCount, + "IntroHighlightSprites"); + + GetMemberFloatArray(IntroHighlightPositions, IntroHighlightCount, + "IntroHighlightPositions"); + UI::CHLCC::TitleMenu* menu = new UI::CHLCC::TitleMenu(); menu->PressToStartAnimation.DurationIn = Profile::TitleMenu::PressToStartAnimDurationIn; @@ -129,6 +153,28 @@ void Configure() { menu->SpinningCircleAnimation.DurationIn = SpinningCircleAnimationDuration; menu->SpinningCircleAnimation.DurationOut = SpinningCircleAnimationDuration; + menu->IntroPanningAnimation.DurationIn = IntroPanningAnimationDuration; + menu->IntroPanningAnimation.DurationOut = IntroPanningAnimationDuration; + menu->IntroAfterPanningWaitAnimation.DurationIn = + IntroAfterPanningWaitDuration; + menu->IntroAfterPanningWaitAnimation.DurationOut = + IntroAfterPanningWaitDuration; + + menu->IntroStarBounceAnimation = PathAnimation(std::vector( + IntroStarBounceAnimationPath, + IntroStarBounceAnimationPath + IntroStarBounceAnimationSegmentCount)); + + menu->IntroExplodingStarRotationAnimation.LoopMode = ALM_Loop; + menu->IntroExplodingStarRotationAnimation.DurationIn = + IntroExplodingStarAnimationRotationDuration; + menu->IntroExplodingStarRotationAnimation.DurationOut = + IntroExplodingStarAnimationRotationDuration; + + menu->IntroExplodingStarAnimation.DurationIn = + IntroExplodingStarAnimationDuration; + menu->IntroExplodingStarAnimation.DurationOut = + IntroExplodingStarAnimationDuration; + UI::TitleMenuPtr = menu; auto drawType = Game::DrawComponentType::_from_integral_unchecked( diff --git a/src/profile/games/chlcc/titlemenu.h b/src/profile/games/chlcc/titlemenu.h index dc8a08ff8..aec0b2360 100644 --- a/src/profile/games/chlcc/titlemenu.h +++ b/src/profile/games/chlcc/titlemenu.h @@ -2,6 +2,7 @@ #include "../../../spritesheet.h" #include "../../../games/chlcc/titlemenu.h" +#include "../../../pathanimation.h" namespace Impacto { namespace Profile { @@ -10,71 +11,85 @@ namespace TitleMenu { void Configure(); +inline int LineNum; int constexpr LineEntriesNumMax = 32; +inline Sprite LineSprites[LineEntriesNumMax]; -inline Sprite IntroBackgroundSprite; inline Sprite BackgroundSprite; + inline Sprite DelusionADVUnderSprite; inline Sprite DelusionADVSprite; -inline Sprite SeiraUnderSprite; -inline Sprite SeiraSprite; -inline Sprite CHLogoSprite; -inline Sprite LCCLogoUnderSprite; -inline Sprite ChuLeftLogoSprite; -inline Sprite ChuRightLogoSprite; -inline Sprite LoveLogoSprite; -inline Sprite StarLogoSprite; -inline Sprite ExclMarkLogoSprite; -inline Sprite CopyrightTextSprite; -inline Sprite SpinningCircleSprite; -inline Sprite ItemHighlightSprite; -inline Sprite LineSprites[LineEntriesNumMax]; -inline Sprite ItemLoadQuickSprite; -inline Sprite ItemLoadSprite; -inline Sprite ItemLoadQuickHighlightedSprite; -inline Sprite ItemLoadHighlightedSprite; -inline Sprite SecondaryItemHighlightSprite; - inline float DelusionADVUnderX; inline float DelusionADVUnderY; inline float DelusionADVX; inline float DelusionADVY; + +inline Sprite SeiraUnderSprite; +inline Sprite SeiraSprite; inline float SeiraUnderX; inline float SeiraUnderY; inline float SeiraX; inline float SeiraY; + +inline Sprite CHLogoSprite; inline float CHLogoX; inline float CHLogoY; + +inline Sprite LCCLogoUnderSprite; inline float LCCLogoUnderX; inline float LCCLogoUnderY; + +inline Sprite ChuLeftLogoSprite; inline float ChuLeftLogoX; inline float ChuLeftLogoY; + +inline Sprite ChuRightLogoSprite; inline float ChuRightLogoX; inline float ChuRightLogoY; + +inline Sprite LoveLogoSprite; inline float LoveLogoX; inline float LoveLogoY; + +inline Sprite StarLogoSprite; inline float StarLogoX; inline float StarLogoY; + +inline Sprite ExclMarkLogoSprite; inline float ExclMarkLogoX; inline float ExclMarkLogoY; + +inline Sprite CopyrightTextSprite; inline float CopyrightTextX; inline float CopyrightTextY; + +inline Sprite SpinningCircleSprite; inline float SpinningCircleX; inline float SpinningCircleY; inline float SpinningCircleAnimationDuration; + +inline Sprite ItemHighlightSprite; +inline Sprite ItemLoadHighlightedSprite; +inline Sprite SecondaryItemHighlightSprite; inline float ItemHighlightOffsetX; inline float ItemHighlightOffsetY; inline float ItemPadding; inline float ItemYBase; inline float ItemFadeInDuration; inline float ItemFadeOutDuration; -inline float SecondaryItemFadeInDuration; -inline float SecondaryItemFadeOutDuration; + inline float PrimaryFadeInDuration; inline float PrimaryFadeOutDuration; + inline float SecondaryFadeInDuration; inline float SecondaryFadeOutDuration; inline float SecondaryItemX; +inline float SecondaryItemFadeInDuration; +inline float SecondaryItemFadeOutDuration; + +inline Sprite ItemLoadQuickSprite; +inline Sprite ItemLoadSprite; +inline Sprite ItemLoadQuickHighlightedSprite; inline float ItemLoadY; inline float ItemLoadQuickY; inline float ItemClearListY; @@ -85,6 +100,7 @@ inline float ItemTipsY; inline float ItemTrophyY; inline float ItemConfigY; inline float ItemSystemSaveY; + inline float SecondaryItemHighlightX; inline float SecondaryMenuPaddingY; inline float SecondaryMenuLoadOffsetY; @@ -100,7 +116,24 @@ inline float SecondaryMenuExtraTrophyY; inline float SecondaryMenuSystemConfigY; inline float SecondaryMenuSystemSaveY; -inline int LineNum; +inline Sprite IntroBackgroundSprite; + +constexpr size_t IntroHighlightCount = 10; +inline Sprite IntroHighlightSprites[IntroHighlightCount]; +inline float IntroHighlightPositions[IntroHighlightCount]; +inline float IntroPanningAnimationDuration; +inline float IntroAfterPanningWaitDuration; + +inline Sprite IntroSmallStarSprite; +inline Sprite IntroBigStarSprite; + +inline PathSegment* IntroStarBounceAnimationPath; +inline int IntroStarBounceAnimationSegmentCount; +inline Sprite IntroBouncingStarSprite; + +inline float IntroExplodingStarAnimationDuration; +inline float IntroExplodingStarAnimationRotationDuration; +inline float IntroExplodingStarAnimationDistance; } // namespace TitleMenu } // namespace CHLCC diff --git a/src/profile/profile.cpp b/src/profile/profile.cpp index de88e0a90..51c544f6a 100644 --- a/src/profile/profile.cpp +++ b/src/profile/profile.cpp @@ -215,6 +215,7 @@ void MakeLuaProfile(std::string const& name) { DefineEnumInt(LuaState); DefineEnumInt(LuaState); DefineEnumInt(LuaState); + DefineEnumInt(LuaState); ImpLog(LL_Info, LC_Profile, "Starting profile %s\n", name.c_str()); diff --git a/src/profile/profile_internal.cpp b/src/profile/profile_internal.cpp index 89f50d917..dfd1c9f95 100644 --- a/src/profile/profile_internal.cpp +++ b/src/profile/profile_internal.cpp @@ -273,6 +273,29 @@ bool TryGetAssetPath(Io::AssetPath& outPath) { LUA_GET_METHODS(AssetPath, Io::AssetPath, "AssetPath") +bool TryGetPathSegment(PathSegment& segment) { + if (!lua_istable(LuaState, -1)) return false; + + int easing; + if (TryGetMemberInt("EasingX", easing)) { + segment.EasingX = EasingFunction::_from_integral_unchecked(easing); + } else { + segment.EasingX = EasingFunction::Linear; + } + + if (TryGetMemberInt("EasingY", easing)) { + segment.EasingY = EasingFunction::_from_integral_unchecked(easing); + } else { + segment.EasingY = segment.EasingX; + } + + return TryGetMemberFloat("Duration", segment.Duration) && + TryGetMemberVec2("StartPosition", segment.StartPosition) && + TryGetMemberVec2("EndPosition", segment.EndPosition); +} + +LUA_GET_METHODS(PathSegment, PathSegment, "PathSegment") + TRY_GET_ENTITY(Sprite, Sprite, Sprites) LUA_GET_METHODS(Sprite, Sprite, "Sprite") diff --git a/src/profile/profile_internal.h b/src/profile/profile_internal.h index e5a1f5ad3..a62cd6645 100644 --- a/src/profile/profile_internal.h +++ b/src/profile/profile_internal.h @@ -10,6 +10,7 @@ #include "../spritesheet.h" #include "../font.h" #include "../spriteanimation.h" +#include "../pathanimation.h" namespace Impacto { namespace Profile { @@ -54,6 +55,7 @@ LUA_GET_METHODS(Sprite, Sprite) LUA_GET_METHODS(SpriteSheet, SpriteSheet) LUA_GET_METHODS(Font, Font*) LUA_GET_METHODS(Animation, SpriteAnimationDef) +LUA_GET_METHODS(PathSegment, PathSegment) uint32_t EnsureGetKeyUint(); int32_t EnsureGetKeyInt(); diff --git a/src/renderer/dx9/renderer.h b/src/renderer/dx9/renderer.h index 27f63524c..6f722ff6c 100644 --- a/src/renderer/dx9/renderer.h +++ b/src/renderer/dx9/renderer.h @@ -104,6 +104,8 @@ class Renderer : public BaseRenderer { void SetScissorRect(RectF const& rect) override; void DisableScissor() override; + void SetBlendMode(RendererBlendMode blendMode) override {} + private: void EnsureSpaceAvailable(int vertices, int vertexSize, int indices); void EnsureTextureBound(uint32_t texture); diff --git a/src/renderer/opengl/renderer.cpp b/src/renderer/opengl/renderer.cpp index f48a1e7a2..d9150f01d 100644 --- a/src/renderer/opengl/renderer.cpp +++ b/src/renderer/opengl/renderer.cpp @@ -127,6 +127,9 @@ void Renderer::Init() { glSamplerParameteri(Sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(Sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glSamplerParameteri(Sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16); + + glEnable(GL_BLEND); + SetBlendMode(RendererBlendMode::Normal); } void Renderer::Shutdown() { @@ -1052,5 +1055,18 @@ void Renderer::DisableScissor() { glDisable(GL_SCISSOR_TEST); } +void Renderer::SetBlendMode(RendererBlendMode blendMode) { + Flush(); + + switch (blendMode) { + case RendererBlendMode::Normal: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + return; + case RendererBlendMode::Additive: + glBlendFunc(GL_ONE, GL_ONE); + return; + } +} + } // namespace OpenGL } // namespace Impacto diff --git a/src/renderer/opengl/renderer.h b/src/renderer/opengl/renderer.h index b0380f48b..679954824 100644 --- a/src/renderer/opengl/renderer.h +++ b/src/renderer/opengl/renderer.h @@ -112,6 +112,8 @@ class Renderer : public BaseRenderer { void SetScissorRect(RectF const& rect) override; void DisableScissor() override; + void SetBlendMode(RendererBlendMode blendMode) override; + private: void EnsureSpaceAvailable(int vertices, int vertexSize, int indices); void EnsureTextureBound(GLuint texture); diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h index 2e6d6f3c2..1b1a3eaf4 100644 --- a/src/renderer/renderer.h +++ b/src/renderer/renderer.h @@ -7,6 +7,7 @@ #include "../text.h" #include "yuvframe.h" #include "../../vendor/span/span.hpp" +#include "enum.h" namespace Impacto { @@ -15,6 +16,8 @@ inline GraphicsApi ActualGraphicsApi; enum class RendererOutlineMode { RO_None, RO_BottomRight, RO_Full }; +BETTER_ENUM(RendererBlendMode, int, Normal, Additive) + class BaseRenderer { public: virtual void Init() = 0; @@ -145,6 +148,8 @@ class BaseRenderer { virtual void SetScissorRect(RectF const& rect) = 0; virtual void DisableScissor() = 0; + virtual void SetBlendMode(RendererBlendMode blendMode) = 0; + bool IsInit = false; IScene3D* Scene = 0; diff --git a/src/renderer/vulkan/renderer.h b/src/renderer/vulkan/renderer.h index ec2fd64f4..236f82119 100644 --- a/src/renderer/vulkan/renderer.h +++ b/src/renderer/vulkan/renderer.h @@ -172,6 +172,8 @@ class Renderer : public BaseRenderer { void SetScissorRect(RectF const& rect) override; void DisableScissor() override; + void SetBlendMode(RendererBlendMode blendMode) override{}; + private: void CreateInstance(); void SetupDebug(); diff --git a/src/util.h b/src/util.h index 96ecbeeb8..b2c1835ca 100644 --- a/src/util.h +++ b/src/util.h @@ -52,6 +52,10 @@ struct RectF { constexpr glm::vec2 Center() const { return glm::vec2(X + Width / 2.0f, Y + Height / 2.0f); } + + constexpr glm::vec2 Position() const { return glm::vec2(X, Y); } + constexpr glm::vec2 Dimensions() const { return glm::vec2(Width, Height); } + constexpr bool ContainsPoint(glm::vec2 point, float angle = 0.0f) const { point -= Center(); if (angle != 0.0f) { diff --git a/src/vm/inst_sound.cpp b/src/vm/inst_sound.cpp index 5526920dc..02d073242 100644 --- a/src/vm/inst_sound.cpp +++ b/src/vm/inst_sound.cpp @@ -44,18 +44,24 @@ VmInstruction(InstSEplay) { StartInstruction; PopUint8(channel); PopUint8(type); + if (type != 2) { PopExpression(effect); PopExpression(loop); ScrWork[SW_SEREQNO + channel] = effect; Audio::Channels[Audio::AC_SE0 + channel]->Volume = (ScrWork[SW_SEVOL + channel] / 100.0f) * 0.3f; + + // Hack to prevent playing the first sound byte before pausing + float fade = type == 1 ? 0.1f : 0.0f; Audio::Channels[Audio::AC_SE0 + channel]->Play("se", effect, (bool)loop, - 0.0f); + fade); + if (type == 1) { + Audio::Channels[Audio::AC_SE0 + channel]->Pause(); + } } else { ImpLogSlow(LL_Warning, LC_VMStub, - "STUB instruction SEplay(channel: %i, type: %i)\n", channel, - type); + "STUB instruction SEplay(channel: %i, type: 2)\n", channel); } } VmInstruction(InstSEplayMO6) {