From 28e7b1e079551db3f7a4c03af26b8648edbb8df9 Mon Sep 17 00:00:00 2001
From: Dominic Masters <dominic@domsplace.com>
Date: Sun, 5 Feb 2023 19:15:06 -0800
Subject: [PATCH] BETA scene generator

---
 assets/games/pokergame/vn/Scene_1.xml         |   8 +-
 src/dawn/visualnovel/scene/SimpleVNScene.cpp  |   3 +
 src/dawn/visualnovel/scene/SimpleVNScene.hpp  | 108 ++++++------
 src/dawnpokergame/game/DawnGame.cpp           |   4 +-
 src/dawntools/locale/languagegen/main.cpp     |   2 +-
 src/dawntools/utils/xml.c                     |  18 +-
 src/dawntools/visualnovel/CMakeLists.txt      |   2 +-
 src/dawntools/visualnovel/vnscenegen/main.cpp | 164 +++++++++++++++++-
 8 files changed, 241 insertions(+), 68 deletions(-)

diff --git a/assets/games/pokergame/vn/Scene_1.xml b/assets/games/pokergame/vn/Scene_1.xml
index 3af77bbb..dc5941ae 100644
--- a/assets/games/pokergame/vn/Scene_1.xml
+++ b/assets/games/pokergame/vn/Scene_1.xml
@@ -4,8 +4,6 @@
     <include path="visualnovel/scene/SimpleVNScene.hpp" />
     <include path="prefabs/characters/DeathPrefab.hpp" />
 
-    <!-- <include scene="Scene_2" /> -->
-    
     <!-- Characters -->
     <character class="DeathPrefab" name="death" />
 
@@ -13,7 +11,7 @@
     <asset type="AudioAsset" name="audio_test" />
 
     <!-- Scene -->
-    <scene type="SimpleVNScene" name="Scene_1" />
+    <scene type="SimpleVNScene" name="vnscene_1" />
 
     <!-- Defaults -->
     <!-- <default type="animation-curve">out-quad</default> -->
@@ -30,7 +28,7 @@
   <events>
     <pause duration="0.1" />
     <text character="death" emotion="happy" string="scene.1.1" />
-    <character-fade character="death" duration="1.0" ease="out-quad" />
-    <scene-transition scene="Scene_2" />
+    <character-fade character="death" duration="1.0" ease="out-quad" fade="in" />
+    <scene-transition scene="vnscene_2" />
   </events>
 </vnscene>
\ No newline at end of file
diff --git a/src/dawn/visualnovel/scene/SimpleVNScene.cpp b/src/dawn/visualnovel/scene/SimpleVNScene.cpp
index 5e3c71e6..99f35f6c 100644
--- a/src/dawn/visualnovel/scene/SimpleVNScene.cpp
+++ b/src/dawn/visualnovel/scene/SimpleVNScene.cpp
@@ -12,6 +12,9 @@ SimpleVNScene::SimpleVNScene(DawnGame *game) : Scene(game) {
 
 }
 
