From 7fa5227f4233cd76dd0b5659214cc238d4bb37a2 Mon Sep 17 00:00:00 2001
From: falsycat <me@falsy.cat>
Date: Sat, 28 Aug 2021 13:27:42 +0900
Subject: [PATCH] Adds LoadScene.

---
 GlyphsJuke.vcxproj         |  2 ++
 GlyphsJuke.vcxproj.filters |  6 +++++
 src/ElementStore.h         | 12 +++------
 src/GlyphElementFactory.h  |  2 +-
 src/LoadScene.cc           | 26 ++++++++++++++++++++
 src/LoadScene.h            | 50 ++++++++++++++++++++++++++++++++++++++
 src/Lua.h                  | 15 +++++++++---
 src/PlayScene.cc           | 26 ++++++++++++--------
 src/PlayScene.h            |  8 +++++-
 src/TitleScene.cc          | 33 ++++++++++++++-----------
 src/TitleScene.h           |  6 ++---
 11 files changed, 146 insertions(+), 40 deletions(-)
 create mode 100644 src/LoadScene.cc
 create mode 100644 src/LoadScene.h

diff --git a/GlyphsJuke.vcxproj b/GlyphsJuke.vcxproj
index f347d0e..c3ed4b3 100644
--- a/GlyphsJuke.vcxproj
+++ b/GlyphsJuke.vcxproj
@@ -150,6 +150,7 @@
     <ClCompile Include="src\Font.cc" />
     <ClCompile Include="src\Game.cc" />
     <ClCompile Include="src\HiraganaMatcher.cc" />
+    <ClCompile Include="src\LoadScene.cc" />
     <ClCompile Include="src\Lua.cc" />
     <ClCompile Include="src\main.cc" />
     <ClCompile Include="src\PlayScene.cc" />
@@ -177,6 +178,7 @@
     <ClInclude Include="src\InputWindowElementFactory.h" />
     <ClInclude Include="src\iScene.h" />
     <ClInclude Include="src\iWritable.h" />
+    <ClInclude Include="src\LoadScene.h" />
     <ClInclude Include="src\Logger.h" />
     <ClInclude Include="src\Lua.h" />
     <ClInclude Include="src\Music.h" />
diff --git a/GlyphsJuke.vcxproj.filters b/GlyphsJuke.vcxproj.filters
index 4afac60..31ffc11 100644
--- a/GlyphsJuke.vcxproj.filters
+++ b/GlyphsJuke.vcxproj.filters
@@ -51,6 +51,9 @@
     <ClCompile Include="src\TitleScene.cc">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\LoadScene.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\iConsole.h">
@@ -215,6 +218,9 @@
     <ClInclude Include="src\Music.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="src\LoadScene.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Library Include="thirdparty\lua5.1.lib" />
