diff --git a/assets/games/liminal/prefabs/VNTextbox.xml b/assets/games/liminal/prefabs/VNTextbox.xml index 57362555..73b3102f 100644 --- a/assets/games/liminal/prefabs/VNTextbox.xml +++ b/assets/games/liminal/prefabs/VNTextbox.xml @@ -13,7 +13,7 @@ texture="texture_border" /> - + + + \ No newline at end of file diff --git a/src/dawn/display/font/FontMeasure.cpp b/src/dawn/display/font/FontMeasure.cpp index 8fdcdb06..fdf88acd 100644 --- a/src/dawn/display/font/FontMeasure.cpp +++ b/src/dawn/display/font/FontMeasure.cpp @@ -19,7 +19,7 @@ int32_t FontMeasure::getQuadCount() { return this->realLength; } -float_t FontMeasure::getHeightOfLineCount(int32_t lineCount) { +float_t FontMeasure::getHeightOfLineCount(size_t lineCount) { assertTrue(lineCount > 0); return this->lineHeight * lineCount; } @@ -28,13 +28,13 @@ size_t FontMeasure::getLineCount() { return this->lines.size(); } -int32_t FontMeasure::getQuadsOnLine(int32_t line) { +int32_t FontMeasure::getQuadsOnLine(size_t line) { assertTrue(line >= 0); assertTrue(line < this->lines.size()); return this->lines[line].length; } -int32_t FontMeasure::getQuadIndexOnLine(int32_t line) { +int32_t FontMeasure::getQuadIndexOnLine(size_t line) { assertTrue(line >= 0); assertTrue(line < this->lines.size()); return this->lines[line].start; diff --git a/src/dawn/display/font/FontMeasure.hpp b/src/dawn/display/font/FontMeasure.hpp index 80a80bc4..d4e85683 100644 --- a/src/dawn/display/font/FontMeasure.hpp +++ b/src/dawn/display/font/FontMeasure.hpp @@ -60,7 +60,7 @@ namespace Dawn { * @param line Which line to get the count of quads for. * @return Count of quads on that line. */ - int32_t getQuadsOnLine(int32_t line); + int32_t getQuadsOnLine(size_t line); /** * Returns the index, of which quad is the first quad on the given line. @@ -68,7 +68,7 @@ namespace Dawn { * @param line Line to get the quad index of. * @return The quad index of that line. */ - int32_t getQuadIndexOnLine(int32_t line); + int32_t getQuadIndexOnLine(size_t line); /** * Returns the height of the count of lines provided. @@ -76,7 +76,7 @@ namespace Dawn { * @param lineCount Count of lines to get the height of. * @return Height of the given count of lines. */ - float_t getHeightOfLineCount(int32_t lineCount); + float_t getHeightOfLineCount(size_t lineCount); /** * Returns the count of lines in this string. diff --git a/src/dawn/games/vn/components/CMakeLists.txt b/src/dawn/games/vn/components/CMakeLists.txt index 582b4b69..65898c2b 100644 --- a/src/dawn/games/vn/components/CMakeLists.txt +++ b/src/dawn/games/vn/components/CMakeLists.txt @@ -7,4 +7,5 @@ target_sources(${DAWN_TARGET_NAME} PRIVATE VNManager.cpp + VNTextboxScroller.cpp ) \ No newline at end of file diff --git a/src/dawn/games/vn/components/VNTextboxScroller.cpp b/src/dawn/games/vn/components/VNTextboxScroller.cpp new file mode 100644 index 00000000..f7f85084 --- /dev/null +++ b/src/dawn/games/vn/components/VNTextboxScroller.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "VNTextboxScroller.hpp" +#include "game/DawnGame.hpp" + +using namespace Dawn; + +VNTextboxScroller::VNTextboxScroller(SceneItem *item) : + SceneItemComponent(item), + label(nullptr) +{ +} + +void VNTextboxScroller::onStart() { + assertNotNull(label); + + std::function x = [&]{ + this->lineCurrent = 0; + this->timeCharacter = 0; + this->label->startQuad = 0; + this->label->quadCount = 0; + }; + x(); + + useEvent([&](float_t delta){ + auto game = this->getGame(); + + this->timeCharacter += delta; + if(this->hasRevealedAllCurrentCharacters()) { + if(this->hasRevealedAllCharacters()) { + if(game->inputManager.isPressed(INPUT_BIND_ACCEPT)) { + std::cout << "HIDE" << std::endl; + } + } else { + if(game->inputManager.isPressed(INPUT_BIND_ACCEPT)) { + this->lineCurrent += this->getCountOfVisibleLines(); + this->label->startQuad = 0; + for(int32_t i = 0; i < this->lineCurrent; i++) { + this->label->startQuad += this->label->measure.getQuadsOnLine(i); + } + this->label->quadCount = 0; + this->timeCharacter = 0.0f; + + // this->label.setTransform( + // UI_COMPONENT_ALIGN_STRETCH, + // UI_COMPONENT_ALIGN_STRETCH, + // glm::vec4( + // glm::vec2( + // this->border.getBorderSize().x + this->labelPadding.x, + // this->border.getBorderSize().y + this->labelPadding.y - + // this->label.measure.getHeightOfLineCount(this->lineCurrent) + // ), + // this->border.getBorderSize() + this->labelPadding + // ), + // 5.0f + // ); + // this->eventNewPage.invoke(); + } + } + + return; + } + + auto lastTimeCharacter = mathFloor(this->timeCharacter); + if(game->inputManager.isDown(INPUT_BIND_ACCEPT)) { + this->timeCharacter += game->timeManager.delta * VN_TEXTBOX_SPEED_FASTER; + } else { + this->timeCharacter += game->timeManager.delta * VN_TEXTBOX_SPEED; + } + + this->label->quadCount = mathFloor(this->timeCharacter); + // this->eventCharacterRevealed.invoke(); + }, getScene()->eventSceneUpdate); +} + +size_t VNTextboxScroller::getCountOfVisibleLines() { + float_t y = this->label->getHeight(); + size_t i = 1; + for(i = this->label->measure.getLineCount(); i > 0; --i) { + if(y >= this->label->measure.getHeightOfLineCount(i)) { + return i; + } + } + return this->label->measure.getLineCount(); +} + +bool_t VNTextboxScroller::hasRevealedAllCurrentCharacters() { + int32_t quadsTotal = 0; + for( + size_t i = this->lineCurrent; + i < mathMin( + this->label->measure.getLineCount(), + this->lineCurrent + this->getCountOfVisibleLines() + ); + i++ + ) { + quadsTotal += this->label->measure.getQuadsOnLine(i); + } + return mathFloor(this->timeCharacter) >= quadsTotal; +} + +bool_t VNTextboxScroller::hasRevealedAllCharacters() { + return ( + this->lineCurrent + this->getCountOfVisibleLines() >= + this->label->measure.getLineCount() + ); +} \ No newline at end of file diff --git a/src/dawn/games/vn/components/VNTextboxScroller.hpp b/src/dawn/games/vn/components/VNTextboxScroller.hpp new file mode 100644 index 00000000..40a679b4 --- /dev/null +++ b/src/dawn/games/vn/components/VNTextboxScroller.hpp @@ -0,0 +1,53 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "scene/SceneItemComponent.hpp" +#include "scene/components/ui/UILabel.hpp" +#include "input/InputManager.hpp" + +#define VN_TEXTBOX_SPEED 25.0f +#define VN_TEXTBOX_SPEED_FASTER 40.0f + +namespace Dawn { + class VNTextboxScroller : public SceneItemComponent { + protected: + + public: + // @optional + StateProperty label; + + size_t lineCurrent = 0; + float_t timeCharacter = 0.0f; + + VNTextboxScroller(SceneItem *item); + + virtual void onStart() override; + + /** + * Returns the count of visible lines within the textbox. Mostly used for + * when we need to decide how to wrap. + * + * @return The count of visible lines. + */ + size_t getCountOfVisibleLines(); + + /** + * Returns true if all of the characters that can be made visible for the + * current textbox size have finished revealing, or false if not. + * + * @return True if above statement is met. + */ + bool_t hasRevealedAllCurrentCharacters(); + + /** + * Returns true only when every character passed previously in setText + * has been revealed by scrolling. + * + * @return True if above statement is true. + */ + bool_t hasRevealedAllCharacters(); + }; +} \ No newline at end of file diff --git a/src/dawn/games/vn/events/VNTextEvent.hpp b/src/dawn/games/vn/events/VNTextEvent.hpp index 00021a60..d5890c1d 100644 --- a/src/dawn/games/vn/events/VNTextEvent.hpp +++ b/src/dawn/games/vn/events/VNTextEvent.hpp @@ -14,13 +14,6 @@ namespace Dawn { protected: void onStart() override { std::cout << "TEXT: " << text << std::endl; - - useEvent([&](float_t delta) { - if(getScene()->game->inputManager.isPressed(INPUT_BIND_ACCEPT)) { - std::cout << "Text Advance" << std::endl; - this->next(); - } - }, getScene()->eventSceneUpdate); } }; } \ No newline at end of file diff --git a/src/dawn/scene/components/ui/UILabel.cpp b/src/dawn/scene/components/ui/UILabel.cpp index 6248e1b7..7e8dc69b 100644 --- a/src/dawn/scene/components/ui/UILabel.cpp +++ b/src/dawn/scene/components/ui/UILabel.cpp @@ -13,7 +13,9 @@ UILabel::UILabel(SceneItem *item) : text(""), fontSize(10.0f), font(&item->scene->game->renderManager.defaultFont), - maxWidth(UI_LABEL_MAX_WIDTH_ALIGN) + maxWidth(UI_LABEL_MAX_WIDTH_ALIGN), + startQuad(0), + quadCount(-1) { } @@ -30,7 +32,12 @@ void UILabel::updateMesh() { if(!this->hasText()) return; float_t width = this->maxWidth; - assertTrue(width == UI_LABEL_MAX_WIDTH_NONE || width == UI_LABEL_MAX_WIDTH_ALIGN || width > 0); + assertTrue( + width == UI_LABEL_MAX_WIDTH_NONE || + width == UI_LABEL_MAX_WIDTH_ALIGN || + width > 0 + ); + if(width == UI_LABEL_MAX_WIDTH_ALIGN) { auto dimensional = this->getParentDimensional(); auto align = (glm::vec4)this->alignment; @@ -56,6 +63,8 @@ void UILabel::updateMesh() { ); this->needsRebuffering = false; + + this->eventFontRebuffered.invoke(); } std::vector UILabel::getPassItems( @@ -100,7 +109,7 @@ void UILabel::onStart() { useEffect([&]{ alignmentNeedsUpdating = true; - }, { &fontSize, &font, &text, &maxWidth }); + }, { &fontSize, &font, &text, &maxWidth, &startQuad, &quadCount }); useEffect([&]{ needsRebuffering = true; diff --git a/src/dawn/scene/components/ui/UILabel.hpp b/src/dawn/scene/components/ui/UILabel.hpp index 3d7fff4a..c06b025c 100644 --- a/src/dawn/scene/components/ui/UILabel.hpp +++ b/src/dawn/scene/components/ui/UILabel.hpp @@ -39,10 +39,14 @@ namespace Dawn { StateProperty maxWidth; /* @optional */ struct Color textColor = COLOR_WHITE; + // @optional + StateProperty startQuad; + // @optional + StateProperty quadCount; + + StateEvent<> eventFontRebuffered; struct FontMeasure measure; - int32_t startQuad = 0; - int32_t quadCount = -1; UILabel(SceneItem *item); diff --git a/src/dawntools/scenetool/SceneTool.cpp b/src/dawntools/scenetool/SceneTool.cpp index d0eda0ee..d40d53b0 100644 --- a/src/dawntools/scenetool/SceneTool.cpp +++ b/src/dawntools/scenetool/SceneTool.cpp @@ -35,7 +35,6 @@ int32_t SceneTool::start() { struct SceneItemComponentRegistry registry; registry.sources = File::normalizeSlashes(flags["sources"]); scene.registry = ®istry; - auto result = ((SceneParser()).parse(&xml, &scene, &error)); if(result != 0) { std::cout << "Failed to parse scene " << input.filename << "::" << error << std::endl; diff --git a/src/dawntools/util/generator/SceneGenerator.cpp b/src/dawntools/util/generator/SceneGenerator.cpp index d1ac1bc7..88b516f3 100644 --- a/src/dawntools/util/generator/SceneGenerator.cpp +++ b/src/dawntools/util/generator/SceneGenerator.cpp @@ -13,6 +13,8 @@ void SceneGenerator::generate( struct MethodGenInfo &methodAssets, struct MethodGenInfo &methodInit ) { + assertNotNull(scene); + classInfo.clazz = scene->name; classInfo.constructorArgs = "DawnGame *game"; classInfo.extendArgs = "game"; @@ -54,10 +56,12 @@ void SceneGenerator::generate( int32_t childNumber = 0; int32_t componentNumber = 0; std::map assetMap; + std::vector componentsUnused; - auto sceneItems = scene->items.begin(); - while(sceneItems != scene->items.end()) { - SceneItemGenerator::generate( + auto itDeps = scene->dependencies.begin(); + while(itDeps != scene->dependencies.end()) { + auto dep = *itDeps; + SceneItemGenerator::generateDependency( assetNumber, componentNumber, childNumber, @@ -68,24 +72,15 @@ void SceneGenerator::generate( &methodAssets.body, "", "this", - &(*sceneItems), - "" + componentsUnused, + scene->items, + scene->code, + dep ); - ++sceneItems; + ++itDeps; } + // Seal methods line(&methodAssets.body, "return assets;", ""); - - // Code - auto itCode = scene->code.begin(); - while(itCode != scene->code.end()) { - SceneCodeGenerator::generate( - &classInfo.publicProperties, - &methodInit.body, - &(*itCode), - "" - ); - ++itCode; - } } \ No newline at end of file diff --git a/src/dawntools/util/generator/SceneItemGenerator.cpp b/src/dawntools/util/generator/SceneItemGenerator.cpp index 6b81addd..2679e5f6 100644 --- a/src/dawntools/util/generator/SceneItemGenerator.cpp +++ b/src/dawntools/util/generator/SceneItemGenerator.cpp @@ -7,6 +7,76 @@ using namespace Dawn; +void SceneItemGenerator::generateDependency( + int32_t &assetNumber, + int32_t &componentNumber, + int32_t &childNumber, + std::map &assetMap, + std::vector &includes, + std::vector *publicProperties, + std::vector *initBody, + std::vector *assetBody, + std::string name, + std::string sceneRef, + std::vector &components, + std::vector &children, + std::vector &code, + struct SceneItemDependency dep +) { + switch(dep.type) { + case SCENE_ITEM_DEPENDENCY_TYPE_CODE: { + auto i = &code[dep.position]; + SceneCodeGenerator::generate( + publicProperties, + initBody, + i, + "" + ); + line(initBody, "", ""); + break; + } + + case SCENE_ITEM_DEPENDENCY_TYPE_COMPONENT: { + auto i = &components[dep.position]; + SceneItemComponentGenerator::generate( + assetMap, + componentNumber, + includes, + name, + publicProperties, + initBody, + i, + "" + ); + line(initBody, "", ""); + break; + } + + case SCENE_ITEM_DEPENDENCY_TYPE_ITEM: { + auto i = &children[dep.position]; + SceneItemGenerator::generate( + assetNumber, + componentNumber, + childNumber, + assetMap, + includes, + publicProperties, + initBody, + assetBody, + name, + sceneRef, + i, + "" + ); + line(initBody, "", ""); + break; + } + + default: + assertUnreachable(); + } +} + void SceneItemGenerator::generate( int32_t &assetNumber, int32_t &componentNumber, @@ -21,6 +91,11 @@ void SceneItemGenerator::generate( struct SceneItem *item, std::string tabs ) { + assertNotNull(publicProperties); + assertNotNull(initBody); + assertNotNull(assetBody); + assertNotNull(item); + // Determine interface std::string name = "itm" + std::to_string(childNumber++); std::string itemType = "SceneItem"; @@ -82,26 +157,11 @@ void SceneItemGenerator::generate( ++itAssets; } - // Add components for children - auto itComponents = item->components.begin(); - while(itComponents != item->components.end()) { - SceneItemComponentGenerator::generate( - assetMap, - componentNumber, - includes, - name, - publicProperties, - initBody, - &(*itComponents), - "" - ); - ++itComponents; - } - - // Process sub children - auto itChildren = item->children.begin(); - while(itChildren != item->children.end()) { - SceneItemGenerator::generate( + // Add the dependencies + auto itDeps = item->dependencies.begin(); + while(itDeps != item->dependencies.end()) { + auto dep = *itDeps; + SceneItemGenerator::generateDependency( assetNumber, componentNumber, childNumber, @@ -112,26 +172,16 @@ void SceneItemGenerator::generate( assetBody, name, sceneRef, - &(*itChildren), - "" + item->components, + item->children, + item->code, + dep ); - ++itChildren; + ++itDeps; } // Set parent if(!parentRef.empty()) { line(initBody, name + "->transform.setParent(&"+parentRef+"->transform);", ""); } - - // Code - auto itCode = item->code.begin(); - while(itCode != item->code.end()) { - SceneCodeGenerator::generate( - publicProperties, - initBody, - &(*itCode), - "" - ); - ++itCode; - } } \ No newline at end of file diff --git a/src/dawntools/util/generator/SceneItemGenerator.hpp b/src/dawntools/util/generator/SceneItemGenerator.hpp index 35fdb66c..48dce188 100644 --- a/src/dawntools/util/generator/SceneItemGenerator.hpp +++ b/src/dawntools/util/generator/SceneItemGenerator.hpp @@ -12,6 +12,23 @@ namespace Dawn { class SceneItemGenerator : public CodeGen { public: + static void generateDependency( + int32_t &assetNumber, + int32_t &componentNumber, + int32_t &childNumber, + std::map &assetMap, + std::vector &includes, + std::vector *publicProperties, + std::vector *initBody, + std::vector *assetBody, + std::string name, + std::string sceneRef, + std::vector &components, + std::vector &children, + std::vector &code, + struct SceneItemDependency dep + ); + static void generate( int32_t &assetNumber, int32_t &componentNumber, diff --git a/src/dawntools/util/parser/SceneItemParser.cpp b/src/dawntools/util/parser/SceneItemParser.cpp index 26773d73..44fe784a 100644 --- a/src/dawntools/util/parser/SceneItemParser.cpp +++ b/src/dawntools/util/parser/SceneItemParser.cpp @@ -54,6 +54,8 @@ int32_t SceneItemParser::onParse( out->prefab = values["prefab"]; + struct SceneItemDependency dep; + auto itChildren = node->children.begin(); while(itChildren != node->children.end()) { // Parse child nodes, they may be components or not @@ -65,6 +67,12 @@ int32_t SceneItemParser::onParse( auto ret = (SceneItemParser()).parse(c, &child, error); if(ret != 0) return ret; out->children.push_back(child); + + // Add a dependency. This solves the reference order problem. + struct SceneItemDependency dep; + dep.type = SCENE_ITEM_DEPENDENCY_TYPE_ITEM; + dep.position = out->children.size() - 1; + out->dependencies.push_back(dep); } else if(c->node == "asset") { struct SceneAsset asset; @@ -77,6 +85,11 @@ int32_t SceneItemParser::onParse( auto ret = (SceneCodeParser()).parse(c, &code, error); if(ret != 0) return ret; out->code.push_back(code); + + // Add Dep + dep.type = SCENE_ITEM_DEPENDENCY_TYPE_CODE; + dep.position = out->code.size() - 1; + out->dependencies.push_back(dep); } else { struct SceneItemComponent component; @@ -84,6 +97,12 @@ int32_t SceneItemParser::onParse( auto ret = (SceneItemComponentParser()).parse(c, &component, error); if(ret != 0) return ret; out->components.push_back(component); + + // Add dep + struct SceneItemDependency dep; + dep.type = SCENE_ITEM_DEPENDENCY_TYPE_COMPONENT; + dep.position = out->components.size() - 1; + out->dependencies.push_back(dep); } ++itChildren; } diff --git a/src/dawntools/util/parser/SceneItemParser.hpp b/src/dawntools/util/parser/SceneItemParser.hpp index f65a970b..00678bf3 100644 --- a/src/dawntools/util/parser/SceneItemParser.hpp +++ b/src/dawntools/util/parser/SceneItemParser.hpp @@ -9,6 +9,17 @@ #include "util/parser/SceneCodeParser.hpp" namespace Dawn { + enum SceneItemDependencyType { + SCENE_ITEM_DEPENDENCY_TYPE_ITEM, + SCENE_ITEM_DEPENDENCY_TYPE_COMPONENT, + SCENE_ITEM_DEPENDENCY_TYPE_CODE, + }; + + struct SceneItemDependency { + enum SceneItemDependencyType type; + size_t position; + }; + struct SceneItem { struct SceneItemComponentRegistry *registry; std::string ref; @@ -21,6 +32,7 @@ namespace Dawn { std::vector children; std::vector assets; std::vector code; + std::vector dependencies; }; class SceneItemParser : public XmlParser { diff --git a/src/dawntools/util/parser/SceneParser.cpp b/src/dawntools/util/parser/SceneParser.cpp index 3dbe86ec..4499a862 100644 --- a/src/dawntools/util/parser/SceneParser.cpp +++ b/src/dawntools/util/parser/SceneParser.cpp @@ -29,6 +29,8 @@ int32_t SceneParser::onParse( out->name = values["name"]; out->extend = values["extend"]; + struct SceneItemDependency dep; + //Parse the children auto itChildren = node->children.begin(); while(itChildren != node->children.end()) { @@ -40,12 +42,22 @@ int32_t SceneParser::onParse( ret = (SceneItemParser()).parse(child, &item, error); if(ret != 0) return 1; out->items.push_back(item); + + //Add the dependency + dep.type = SCENE_ITEM_DEPENDENCY_TYPE_ITEM; + dep.position = out->items.size() - 1; + out->dependencies.push_back(dep); } else if(child->node == "code") { struct SceneCode code; ret = (SceneCodeParser()).parse(child, &code, error); if(ret != 0) return ret; out->code.push_back(code); + + //Add the dependency + dep.type = SCENE_ITEM_DEPENDENCY_TYPE_CODE; + dep.position = out->code.size() - 1; + out->dependencies.push_back(dep); } ++itChildren; diff --git a/src/dawntools/util/parser/SceneParser.hpp b/src/dawntools/util/parser/SceneParser.hpp index c9aef74f..e94ea214 100644 --- a/src/dawntools/util/parser/SceneParser.hpp +++ b/src/dawntools/util/parser/SceneParser.hpp @@ -14,6 +14,7 @@ namespace Dawn { std::vector items; std::vector code; struct SceneItemComponentRegistry *registry; + std::vector dependencies; }; class SceneParser : public XmlParser {