+void SimpleVNScene::vnStage() {
+  
+}
 
 std::vector<Asset*> SimpleVNScene::getRequiredAssets() {
   auto assMan = &this->game->assetManager;
diff --git a/src/dawn/visualnovel/scene/SimpleVNScene.hpp b/src/dawn/visualnovel/scene/SimpleVNScene.hpp
index ae070f8b..1469f0c8 100644
--- a/src/dawn/visualnovel/scene/SimpleVNScene.hpp
+++ b/src/dawn/visualnovel/scene/SimpleVNScene.hpp
@@ -1,55 +1,55 @@
-// Copyright (c) 2022 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#pragma once
-#include "scene/Scene.hpp"
-#include "game/DawnGame.hpp"
-#include "util/array.hpp"
-#include "scene/components/Components.hpp"
-#include "scene/components/audio/AudioListener.hpp"
-#include "visualnovel/VisualNovelManager.hpp"
-#include "visualnovel/events/VisualNovelTextboxEvent.hpp"
-#include "visualnovel/events/timing/VisualNovelPauseEvent.hpp"
-#include "visualnovel/events/VisualNovelFadeEvent.hpp"
-#include "visualnovel/events/VisualNovelCallbackEvent.hpp"
-#include "visualnovel/events/VisualNovelChangeSimpleBackgroundEvent.hpp"
-
-namespace Dawn {
-  class SimpleVNScene : public Scene {
-    protected:
-      Camera *camera = nullptr;
-      UICanvas *canvas = nullptr;
-      VisualNovelTextbox *textbox = nullptr;
-      SimpleVisualNovelBackground *background = nullptr;
-      VisualNovelFader *vnFader = nullptr;
-      VisualNovelManager *vnManager = nullptr;
-      AudioListener *audioListener = nullptr;
-
-      /**
-       * Internal method to stage the VN scene.
-       */
-      virtual void vnStage() = 0;
-
-    public:
-      /**
-       * Constructs a new Simple VN Scene. Custom class that implements the most
-       * common VN Things.
-       * 
-       * @param game 
-       */
-      SimpleVNScene(DawnGame *game);
-
-      std::vector<Asset*> getRequiredAssets() override;
-      void stage() override;
-
-      /**
-       * Returns the first VN event for the scene. Called by the VN Manager for
-       * simple scenes.
-       * 
-       * @return First VN event to be queued.
-       */
-      virtual IVisualNovelEvent * getVNEvent() = 0;
-  };
+// Copyright (c) 2022 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "scene/Scene.hpp"
+#include "game/DawnGame.hpp"
+#include "util/array.hpp"
+#include "scene/components/Components.hpp"
+#include "scene/components/audio/AudioListener.hpp"
+#include "visualnovel/VisualNovelManager.hpp"
+#include "visualnovel/events/VisualNovelTextboxEvent.hpp"
+#include "visualnovel/events/timing/VisualNovelPauseEvent.hpp"
+#include "visualnovel/events/VisualNovelFadeEvent.hpp"
+#include "visualnovel/events/VisualNovelCallbackEvent.hpp"
+#include "visualnovel/events/VisualNovelChangeSimpleBackgroundEvent.hpp"
+
+namespace Dawn {
+  class SimpleVNScene : public Scene {
+    protected:
+      Camera *camera = nullptr;
+      UICanvas *canvas = nullptr;
+      VisualNovelTextbox *textbox = nullptr;
+      SimpleVisualNovelBackground *background = nullptr;
+      VisualNovelFader *vnFader = nullptr;
+      VisualNovelManager *vnManager = nullptr;
+      AudioListener *audioListener = nullptr;
+
+      /**
+       * Internal method to stage the VN scene.
+       */
+      virtual void vnStage();
+
+    public:
+      /**
+       * Constructs a new Simple VN Scene. Custom class that implements the most
+       * common VN Things.
+       * 
+       * @param game 
+       */
+      SimpleVNScene(DawnGame *game);
+
+      std::vector<Asset*> getRequiredAssets() override;
+      void stage() override;
+
+      /**
+       * Returns the first VN event for the scene. Called by the VN Manager for
+       * simple scenes.
+       * 
+       * @return First VN event to be queued.
+       */
+      virtual IVisualNovelEvent * getVNEvent() = 0;
+  };
 }
\ No newline at end of file
diff --git a/src/dawnpokergame/game/DawnGame.cpp b/src/dawnpokergame/game/DawnGame.cpp
index 74ce541e..39f57f40 100644
--- a/src/dawnpokergame/game/DawnGame.cpp
+++ b/src/dawnpokergame/game/DawnGame.cpp
@@ -4,7 +4,7 @@
 // https://opensource.org/licenses/MIT
 
 #include "DawnGame.hpp"
-#include "scenes/Scene_1.hpp"
+#include "scenes/vnscene_1.hpp"
 
 using namespace Dawn;
 
@@ -24,7 +24,7 @@ int32_t DawnGame::init() {
   this->renderManager.init();
   this->audioManager.init();
 
-  this->scene = new Scene_1(this);
+  this->scene = new vnscene_1(this);
   
   return DAWN_GAME_INIT_RESULT_SUCCESS;
 }
diff --git a/src/dawntools/locale/languagegen/main.cpp b/src/dawntools/locale/languagegen/main.cpp
index a2b57854..2669bf5c 100644
--- a/src/dawntools/locale/languagegen/main.cpp
+++ b/src/dawntools/locale/languagegen/main.cpp
@@ -175,7 +175,7 @@ int main(int argc, char *argv[]) {
     }
 
     std::string filenameOut(argv[2]);
-    filenameOut += "/nlanguage_" + it->first + ".language";
+    filenameOut += "/language_" + it->first + ".language";
     
     char *filenameOutC = (char *)malloc(sizeof(char) * (filenameOut.size() + 1));
     if(filenameOutC == NULL) {
diff --git a/src/dawntools/utils/xml.c b/src/dawntools/utils/xml.c
index 6ab327f9..6031551d 100644
--- a/src/dawntools/utils/xml.c
+++ b/src/dawntools/utils/xml.c
@@ -11,6 +11,7 @@ int32_t xmlLoadChild(xml_t *xml, char *data, int32_t i) {
   char c;
   int32_t level = 0;
   uint8_t doing = XML_DOING_NOTHING;
+  uint8_t doingBeforeComment;
   bool insideTag = false;
   char* buffer = malloc(sizeof(char) * XML_TEXT_BUFFER_MAX);
   int32_t bufferLength = 0;
@@ -27,9 +28,10 @@ int32_t xmlLoadChild(xml_t *xml, char *data, int32_t i) {
         // Look for either an opening tag (<) or a word for a value.
         if(c == '>') continue;
         if(c == '<') {
-          if(data[i] == '!' && data[i+1] == '-') {
+          if(data[i] == '!' && data[i+1] == '-' && data[i+2] == '-') {
+            doingBeforeComment = doing;
             doing = XML_PARSING_COMMENT;
-            i += 2;
+            i += 3;
           } else if(insideTag) {
             i = xmlLoadChild(xml->children + xml->childrenCount++, data, i-1);
             doing = XML_PARSING_CHILD;
@@ -145,6 +147,13 @@ int32_t xmlLoadChild(xml_t *xml, char *data, int32_t i) {
             continue;
           }
 
+          if(data[i] == '!' && data[i+1] == '-' && data[i+2] == '-') {
+            doingBeforeComment = doing;
+            doing = XML_PARSING_COMMENT;
+            i += 3;
+            continue;
+          }
+
           // Likely another child.
           i = xmlLoadChild(xml->children + xml->childrenCount++, data, i-1);
         }
@@ -168,8 +177,9 @@ int32_t xmlLoadChild(xml_t *xml, char *data, int32_t i) {
         if(c != '-') continue;
         if(data[i] != '-') continue;
         if(data[i+1] != '>') continue;
-        i+= 2;
-        doing = XML_DOING_NOTHING;
+        i += 2;
+        doing = doingBeforeComment;
+        break;
 
       default:
         break;
diff --git a/src/dawntools/visualnovel/CMakeLists.txt b/src/dawntools/visualnovel/CMakeLists.txt
index bbf2d387..16ad8d1a 100644
--- a/src/dawntools/visualnovel/CMakeLists.txt
+++ b/src/dawntools/visualnovel/CMakeLists.txt
@@ -14,6 +14,6 @@ function(tool_vnscene target in)
   )
   target_include_directories(${DAWN_TARGET_NAME}
     PUBLIC
-      ${DAWN_GENERATED_DIR}/scenes
+      ${DAWN_GENERATED_DIR}
   )
 endfunction()
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/main.cpp b/src/dawntools/visualnovel/vnscenegen/main.cpp
index f0724fbe..26435b24 100644
--- a/src/dawntools/visualnovel/vnscenegen/main.cpp
+++ b/src/dawntools/visualnovel/vnscenegen/main.cpp
@@ -119,6 +119,101 @@ int32_t parseHeader(struct HeaderInformation *info, xml_t *node) {
   return 0;
 }
 
+std::string parseEase(std::string e) {
+  if(e == "out-quad") return "easeOutQuad";
+  std::cout << "Invalid ease defined" << std::endl;
+  return "";
+}
+
+std::string parseEvent(xml_t *evt, struct HeaderInformation *header) {
+  std::string buffer;
+  std::string node(evt->node);
+
+  if(node == "pause") {
+    header->includes.push_back("visualnovel/events/timing/VisualNovelPauseEvent.hpp");
+
+    auto attrDuration = xmlGetAttributeByName(evt, "duration");
+    if(attrDuration == -1) {
+      std::cout << "Pause event is missing duration argument." << std::endl;
+      return "";
+    }
+    auto dur = std::string(evt->attributeDatas[attrDuration]);
+    if(dur.find('.') == std::string::npos) dur += ".0";
+    buffer += "new VisualNovelPauseEvent(vnManager, " + dur + "f)";
+
+
+  } else if(node == "text") {
+    header->includes.push_back("visualnovel/events/VisualNovelTextboxEvent.hpp");
+
+    auto attrChar = xmlGetAttributeByName(evt, "character");
+    if(attrChar == -1) {
+      std::cout << "Text event missing character attribute." << std::endl;
+      return "";
+    }
+
+    auto attrEmotion = xmlGetAttributeByName(evt, "emotion");
+    if(attrEmotion == -1) {
+      std::cout << "Text event missing emotion attribute." << std::endl;
+      return "";
+    }
+
+    auto attrString = xmlGetAttributeByName(evt, "string");
+    if(attrString == -1) {
+      std::cout << "Text event missing string attribute." << std::endl;
+      return "";
+    }
+
+    std::string charName(evt->attributeDatas[attrChar]);
+    std::string emo(evt->attributeDatas[attrEmotion]);
+    emo[0] = toupper(emo[0]);
+    
+    buffer += "new VisualNovelTextboxEvent(vnManager, ";
+    buffer += "this->" + charName + "->vnCharacter, ";
+    buffer += "this->" + charName + "->emotion" + emo + ", ";
+    buffer += "\"" + std::string(evt->attributeDatas[attrString]) + "\"" ;
+    buffer += ")";
+
+
+  } else if(node == "character-fade") {
+    header->includes.push_back("visualnovel/events/characters/VisualNovelFadeCharacterEvent.hpp");
+    auto attrChar = xmlGetAttributeByName(evt, "character");
+    auto attrDuration = xmlGetAttributeByName(evt, "duration");
+    auto attrEase = xmlGetAttributeByName(evt, "ease");
+    auto attrFade = xmlGetAttributeByName(evt, "fade");
+
+    if(attrChar == -1) {
+      std::cout << "Character fade event missing character attribute." << std::endl;
+      return "";
+    }
+    
+    std::string character = std::string(evt->attributeDatas[attrChar]);
+    std::string easeIn = "true";
+    std::string ease = "easeLinear";
+    std::string duration = "1.0";
+
+    if(attrFade != -1) easeIn = std::string(evt->attributeDatas[attrFade]) == "in" ? "true" : "false";
+
+    if(attrDuration != -1) duration = std::string(evt->attributeDatas[attrDuration]);
+    if(duration.find('.') == std::string::npos) duration += ".0";
+
+    if(attrEase != -1) {
+      ease = parseEase(std::string(evt->attributeDatas[attrEase]));
+      if(ease.size() == 0) return "";
+    }
+
+    buffer += "new VisualNovelFadeCharacterEvent(vnManager, ";
+    buffer += "this->" + character + "->vnCharacter, ";
+    buffer += easeIn + ", ";
+    buffer += "&" + ease + ", ";
+    buffer += duration + "f";
+    buffer += ")";
+  } else if(node == "scene-transition") {
+    buffer += " ";
+  }
+
+  return buffer;
+}
+
 int main(int32_t argc, char *args[]) {
   if(argc != 3) {
     std::cout << "Invalid number of args passed to VNScene Generator" << std::endl;
@@ -172,13 +267,80 @@ int main(int32_t argc, char *args[]) {
     return 1;
   }
 
+  // Parse and load events.
+  std::string bufferEvents;
+  for(int32_t i = 0; i < xml.childrenCount; i++) {
+    auto events = xml.children + i;
+    if(std::string(events->node) != "events") continue;
+    bufferEvents += "\n        start\n";
+    for(int32_t j = 0; j < events->childrenCount; j++) {
+      auto evt = events->children + j;
+      if(std::string(evt->node) == "scene-transition") continue;
+      auto evtParsed = parseEvent(evt, &header);
+      if(evtParsed.size() == 0) return 1;
+      bufferEvents += "          ->then(" + evtParsed + ")\n";
+    }
+    bufferEvents += "        ;\n";
+  }
+
+  // Now render output to file.
   std::string bufferOut;
-  bufferOut += "#pragma once\n";
+  bufferOut += "#pragma once\n\n";
   auto itInclude = header.includes.begin();
   while(itInclude != header.includes.end()) {
     bufferOut += "#include \"" + (*itInclude) + "\"\n";
     ++itInclude;
   }
+  bufferOut += "\nnamespace Dawn{\n";
+  bufferOut += "  class " + header.name + " : public " + header.type + " {\n";
+  bufferOut += "    protected:\n";
+
+  // Characters (As properties)
+  auto itCharacters = header.characters.begin();
+  while(itCharacters != header.characters.end()) {
+    auto c = *itCharacters;
+    bufferOut += "      " + c.clazz + " *" + c.name + ";\n";
+    ++itCharacters;
+  }
+  bufferOut += "\n      void vnStage() override {\n";
+  bufferOut += "        " + header.type + "::vnStage();\n";
+  
+  // Initialize the characters
+  itCharacters = header.characters.begin();
+  while(itCharacters != header.characters.end()) {
+    auto c = *itCharacters;
+    bufferOut += "        this->" + c.name + " = " + c.clazz + "::create(this);\n";
+    ++itCharacters;
+  }
+
+  bufferOut += "      }\n";
+
+  bufferOut += "\n    public:\n";
+  bufferOut += "      " + header.name + "(DawnGame *game) : " + header.type + "(game) {\n";
+  bufferOut += "      }\n";
+
+  // Assets
+  bufferOut += "\n      std::vector<Asset*> getRequiredAssets() override{\n";
+  bufferOut += "        auto man = &this->game->assetManager;\n";
+  bufferOut += "        std::vector<Asset*> assets = " + header.type + "::getRequiredAssets();\n";
+  itCharacters = header.characters.begin();
+  while(itCharacters != header.characters.end()) {
+    auto c = *itCharacters;
+    bufferOut += "        vectorAppend(&assets, " + c.clazz + "::getRequiredAssets(man));\n"; 
+    ++itCharacters;
+  }
+  bufferOut += "        return assets;\n";
+  bufferOut += "      }\n";
+
+  // VN Events
+  bufferOut += "\n      IVisualNovelEvent * getVNEvent() override {\n";
+  bufferOut += "        auto start = new VisualNovelPauseEvent(vnManager, 0.01f);\n";
+  bufferOut += bufferEvents;
+  bufferOut += "\n        return start;\n";
+  bufferOut += "      }\n";
+
+  bufferOut += "  };\n";
+  bufferOut += "}";
 
   // Finished with XML data, now we can write data out.
   xmlDispose(&xml);