diff --git a/src/ElementStore.h b/src/ElementStore.h
index 70eac24..5996912 100644
--- a/src/ElementStore.h
+++ b/src/ElementStore.h
@@ -15,13 +15,13 @@ class ElementStore {
  public:
   ElementStore() = delete;
 
-  ElementStore(ElementStore&&) = delete;
+  ElementStore(ElementStore&&) = default;
   ElementStore(const ElementStore&) = delete;
 
-  ElementStore& operator=(ElementStore&&) = delete;
+  ElementStore& operator=(ElementStore&&) = default;
   ElementStore& operator=(const ElementStore&) = delete;
 
-  ElementStore(iClock* clock, size_t reserve) : clock_(clock) {
+  ElementStore(size_t reserve) {
     pending_.reserve(reserve);
     performing_.reserve(reserve);
   }
@@ -35,9 +35,7 @@ class ElementStore {
     pending_.insert(insert_pos, std::move(e));
   }
 
-  void Update(Frame& frame) {
-    const uint64_t now = clock_->now();
-
+  void Update(Frame& frame, uint64_t now) {
     auto pending_beg = pending_.begin();
     auto pending_end = pending_.end();
     auto pending_itr = pending_beg;
@@ -83,8 +81,6 @@ class ElementStore {
   }
 
  private:
-  const iClock* clock_;
-
   std::vector<UniqPtr<iElement>> pending_;
   std::vector<UniqPtr<iElement>> performing_;
 };
diff --git a/src/GlyphElementFactory.h b/src/GlyphElementFactory.h
index 1b07633..4abf8f3 100644
--- a/src/GlyphElementFactory.h
+++ b/src/GlyphElementFactory.h
@@ -27,7 +27,7 @@ class GlyphElementFactory : public iElementFactory {
 
     const std::string text = std::get<std::string>(param.custom[0]);
     const std::string name = std::get<std::string>(param.custom[1]);
-    const intmax_t    size = std::get<double>(param.custom[2]);
+    const uint32_t    size = static_cast<uint32_t>(std::get<double>(param.custom[2]));
 
     auto& font = FindOrCreateFont(name);
     auto  tex  = std::move(font.RenderGlyphs(ConvertStrToWstr(text), size));  /* TODO */
diff --git a/src/LoadScene.cc b/src/LoadScene.cc
new file mode 100644
index 0000000..d9b9be1
--- /dev/null
+++ b/src/LoadScene.cc
@@ -0,0 +1,26 @@
+#include "LoadScene.h"
+
+#include "common.h"
+
+
+gj::LoadScene::LoadScene(Param&& p) : 
+    alloc_(p.super.alloc), clock_(p.super.clock), loading_(L"Loading...") {
+  prod_   = p.super.alloc->MakeUniq<PlayScene>(p.super, p.title, p.path);
+  orphan_ = std::move(p.orphan);
+}
+
+gj::UniqPtr<gj::iScene> gj::LoadScene::Update(Frame& frame) {
+  if (prod_->HasPrepared() && !(orphan_ && orphan_->IsBusy())) {
+    prod_->Start();
+    return UniqPtr<iScene>(prod_.release(), iAllocator::Deleter<iScene>(alloc_));
+  }
+
+  const uint64_t now = clock_->now();
+
+  if (XorShift(now+1)%10) {
+    loading_.SetPosition((frame.w - loading_.width())/2, frame.h/2);
+    frame.Add(&loading_);
+  } 
+
+  return nullptr;
+}
\ No newline at end of file
diff --git a/src/LoadScene.h b/src/LoadScene.h
new file mode 100644
index 0000000..c8b1d70
--- /dev/null
+++ b/src/LoadScene.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <string>
+
+#include "iAllocator.h"
+#include "iClock.h"
+#include "iScene.h"
+#include "Music.h"
+#include "PlayScene.h"
+#include "Text.h"
+
+namespace gj {
+
+
+class LoadScene : public iScene {
+ public:
+  struct Param {
+    iScene::Param super;
+
+    std::string title;
+    std::string path;
+    
+  /*  'orphan' is an instance of Music that couldn't be deleted
+   * at previous scene because it was busy. */
+    UniqPtr<Music> orphan;
+  };
+
+  LoadScene() = delete;
+  LoadScene(LoadScene&&) = delete;
+  LoadScene(const LoadScene&) = delete;
+
+  LoadScene& operator=(LoadScene&&) = delete;
+  LoadScene& operator=(const LoadScene&) = delete;
+
+  LoadScene(Param&& p);
+
+  UniqPtr<iScene> Update(Frame& frame) override;
+
+ private:
+  iAllocator* alloc_;
+  const iClock* clock_;
+
+  UniqPtr<PlayScene> prod_;
+  UniqPtr<Music>     orphan_;
+
+  Text loading_;
+};
+
+
+}
\ No newline at end of file
diff --git a/src/Lua.h b/src/Lua.h
index db2af2e..d0f9259 100644
--- a/src/Lua.h
+++ b/src/Lua.h
@@ -19,16 +19,25 @@ class Lua {
   using FactoryMap = std::map<std::string, iElementFactory*>;
 
   Lua() = delete;
-  Lua(Lua&&) = delete;
   Lua(const Lua&) = delete;
 
-  Lua& operator=(Lua&&) = delete;
   Lua& operator=(const Lua&) = delete;
+  
+  Lua(Lua&& src) noexcept : L(src.L) {
+    src.L = nullptr;
+  }
+  Lua& operator=(Lua&& src) noexcept {
+    if (&src != this) {
+      L = src.L;
+      src.L = nullptr;
+    }
+    return *this;
+  }
 
   Lua(iAllocator* alloc, ElementStore* store, const FactoryMap& factory, const std::string& path);
 
   ~Lua() {
-    lua_close(L);
+    if (L) lua_close(L);
   }
 
  private:
diff --git a/src/PlayScene.cc b/src/PlayScene.cc
index dd927aa..ddd04c0 100644
--- a/src/PlayScene.cc
+++ b/src/PlayScene.cc
@@ -7,8 +7,17 @@
 #include "ResultScene.h"
 
 
+gj::UniqPtr<gj::iScene> gj::PlayScene::Update(Frame& f) {
+  if (store_.IsEmpty()) {
+    return param_.alloc->MakeUniq<iScene, ResultScene>(param_, sb_);
+  }
+
+  store_.Update(f, clock_.now());
+  return nullptr;
+}
+
 gj::PlayScene::PlayScene(const Param& p, const std::string& title, const std::string& path) :
-    param_(p), clock_(p.clock), store_(&clock_, 256) {
+    param_(p), clock_(p.clock), store_(256) {
 
   GlyphElementFactory       glyph(p.alloc);
   InputWindowElementFactory inputWin(p.alloc, &sb_);
@@ -21,16 +30,13 @@ gj::PlayScene::PlayScene(const Param& p, const std::string& title, const std::st
     { "InputWin", &inputWin },
     { "Music",    &music },
   };
-  lua_ = p.alloc->MakeUniq<Lua>(
-    p.alloc, &store_, map, path);
+  lua_ = p.alloc->MakeUniq<Lua>(p.alloc, &store_, map, path);
 }
 
+void gj::PlayScene::Start() {
+  clock_ = OffsetClock(param_.clock);
+}
 
-gj::UniqPtr<gj::iScene> gj::PlayScene::Update(Frame& f) {
-  if (store_.IsEmpty()) {
-    return param_.alloc->MakeUniq<iScene, ResultScene>(param_, sb_);
-  }
-
-  store_.Update(f);
-  return nullptr;
+bool gj::PlayScene::HasPrepared() const {
+  return store_.CountPreparings() == 0;
 }
\ No newline at end of file
diff --git a/src/PlayScene.h b/src/PlayScene.h
index 9332a9f..e31c956 100644
--- a/src/PlayScene.h
+++ b/src/PlayScene.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <string>
 
 #include "ElementStore.h"
 #include "iScene.h"
@@ -13,6 +14,8 @@ namespace gj {
 
 class PlayScene : public iScene {
  public:
+  friend class LoadScene;
+
   PlayScene() = delete;
   PlayScene(PlayScene&&) = delete;
   PlayScene(const PlayScene&) = delete;
@@ -21,10 +24,13 @@ class PlayScene : public iScene {
   PlayScene& operator=(const PlayScene&) = delete;
 
   PlayScene(const Param& p, const std::string& title, const std::string& path);
-
+  
   UniqPtr<iScene> Update(Frame& f) override;
 
  private:
+  void Start();
+  bool HasPrepared() const;
+
   Param param_;
 
   OffsetClock clock_;
diff --git a/src/TitleScene.cc b/src/TitleScene.cc
index 45a6f4b..4727f86 100644
--- a/src/TitleScene.cc
+++ b/src/TitleScene.cc
@@ -10,12 +10,12 @@
 
 #include "common.h"
 #include "Font.h"
-#include "PlayScene.h"
+#include "LoadScene.h"
 
 
 gj::TitleScene::TitleScene(const Param& p) :
     param_(p),
-    score_(L"penguin: you didn't see anything..."),
+    title_(L"penguin: you didn't see anything..."),
     next_(L"> L"), prev_(L"H <"),
     guide_(L"H:PREV / SPACE:PLAY / L:NEXT"),
     logo_(Colorbuffer(p.alloc, 1, 1)) {
@@ -35,11 +35,10 @@ gj::TitleScene::TitleScene(const Param& p) :
     auto& obj = e.get<::picojson::object>();
 
     Score s;
-
-    s.displayName = obj["displayName"].get<std::string>();
-    s.music       = obj["music"].get<std::string>();
-    s.score       = obj["score"].get<std::string>();
-    s.playOffset  = obj["playOffset"].get<double>();
+    s.title      = obj["title"].get<std::string>();
+    s.music      = obj["music"].get<std::string>();
+    s.path       = obj["path"].get<std::string>();
+    s.playOffset = obj["playOffset"].get<double>();
 
     list_.push_back(s);
   }
@@ -56,16 +55,22 @@ gj::UniqPtr<gj::iScene> gj::TitleScene::Update(Frame& frame) {
   /* input handling */
   for (const auto c : frame.input) {
     switch (c) {
+    case ' ': {
+      if (music_) param_.audio->RemoveEffect(music_.get());
+      const auto& s = list_[select_index_];
+      LoadScene::Param param;
+      param.super  = param_;
+      param.path   = s.path;
+      param.title  = s.title;
+      param.orphan = std::move(music_);
+      return param_.alloc->MakeUniq<iScene, LoadScene>(std::move(param));
+    }
     case 'h':
       SelectScore_(select_index_? select_index_-1: list_.size()-1);
       break;
     case 'l':
       SelectScore_((select_index_+1)%list_.size());
       break;
-    case ' ':
-      if (music_) param_.audio->RemoveEffect(music_.get());
-      return param_.alloc->MakeUniq<iScene, PlayScene>(
-        param_, list_[select_index_].displayName, list_[select_index_].score);
     }
   }
 
@@ -126,8 +131,8 @@ gj::UniqPtr<gj::iScene> gj::TitleScene::Update(Frame& frame) {
   prev_.SetPosition(static_cast<int32_t>(w*.2), selector_y);
   frame.Add(&prev_);
   
-  score_.SetPosition((w-score_.width())/2, selector_y);
-  frame.Add(&score_);
+  title_.SetPosition((w-title_.width())/2, selector_y);
+  frame.Add(&title_);
 
   guide_.SetPosition((w-guide_.width())/2, selector_y+3);
   frame.Add(&guide_);
@@ -141,7 +146,7 @@ gj::UniqPtr<gj::iScene> gj::TitleScene::Update(Frame& frame) {
 
 void gj::TitleScene::SelectScore_(size_t index) {
   const auto& s = list_[index];
-  score_        = Text(ConvertStrToWstr(s.displayName));
+  title_        = Text(ConvertStrToWstr(s.title));
   select_index_ = index;
   
   trying_play_ = true;
diff --git a/src/TitleScene.h b/src/TitleScene.h
index 9a2d0b6..acf695a 100644
--- a/src/TitleScene.h
+++ b/src/TitleScene.h
@@ -27,15 +27,15 @@ class TitleScene : public iScene {
 
  private:
   struct Score {
-    std::string displayName;
-    std::string score;
+    std::string title;
+    std::string path;
     std::string music;
     double      playOffset = 0;
   };
 
   Param param_;
 
-  Text score_;
+  Text title_;
   Text next_;
   Text prev_;
   Text guide_;