From bd2962fc3838bd801ef30911a680d3f3fff63208 Mon Sep 17 00:00:00 2001 From: Asuro Date: Wed, 21 May 2025 02:48:36 +0200 Subject: [PATCH] refactor --- src/game/Entity.h | 151 ++++++++++++++ src/game/Level.cpp | 271 +----------------------- src/game/Level.h | 152 +------------- src/game/Tools.cpp | 3 + src/game/UI.cpp | 333 ++++++++++++++++++++++++++++++ src/game/UI.h | 41 ++++ src/game/data/static/uiconfig.dat | Bin 9670 -> 9704 bytes src/game/mini.def | 1 + src/gen/Generated.cpp | 12 ++ src/gen/Generated.h | 5 +- 10 files changed, 557 insertions(+), 412 deletions(-) create mode 100644 src/game/Entity.h create mode 100644 src/game/UI.cpp create mode 100644 src/game/UI.h diff --git a/src/game/Entity.h b/src/game/Entity.h new file mode 100644 index 0000000..c1e1745 --- /dev/null +++ b/src/game/Entity.h @@ -0,0 +1,151 @@ +#pragma once + +#include "../gen/Generated.h" +#include "Log.h" +#include "Puzzle.h" // TODO: remove +#include "rendering/Rendering.h" +#include + +#define ENTITY_HANDLE(X) \ + struct X \ + { \ + uint16_t Idx = UINT16_MAX; \ + }; \ + inline bool IsValid(X h) \ + { \ + return h.Idx != UINT16_MAX; \ + } + +namespace Game +{ + struct EntityRenderData + { + Gen::Vec4 DotColor{1.0f, 1.0f, 1.0f, 1.0f}; + Gen::Vec4 BaseColor{0.0f, 0.0f, 0.0f, 1.0f}; + Gen::Transform Transform; + Gen::EMaterial::Enum MaterialHandle = Gen::EMaterial::UI; + Gen::TextureHandle TextureHandle; + Gen::ModelHandle ModelH; + bool Visible = true; + + void Render(const Model* models, const Material* materials, const Texture* textures); + void LoadFromSaved(const Gen::SavedEntityRenderData& saved); + }; + + ENTITY_HANDLE(CubeHandle); + struct Cube + { + int32_t TestX = -1; + int32_t TestY = -1; + EntityRenderData EData; + + void Setup(); + void Update(); + }; + + ENTITY_HANDLE(TestEntityHandle); + struct TestEntity + { + EntityRenderData EData; + + void Setup(); + }; + + ENTITY_HANDLE(PuzzleTileEntityHandle); + struct PuzzleTileEntity + { + EntityRenderData EData; + }; + + ENTITY_HANDLE(PuzzleTileCoverHandle); + struct PuzzleTileCover + { + EntityRenderData EData; + }; + + ENTITY_HANDLE(UIQuadEntityHandle); + struct UIQuadEntity + { + EntityRenderData EData; + Gen::Vec3 UIPos; + float UIRot = 0.0f; + }; + + ENTITY_HANDLE(LevelEntityHandle); + struct LevelEntity + { + EntityRenderData EData; + }; + + class IEntityManager + { + public: + virtual bool Setup(uint8_t*& ptr, bool forceReset) = 0; + }; + + template class EntityManager : public IEntityManager + { + public: + uint16_t Count = 0; + T* Data = nullptr; + uint32_t EntitySize = 0; + T InvalidObject{}; + bool IsEnabled = true; + + public: + // Returns true if size changed + bool Setup(uint8_t*& ptr, bool forceReset) + { + bool changed = false; + if (EntitySize != sizeof(T) || forceReset) + { + Count = 0; + changed = true; + } + EntitySize = sizeof(T); + Data = reinterpret_cast(ptr); + ptr += C * EntitySize; + return changed; + } + + HandleT New() + { + if (Data == nullptr) + { + ERROR_ONCE("Accessed EntityManager before setup!"); + return {}; + } + if (Count >= C) + { + ERROR_ONCE("Too many entities!"); + return {}; + } + Data[Count] = {}; + HandleT H; + H.Idx = Count; + ++Count; + return H; + } + + T& Get(HandleT handle) + { + if (handle.Idx >= Count) + { + ERROR_ONCE("OOB Access!"); + return InvalidObject; + } + return Data[handle.Idx]; + } + + void Render(const Model* models, const Material* materials, const Texture* textures) + { + if (!IsEnabled) return; + for (uint16_t i = 0; i < Count; ++i) + { + Get({i}).EData.Render(models, materials, textures); + } + } + }; + + typedef EntityManager UIQuadEntityManager; +} // namespace Game diff --git a/src/game/Level.cpp b/src/game/Level.cpp index f76fe81..1ec3dd0 100644 --- a/src/game/Level.cpp +++ b/src/game/Level.cpp @@ -6,6 +6,7 @@ #include "Level.h" #include "Log.h" #include "Puzzle.h" +#include "UI.h" #include "bx/bx.h" #include "rendering/Rendering.h" @@ -189,6 +190,7 @@ namespace Game Puzzles[i].Setup(); } } + PuzzleUI.Setup(); TabletHandle = UIQuads.New(); UpdatePlayerInputMode(); @@ -286,12 +288,17 @@ namespace Game } // Puzzle tiles + uint16_t activeIdx = GetInstance().DebugData.SelectedDebugLevel; for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i) { - Puzzles[i].IsActive = GetInstance().DebugData.SelectedDebugLevel == i; + Puzzles[i].IsActive = activeIdx == i; Puzzles[i].Update(); } + Puzzle::PuzzleSolver solver; + bool isPuzzleSolved = solver.IsPuzzleSolved(Puzzles[activeIdx].Data); + PuzzleUI.Update(Puzzles[activeIdx].Data, isPuzzleSolved); + END_PERF(GetShared().Window.PerfCounters, PerfCounterType::GameLevelUpdate, GetShared().Window.FrameCounter); } @@ -369,187 +376,18 @@ namespace Game auto& cover = level.PuzzleTileCovers.Get(CoverHandles[idx]); cover.EData.Visible = false; } + } - UIPlacedCards[i] = level.UIQuads.New(); - auto& quad = level.UIQuads.Get(UIPlacedCards[i]); - quad.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/plane.glb"); - quad.EData.MaterialHandle = EMaterial::UI; - } - for (int32_t i = 0; i < Puzzle::Config::MaxAvailableStacks * WorldPuzzle::UIAvailableCardMaxStackPreview; ++i) - { - UIAvailableCards[i] = level.UIQuads.New(); - auto& quad = level.UIQuads.Get(UIAvailableCards[i]); - quad.EData.MaterialHandle = EMaterial::UI; - quad.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/plane.glb"); - } - SolvedQuad = level.UIQuads.New(); - ResetQuad = level.UIQuads.New(); IsSetup = true; LOG("finished setup!"); } - Vec3 GetMousePosWorld() - { - auto& window = GetShared().Window; - Vec2 mousePos = GetMousePos(); - mousePos.x = mousePos.x / window.WindowWidth; - mousePos.y = mousePos.y / window.WindowHeight; - mousePos *= 2.0f; - mousePos -= 1.0f; - Vec4 mousePosView = {mousePos.x, -mousePos.y, 0.0f, 1.0f}; - Vec4 mousePosCam4 = Mul(GetInstance().Player.ProjectionInverse, mousePosView); - Vec3 mousePosCam = Vec3{ - mousePosCam4.x /= mousePosCam4.w, - mousePosCam4.y /= mousePosCam4.w, - mousePosCam4.z /= mousePosCam4.w, - }; - - return LocalToGlobalPoint(GetInstance().Player.PlayerCamTransform, mousePosCam); - } - - bool IsQuadHovered(Transform& quadTransform, Vec3 mousePosWorld, Vec3& outQuadPlaneIntersectPos) - { - Vec3 quadPosWorld = quadTransform.Position; - Vec3 quadXWorld = LocalToGlobalPoint(quadTransform, {1, 0, 0}); - Vec3 quadZWorld = LocalToGlobalPoint(quadTransform, {0, 0, 1}); - if (RayPlaneIntersect(GetInstance().Player.PlayerCamTransform.Position, - mousePosWorld, - quadPosWorld, - quadXWorld, - quadZWorld, - outQuadPlaneIntersectPos)) - { - Vec3 quadSpaceIntersect = GlobalToLocalPoint(quadTransform, outQuadPlaneIntersectPos); - if (quadSpaceIntersect.x >= -1.0f && quadSpaceIntersect.x <= 1.0f && quadSpaceIntersect.z >= -1.0f && - quadSpaceIntersect.z <= 1.0f) - { - return true; - } - } - return false; - } - void WorldPuzzle::Update() { Level& level = GetInstance().GameLevel; auto& staticCards = Puzzle::GetStaticPuzzleData().Cards; auto& visuals = Puzzle::GetStaticPuzzleData().Visuals; - Transform& camTransform = GetInstance().Player.PlayerCamTransform; - UpdateMatrix(camTransform); - Vec3 cameraPos = camTransform.Position; - - // TODO: disable warning & check if parentheses make sense like this - Vec2 uiOffset = Vec2{static_cast(Data.WidthTiles / Puzzle::Config::CardSize) - 1, - static_cast(Data.HeightTiles / Puzzle::Config::CardSize) - 1}; - uiOffset *= -UICardOffset * 0.5f; - - Transform& boardTransform = level.UIQuads.Get(level.TabletHandle).EData.Transform; - Transform tileOriginTransform = boardTransform; - tileOriginTransform.Position += AxisForward(camTransform.M) * -0.01f; - TranslateLocal(tileOriginTransform, Vec3{uiOffset.x, 0.0f, uiOffset.y} * 1.0f); - UpdateMatrix(tileOriginTransform); - - Vec3 mousePosWorld = GetMousePosWorld(); - Vec3 quadPlaneIntersectPos; - - Puzzle::PuzzleSolver solver; - EntityRenderData solvedData; - solvedData.LoadFromSaved(GetInstance().Player.Config.TabletStatusRenderData); - auto& solvedQuad = level.UIQuads.Get(SolvedQuad); - solvedQuad.EData = solvedData; - solvedQuad.EData.Visible = IsActive; - solvedQuad.EData.TextureHandle = solver.IsPuzzleSolved(Data) - ? GetInstance().Player.Config.TabletStatusSolvedTexture - : solvedData.TextureHandle; - solvedQuad.EData.Transform.Position = tileOriginTransform.Position; - solvedQuad.EData.Transform.Rotation = camTransform.Rotation; - TranslateLocal(solvedQuad.EData.Transform, solvedData.Transform.Position); - Rotate(solvedQuad.EData.Transform, Vec3{bx::kPi * 0.5f, 0.0f, (1.0f - 0 * 0.5f) * bx::kPi}); - - EntityRenderData resetData; - resetData.LoadFromSaved(GetInstance().Player.Config.TabletResetRenderData); - auto& resetQuad = level.UIQuads.Get(ResetQuad); - resetQuad.EData = resetData; - resetQuad.EData.Visible = IsActive; - resetQuad.EData.Transform.Position = tileOriginTransform.Position; - resetQuad.EData.Transform.Rotation = camTransform.Rotation; - TranslateLocal(resetQuad.EData.Transform, resetData.Transform.Position); - Rotate(resetQuad.EData.Transform, Vec3{bx::kPi * 0.5f, 0.0f, (1.0f - 0 * 0.5f) * bx::kPi}); - if (GetMouseButtonPressedNow(MouseButton::Left) && - IsQuadHovered(resetQuad.EData.Transform, mousePosWorld, quadPlaneIntersectPos)) - { - // TODO - } - - // Available Cards - for (int32_t i = 0; i < Puzzle::Config::MaxAvailableStacks; ++i) - { - auto& card = Data.AvailableCards[i]; - for (int32_t j = 0; j < UIAvailableCardMaxStackPreview; j++) - { - auto& quad = level.UIQuads.Get(UIAvailableCards[i * UIAvailableCardMaxStackPreview + j]); - int32_t remaining = (int32_t)card.MaxAvailableCount - (int32_t)card.UsedCount; - if (i < Data.AvailableCardCount && j < remaining) - { - quad.EData.Visible = IsActive; - quad.EData.TextureHandle = Puzzle::IsValid(Data.AvailableCards[i].RefCard) - ? staticCards[Data.AvailableCards[i].RefCard.Idx].BoardTextureHandle - : Gen::TextureHandle{}; - quad.EData.Transform.Position = tileOriginTransform.Position; - quad.EData.Transform.Rotation = camTransform.Rotation; - TranslateLocal(quad.EData.Transform, - Vec3{j * 0.05f + i * 1.2f, 6.0f + (j % 2 == 0 ? 0.02f : 0.0f), j * 0.001f} * - UICardOffset * UICardScale); - Rotate(quad.EData.Transform, Vec3{bx::kPi * 0.5f, 0.0f, (1.0f - 0 * 0.5f) * bx::kPi}); - quad.EData.Transform.Scale = {UICardScale, UICardScale, UICardScale}; - quad.EData.DotColor = {1.0f, 1.0f, 1.0f, 1.0f}; - quad.EData.BaseColor = {1.0f, 1.0f, 1.0f, 1.0f}; - - if (j == 0) - { - if (IsQuadHovered(quad.EData.Transform, mousePosWorld, quadPlaneIntersectPos) && - DraggedAvailableCardIdx == UINT16_MAX && DraggedCard.X == -1 && - GetMouseButtonPressedNow(MouseButton::Left)) - { - DraggedAvailableCardIdx = i; - } - if (DraggedAvailableCardIdx == i) - { - Vec3 dragPos = quadPlaneIntersectPos; - dragPos -= AxisForward(camTransform.M) * 0.01f; - quad.EData.Transform.Position = dragPos; - - Vec3 boardPos = GlobalToLocalPoint(tileOriginTransform, quadPlaneIntersectPos); - Vec3 boardTilePos = boardPos / UICardOffset; - int32_t xPos = (int32_t)bx::round(boardTilePos.x); - int32_t yPos = (int32_t)bx::round(boardTilePos.z); - - if (!GetMouseButton(MouseButton::Left)) - { - if (xPos >= 0 && xPos < Data.WidthTiles / Puzzle::Config::CardSize && yPos >= 0 && - yPos < Data.HeightTiles / Puzzle::Config::CardSize) - { - Gen::PuzPos targetCardPos = {(int8_t)xPos, (int8_t)yPos}; - Gen::PlacedPuzzleCard& targetCard = - Data.PlacedCards[yPos * Puzzle::Config::MaxPuzzleSizeCards + xPos]; - if (!Puzzle::IsValid(targetCard.RefCard) || targetCard.RefCard.Idx == 0) - { - Puzzle::DragAvailableCardTo(Data, targetCardPos, DraggedAvailableCardIdx, 0); - } - } - DraggedAvailableCardIdx = UINT16_MAX; - } - } - } - } - else - { - quad.EData.Visible = false; - } - } - } - // Board for (int8_t y = 0; y < Data.HeightTiles / Puzzle::Config::CardSize; ++y) { @@ -558,7 +396,6 @@ namespace Game int32_t cardIdx = y * Puzzle::Config::MaxPuzzleSizeCards + x; Gen::PlacedPuzzleCard& card = Data.PlacedCards[cardIdx]; auto& tile = level.PuzzleTiles.Get(TileHandles[cardIdx]); - auto& quad = level.UIQuads.Get(UIPlacedCards[cardIdx]); bool isValid = Puzzle::IsValid(card.RefCard); auto& staticCard = isValid ? staticCards[card.RefCard.Idx] : staticCards[0]; @@ -597,96 +434,6 @@ namespace Game Gen::RotateLocal(cover.EData.Transform, Gen::EulerFromRotation(model.Sockets[i].Rot)); } } - - // UI Quad - quad.EData.Visible = isValid && IsActive; - quad.EData.TextureHandle = - isValid ? staticCards[card.RefCard.Idx].BoardTextureHandle : Gen::TextureHandle{}; - quad.EData.DotColor = card.IsLocked ? Puzzle::GetStaticPuzzleData().Visuals.DisabledCardTint - : Vec4{1.0f, 1.0f, 1.0f, 1.0f}; - - quad.EData.Transform.Position = tileOriginTransform.Position; - quad.EData.Transform.Rotation = camTransform.Rotation; - TranslateLocal(quad.EData.Transform, - Vec3{(float)card.Position.X, (float)card.Position.Y, 0.0f} * UICardOffset * UICardScale); - Rotate(quad.EData.Transform, Vec3{bx::kPi * 0.5f, 0.0f, (1.0f - card.Rotation * 0.5f) * bx::kPi}); - quad.EData.Transform.Scale = {UICardScale, UICardScale, UICardScale}; - - Vec3 quadPlaneIntersectPos; - if (isValid && IsQuadHovered(quad.EData.Transform, mousePosWorld, quadPlaneIntersectPos)) - { - if (!card.IsLocked && DraggedCard.X == -1 && DraggedAvailableCardIdx == UINT16_MAX) - { - if (GetMouseButtonPressedNow(MouseButton::Left)) - { - DraggedCard.X = x; - DraggedCard.Y = y; - } - if (GetMouseButtonPressedNow(MouseButton::Right)) - { - Puzzle::RotateCard(card); - } - } - } - if (DraggedCard.X == x && DraggedCard.Y == y) - { - Vec3 dragPos = quadPlaneIntersectPos; - dragPos -= AxisForward(camTransform.M) * 0.01f; - quad.EData.Transform.Position = dragPos; - - Vec3 boardPos = GlobalToLocalPoint(tileOriginTransform, quadPlaneIntersectPos); - Vec3 boardTilePos = boardPos / UICardOffset; - int32_t xPos = (int32_t)bx::round(boardTilePos.x); - int32_t yPos = (int32_t)bx::round(boardTilePos.z); - Gen::PuzPos srcCardPos = {(int8_t)DraggedCard.X, (int8_t)DraggedCard.Y}; - Gen::PlacedPuzzleCard& srcCard = - Data.PlacedCards[srcCardPos.Y * Puzzle::Config::MaxPuzzleSizeCards + srcCardPos.X]; - - if (GetMouseButtonPressedNow(MouseButton::Right)) - { - Puzzle::RotateCard(srcCard); - } - - if (!GetMouseButton(MouseButton::Left)) - { - Gen::PuzPos targetCardPos = {(int8_t)xPos, (int8_t)yPos}; - if (xPos >= 0 && xPos < Data.WidthTiles / Puzzle::Config::CardSize && yPos >= 0 && - yPos < Data.HeightTiles / Puzzle::Config::CardSize) - { - PlacedPuzzleCard srcCardCopy = srcCard; - Gen::PlacedPuzzleCard& targetCard = - Data.PlacedCards[yPos * Puzzle::Config::MaxPuzzleSizeCards + xPos]; - bool canBeReplaced = !Puzzle::IsValid(targetCard.RefCard) || targetCard.RefCard.Idx == 0; - if (canBeReplaced && Puzzle::ReturnPlacedCard(Data, srcCardPos)) - { - int32_t foundIdx = -1; - for (int32_t availCardIdx = 0; availCardIdx < Data.AvailableCardCount; ++availCardIdx) - { - LOG("CHECK: %u", Data.AvailableCards[availCardIdx].RefCard.Idx); - if (Data.AvailableCards[availCardIdx].RefCard.Idx == srcCardCopy.RefCard.Idx) - { - foundIdx = availCardIdx; - break; - } - } - if (foundIdx >= 0) - { - Puzzle::DragAvailableCardTo(Data, targetCardPos, foundIdx, srcCard.Rotation); - } - else - { - LOG_ERROR("NOTFOUND: %u", srcCardCopy.RefCard.Idx); - } - } - } - else - { - Puzzle::ReturnPlacedCard(Data, srcCardPos); - } - DraggedCard.X = -1; - DraggedCard.Y = -1; - } - } } } } diff --git a/src/game/Level.h b/src/game/Level.h index eafef22..4399769 100644 --- a/src/game/Level.h +++ b/src/game/Level.h @@ -1,166 +1,21 @@ #pragma once + #include "../engine/Shared.h" -#include "Log.h" #include "Puzzle.h" +#include "UI.h" #include "rendering/Rendering.h" #include #include - -#define ENTITY_HANDLE(X) \ - struct X \ - { \ - uint16_t Idx = UINT16_MAX; \ - }; \ - inline bool IsValid(X h) \ - { \ - return h.Idx != UINT16_MAX; \ - } - namespace Game { - struct EntityRenderData - { - Gen::Vec4 DotColor{1.0f, 1.0f, 1.0f, 1.0f}; - Gen::Vec4 BaseColor{0.0f, 0.0f, 0.0f, 1.0f}; - Gen::Transform Transform; - Gen::EMaterial::Enum MaterialHandle = Gen::EMaterial::UI; - Gen::TextureHandle TextureHandle; - Gen::ModelHandle ModelH; - bool Visible = true; - - void Render(const Model* models, const Material* materials, const Texture* textures); - void LoadFromSaved(const Gen::SavedEntityRenderData& saved); - }; - - ENTITY_HANDLE(CubeHandle); - struct Cube - { - int32_t TestX = -1; - int32_t TestY = -1; - EntityRenderData EData; - - void Setup(); - void Update(); - }; - - ENTITY_HANDLE(TestEntityHandle); - struct TestEntity - { - EntityRenderData EData; - - void Setup(); - }; - - ENTITY_HANDLE(PuzzleTileEntityHandle); - struct PuzzleTileEntity - { - EntityRenderData EData; - }; - - ENTITY_HANDLE(PuzzleTileCoverHandle); - struct PuzzleTileCover - { - EntityRenderData EData; - }; - - ENTITY_HANDLE(UIQuadEntityHandle); - struct UIQuadEntity - { - EntityRenderData EData; - }; - - ENTITY_HANDLE(LevelEntityHandle); - struct LevelEntity - { - EntityRenderData EData; - }; - - class IEntityManager - { - public: - virtual bool Setup(uint8_t*& ptr, bool forceReset) = 0; - }; - - template class EntityManager : public IEntityManager - { - public: - uint16_t Count = 0; - T* Data = nullptr; - uint32_t EntitySize = 0; - T InvalidObject{}; - bool IsEnabled = true; - - public: - // Returns true if size changed - bool Setup(uint8_t*& ptr, bool forceReset) - { - bool changed = false; - if (EntitySize != sizeof(T) || forceReset) - { - Count = 0; - changed = true; - } - EntitySize = sizeof(T); - Data = reinterpret_cast(ptr); - ptr += C * EntitySize; - return changed; - } - - HandleT New() - { - if (Data == nullptr) - { - ERROR_ONCE("Accessed EntityManager before setup!"); - return {}; - } - if (Count >= C) - { - ERROR_ONCE("Too many entities!"); - return {}; - } - Data[Count] = {}; - HandleT H; - H.Idx = Count; - ++Count; - return H; - } - - T& Get(HandleT handle) - { - if (handle.Idx >= Count) - { - ERROR_ONCE("OOB Access!"); - return InvalidObject; - } - return Data[handle.Idx]; - } - - void Render(const Model* models, const Material* materials, const Texture* textures) - { - if (!IsEnabled) return; - for (uint16_t i = 0; i < Count; ++i) - { - Get({i}).EData.Render(models, materials, textures); - } - } - }; struct WorldPuzzle { static constexpr Gen::Vec2 WorldCardSize{10.0f, 10.0f}; - static constexpr float UICardScale = 0.05f; - static constexpr float UICardOffset = 2.1f * UICardScale; - static constexpr int32_t UIAvailableCardMaxStackPreview = 3; Gen::PuzzleData Data; Gen::Vec3 WorldPosition; PuzzleTileEntityHandle TileHandles[Puzzle::Config::MaxCardsInPuzzle]; PuzzleTileCoverHandle CoverHandles[Puzzle::Config::MaxCardsInPuzzle * Puzzle::Config::MaxCoversInTile]; - UIQuadEntityHandle UIPlacedCards[Puzzle::Config::MaxCardsInPuzzle]; - UIQuadEntityHandle UIAvailableCards[Puzzle::Config::MaxAvailableStacks * UIAvailableCardMaxStackPreview]; - UIQuadEntityHandle SolvedQuad; - UIQuadEntityHandle ResetQuad; - Gen::PuzPos DraggedCard{-1, -1}; - uint16_t DraggedAvailableCardIdx = UINT16_MAX; bool IsSetup = false; bool IsActive = false; @@ -176,7 +31,7 @@ namespace Game EntityManager Tests; EntityManager PuzzleTiles; EntityManager PuzzleTileCovers; - EntityManager UIQuads; + UIQuadEntityManager UIQuads; EntityManager LevelEntities; CubeHandle PlayerOutsideViewCube; @@ -185,6 +40,7 @@ namespace Game public: Gen::StaticPuzzleData PuzzleData; WorldPuzzle Puzzles[Puzzle::Config::MaxVisiblePuzzles]; + WorldPuzzleUI PuzzleUI; public: void Setup(GameData& data); diff --git a/src/game/Tools.cpp b/src/game/Tools.cpp index 136abf2..6bf87ee 100644 --- a/src/game/Tools.cpp +++ b/src/game/Tools.cpp @@ -306,6 +306,8 @@ namespace Tools ImGui::Text("Status"); bTabletChanged |= Tools::EntityDataSettings(player.Config.TabletStatusRenderData); bTabletChanged |= Tools::TextureDropdown(player.Config.TabletStatusSolvedTexture, "Solved Texture"); + bTabletChanged |= + Tools::TextureDropdown(player.Config.TabletStatusNotSolvedTexture, "Not Solved Texture"); ImGui::Text("Reset"); bTabletChanged |= Tools::EntityDataSettings(player.Config.TabletResetRenderData); if (bTabletChanged) @@ -314,6 +316,7 @@ namespace Tools s.Init("game/data/static/uiconfig.dat", "UICO"); s.WriteT(player.Config); s.Finish(); + level.PuzzleUI.Reset(); } ImGui::Separator(); diff --git a/src/game/UI.cpp b/src/game/UI.cpp new file mode 100644 index 0000000..1fc09e6 --- /dev/null +++ b/src/game/UI.cpp @@ -0,0 +1,333 @@ +#include "UI.h" + +#include "Gen.h" +#include "Global.h" +#include "Input.h" +#include "Instance.h" +#include "Level.h" + +#include "bx/math.h" + +using namespace Gen; + +namespace +{ + Game::StaticUIData StaticData; +} + +namespace Game +{ + UIQuadEntityHandle NewQuad(UIQuadEntityManager& manager, const Gen::SavedEntityRenderData& loadData) + { + UIQuadEntityHandle h = manager.New(); + if (!IsValid(h)) return h; + + UIQuadEntity& entity = manager.Get(h); + entity.EData.LoadFromSaved(loadData); + entity.UIPos = entity.EData.Transform.Position; + return h; + } + + void UpdateQuad(UIQuadEntityManager& manager, UIQuadEntityHandle handle) + { + if (!IsValid(handle)) return; + + UIQuadEntity& entity = manager.Get(handle); + entity.EData.Transform.Position = StaticData.UITransform.Position; + entity.EData.Transform.Rotation = StaticData.UITransform.Rotation; + TranslateLocal(entity.EData.Transform, entity.UIPos); + Rotate(entity.EData.Transform, Vec3{bx::kPi * 0.5f, 0.0f, (1.0f - 0 * 0.5f) * bx::kPi}); + Rotate(entity.EData.Transform, {0.0f, entity.UIRot, 0.0f}); + } + + Vec3 GetMousePosWorld() + { + auto& window = GetShared().Window; + Vec2 mousePos = GetMousePos(); + mousePos.x = mousePos.x / window.WindowWidth; + mousePos.y = mousePos.y / window.WindowHeight; + mousePos *= 2.0f; + mousePos -= 1.0f; + Vec4 mousePosView = {mousePos.x, -mousePos.y, 0.0f, 1.0f}; + Vec4 mousePosCam4 = Mul(GetInstance().Player.ProjectionInverse, mousePosView); + Vec3 mousePosCam = Vec3{ + mousePosCam4.x /= mousePosCam4.w, + mousePosCam4.y /= mousePosCam4.w, + mousePosCam4.z /= mousePosCam4.w, + }; + + return LocalToGlobalPoint(GetInstance().Player.PlayerCamTransform, mousePosCam); + } + + bool IsQuadHovered(Transform& quadTransform, Vec3 mousePosWorld, Vec3& outQuadPlaneIntersectPos) + { + Vec3 quadPosWorld = quadTransform.Position; + Vec3 quadXWorld = LocalToGlobalPoint(quadTransform, {1, 0, 0}); + Vec3 quadZWorld = LocalToGlobalPoint(quadTransform, {0, 0, 1}); + if (RayPlaneIntersect(GetInstance().Player.PlayerCamTransform.Position, + mousePosWorld, + quadPosWorld, + quadXWorld, + quadZWorld, + outQuadPlaneIntersectPos)) + { + Vec3 quadSpaceIntersect = GlobalToLocalPoint(quadTransform, outQuadPlaneIntersectPos); + if (quadSpaceIntersect.x >= -1.0f && quadSpaceIntersect.x <= 1.0f && quadSpaceIntersect.z >= -1.0f && + quadSpaceIntersect.z <= 1.0f) + { + return true; + } + } + return false; + } + + void WorldPuzzleUI::Setup() + { + auto& level = GetInstance().GameLevel; + SolvedQuad = NewQuad(level.UIQuads, GetInstance().Player.Config.TabletStatusRenderData); + ResetQuad = NewQuad(level.UIQuads, GetInstance().Player.Config.TabletResetRenderData); + + for (int32_t i = 0; i < Puzzle::Config::MaxCardsInPuzzle; ++i) + { + UIPlacedCards[i] = level.UIQuads.New(); + auto& quad = level.UIQuads.Get(UIPlacedCards[i]); + quad.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/plane.glb"); + quad.EData.MaterialHandle = EMaterial::UI; + } + for (int32_t i = 0; i < Puzzle::Config::MaxAvailableStacks * UIAvailableCardMaxStackPreview; ++i) + { + UIAvailableCards[i] = level.UIQuads.New(); + auto& quad = level.UIQuads.Get(UIAvailableCards[i]); + quad.EData.MaterialHandle = EMaterial::UI; + quad.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/plane.glb"); + } + } + + void WorldPuzzleUI::UpdateAvailableCards(Gen::PuzzleData& Data) + { + auto& level = GetInstance().GameLevel; + auto& staticCards = Puzzle::GetStaticPuzzleData().Cards; + + Vec3 quadPlaneIntersectPos; + Vec3 mousePosWorld = GetMousePosWorld(); + + for (int32_t i = 0; i < Puzzle::Config::MaxAvailableStacks; ++i) + { + auto& card = Data.AvailableCards[i]; + for (int32_t j = 0; j < UIAvailableCardMaxStackPreview; j++) + { + auto h = UIAvailableCards[i * UIAvailableCardMaxStackPreview + j]; + auto& quad = level.UIQuads.Get(h); + int32_t remaining = (int32_t)card.MaxAvailableCount - (int32_t)card.UsedCount; + if (i < Data.AvailableCardCount && j < remaining) + { + quad.UIPos = Vec3{j * 0.05f + i * 1.2f, 4.2f + (j % 2 == 0 ? 0.02f : 0.0f), j * 0.001f} * + UICardOffset * UICardScale; + UpdateQuad(level.UIQuads, h); + quad.EData.Visible = true; + quad.EData.TextureHandle = Puzzle::IsValid(Data.AvailableCards[i].RefCard) + ? staticCards[Data.AvailableCards[i].RefCard.Idx].BoardTextureHandle + : Gen::TextureHandle{}; + quad.EData.Transform.Scale = {UICardScale, UICardScale, UICardScale}; + quad.EData.DotColor = {1.0f, 1.0f, 1.0f, 1.0f}; + quad.EData.BaseColor = {1.0f, 1.0f, 1.0f, 1.0f}; + + if (j == 0) + { + if (IsQuadHovered(quad.EData.Transform, mousePosWorld, quadPlaneIntersectPos) && + DraggedAvailableCardIdx == UINT16_MAX && DraggedCard.X == -1 && + GetMouseButtonPressedNow(MouseButton::Left)) + { + DraggedAvailableCardIdx = i; + } + if (DraggedAvailableCardIdx == i) + { + Vec3 dragPos = quadPlaneIntersectPos; + dragPos += StaticData.ZAxis * -0.01f; + quad.EData.Transform.Position = dragPos; + + Vec3 boardPos = GlobalToLocalPoint(StaticData.UITransform, quadPlaneIntersectPos); + Vec3 boardTilePos = boardPos / UICardOffset; + int32_t xPos = (int32_t)bx::round(boardTilePos.x); + int32_t yPos = (int32_t)bx::round(boardTilePos.y); + + if (!GetMouseButton(MouseButton::Left)) + { + if (xPos >= 0 && xPos < Data.WidthTiles / Puzzle::Config::CardSize && yPos >= 0 && + yPos < Data.HeightTiles / Puzzle::Config::CardSize) + { + Gen::PuzPos targetCardPos = {(int8_t)xPos, (int8_t)yPos}; + Gen::PlacedPuzzleCard& targetCard = + Data.PlacedCards[yPos * Puzzle::Config::MaxPuzzleSizeCards + xPos]; + if (!Puzzle::IsValid(targetCard.RefCard) || targetCard.RefCard.Idx == 0) + { + Puzzle::DragAvailableCardTo(Data, targetCardPos, DraggedAvailableCardIdx, 0); + } + } + DraggedAvailableCardIdx = UINT16_MAX; + } + } + } + } + else + { + quad.EData.Visible = false; + } + } + } + } + + void WorldPuzzleUI::UpdateBoardCards(Gen::PuzzleData& Data) + { + auto& level = GetInstance().GameLevel; + auto& staticCards = Puzzle::GetStaticPuzzleData().Cards; + Vec3 quadPlaneIntersectPos; + + for (int8_t y = 0; y < Data.HeightTiles / Puzzle::Config::CardSize; ++y) + { + for (int8_t x = 0; x < Data.WidthTiles / Puzzle::Config::CardSize; ++x) + { + // UI Quad + int32_t cardIdx = y * Puzzle::Config::MaxPuzzleSizeCards + x; + PlacedPuzzleCard& card = Data.PlacedCards[cardIdx]; + bool isValid = Puzzle::IsValid(card.RefCard); + + auto& quad = level.UIQuads.Get(UIPlacedCards[cardIdx]); + quad.UIPos = Vec3{(float)card.Position.X, (float)card.Position.Y, 0.0f} * UICardOffset * UICardScale; + quad.UIRot = card.Rotation * bx::kPi * 0.5f; + UpdateQuad(level.UIQuads, UIPlacedCards[cardIdx]); + + quad.EData.Visible = isValid; + quad.EData.TextureHandle = + isValid ? staticCards[card.RefCard.Idx].BoardTextureHandle : Gen::TextureHandle{}; + quad.EData.DotColor = card.IsLocked ? Puzzle::GetStaticPuzzleData().Visuals.DisabledCardTint + : Vec4{1.0f, 1.0f, 1.0f, 1.0f}; + quad.EData.Transform.Scale = {UICardScale, UICardScale, UICardScale}; + + if (isValid && IsQuadHovered(quad.EData.Transform, StaticData.MousePosWorld, quadPlaneIntersectPos)) + { + if (!card.IsLocked && DraggedCard.X == -1 && DraggedAvailableCardIdx == UINT16_MAX) + { + if (GetMouseButtonPressedNow(MouseButton::Left)) + { + DraggedCard.X = x; + DraggedCard.Y = y; + } + if (GetMouseButtonPressedNow(MouseButton::Right)) + { + Puzzle::RotateCard(card); + } + } + } + + if (DraggedCard.X == x && DraggedCard.Y == y) + { + Vec3 dragPos = quadPlaneIntersectPos; + dragPos += StaticData.ZAxis * -0.01f; + quad.EData.Transform.Position = dragPos; + + Vec3 boardPos = GlobalToLocalPoint(StaticData.UITransform, quadPlaneIntersectPos); + Vec3 boardTilePos = boardPos / UICardOffset; + int32_t xPos = (int32_t)bx::round(boardTilePos.x); + int32_t yPos = (int32_t)bx::round(boardTilePos.y); + Gen::PuzPos srcCardPos = {(int8_t)DraggedCard.X, (int8_t)DraggedCard.Y}; + Gen::PlacedPuzzleCard& srcCard = + Data.PlacedCards[srcCardPos.Y * Puzzle::Config::MaxPuzzleSizeCards + srcCardPos.X]; + + if (GetMouseButtonPressedNow(MouseButton::Right)) + { + Puzzle::RotateCard(srcCard); + } + + if (!GetMouseButton(MouseButton::Left)) + { + Gen::PuzPos targetCardPos = {(int8_t)xPos, (int8_t)yPos}; + if (xPos >= 0 && xPos < Data.WidthTiles / Puzzle::Config::CardSize && yPos >= 0 && + yPos < Data.HeightTiles / Puzzle::Config::CardSize) + { + PlacedPuzzleCard srcCardCopy = srcCard; + Gen::PlacedPuzzleCard& targetCard = + Data.PlacedCards[yPos * Puzzle::Config::MaxPuzzleSizeCards + xPos]; + bool canBeReplaced = !Puzzle::IsValid(targetCard.RefCard) || targetCard.RefCard.Idx == 0; + if (canBeReplaced && Puzzle::ReturnPlacedCard(Data, srcCardPos)) + { + int32_t foundIdx = -1; + for (int32_t availCardIdx = 0; availCardIdx < Data.AvailableCardCount; ++availCardIdx) + { + if (Data.AvailableCards[availCardIdx].RefCard.Idx == srcCardCopy.RefCard.Idx) + { + foundIdx = availCardIdx; + break; + } + } + if (foundIdx >= 0) + { + Puzzle::DragAvailableCardTo(Data, targetCardPos, foundIdx, srcCard.Rotation); + } + else + { + LOG_ERROR("NOTFOUND: %u", srcCardCopy.RefCard.Idx); + } + } + } + else + { + Puzzle::ReturnPlacedCard(Data, srcCardPos); + } + DraggedCard.X = -1; + DraggedCard.Y = -1; + } + } + } + } + } + + void WorldPuzzleUI::Update(Gen::PuzzleData& Data, bool IsPuzzleSolved) + { + auto& level = GetInstance().GameLevel; + + Transform& camTransform = GetInstance().Player.PlayerCamTransform; + UpdateMatrix(camTransform); + + StaticData.UITransform = level.UIQuads.Get(level.TabletHandle).EData.Transform; + StaticData.UITransform.Rotation = camTransform.Rotation; + StaticData.ZAxis = AxisForward(StaticData.UITransform.M); + StaticData.UITransform.Position += StaticData.ZAxis * -0.01f; + StaticData.MousePosWorld = GetMousePosWorld(); + + // TODO: disable warning & check if parentheses make sense like this + Vec2 uiOffset = Vec2{static_cast(Data.WidthTiles / Puzzle::Config::CardSize) - 1, + static_cast(Data.HeightTiles / Puzzle::Config::CardSize) - 1}; + uiOffset *= -UICardOffset * 0.5f; + + Transform tileOriginTransform = StaticData.UITransform; + tileOriginTransform.Position += AxisForward(StaticData.UITransform.M) * -1.0f; + TranslateLocal(tileOriginTransform, Vec3{uiOffset.x, 0.0f, uiOffset.y} * 1.0f); + UpdateMatrix(tileOriginTransform); + + auto& solvedQuad = level.UIQuads.Get(SolvedQuad); + solvedQuad.EData.Visible = true; + solvedQuad.EData.TextureHandle = IsPuzzleSolved ? GetInstance().Player.Config.TabletStatusSolvedTexture + : GetInstance().Player.Config.TabletStatusNotSolvedTexture; + UpdateQuad(level.UIQuads, SolvedQuad); + auto& resetQuad = level.UIQuads.Get(ResetQuad); + resetQuad.EData.Visible = true; + UpdateQuad(level.UIQuads, ResetQuad); + + Vec3 hoverPosWorld; + if (GetMouseButtonPressedNow(MouseButton::Left) && + IsQuadHovered(resetQuad.EData.Transform, StaticData.MousePosWorld, hoverPosWorld)) + { + LOG_WARN("TODO!"); + // TODO + } + + UpdateAvailableCards(Data); + UpdateBoardCards(Data); + } + + void WorldPuzzleUI::Reset() + { + auto& level = GetInstance().GameLevel; + } +} // namespace Game diff --git a/src/game/UI.h b/src/game/UI.h new file mode 100644 index 0000000..69058da --- /dev/null +++ b/src/game/UI.h @@ -0,0 +1,41 @@ +#pragma once + +#include "../gen/Generated.h" +#include "Entity.h" +#include "Puzzle.h" + +namespace Game +{ + struct StaticUIData + { + Gen::Transform UITransform; + Gen::Vec3 ZAxis; + Gen::Vec3 MousePosWorld; + }; + + struct WorldPuzzleUI + { + static constexpr float UICardScale = 0.05f; + static constexpr float UICardOffset = 2.1f * UICardScale; + static constexpr int32_t UIAvailableCardMaxStackPreview = 3; + + UIQuadEntityHandle SolvedQuad; + UIQuadEntityHandle ResetQuad; + + UIQuadEntityHandle UIPlacedCards[Puzzle::Config::MaxCardsInPuzzle]; + UIQuadEntityHandle UIAvailableCards[Puzzle::Config::MaxAvailableStacks * UIAvailableCardMaxStackPreview]; + Gen::PuzPos DraggedCard{-1, -1}; + uint16_t DraggedAvailableCardIdx = UINT16_MAX; + + void Setup(); + void UpdateAvailableCards(Gen::PuzzleData& Data); + void UpdateBoardCards(Gen::PuzzleData& Data); + void Update(Gen::PuzzleData& Data, bool IsPuzzleSolved); + void Reset(); + }; + + UIQuadEntityHandle NewQuad(UIQuadEntityManager& manager, const Gen::SavedEntityRenderData& loadData); + void UpdateQuad(UIQuadEntityManager& manager, UIQuadEntityHandle handle); + Gen::Vec3 GetMousePosWorld(); + bool IsQuadHovered(Gen::Transform& quadTransform, Gen::Vec3 mousePosWorld, Gen::Vec3& outQuadPlaneIntersectPos); +} // namespace Game diff --git a/src/game/data/static/uiconfig.dat b/src/game/data/static/uiconfig.dat index cb1aba7d2fda3ae62aac05bbc3821a421aadb899..fdb8cdd253d090aa00368104ed5bfdb3505a1943 100644 GIT binary patch delta 159 zcmX@+{lZ%*)YI9Yk%5tcfx%Q|#cc)#J_Zd128Mbj1_lRah}c9W2X0mdkQyciCI-gI zf$Tp7n4}ownZy`OnPeCOnIsvqCKob(+?>WFt03T)UlN?3Q)Kq7w^0Z%$*9RoFaV=`o Id|Q(objBasePtr + WriteDestinations[j]); + isOk = Load(fieldPtr, 1, serializer) && isOk; + continue; + } if (bx::strCmp(memberName, "TabletStatusSolvedTexture") == 0) { auto* fieldPtr = reinterpret_cast(objBasePtr + WriteDestinations[j]); diff --git a/src/gen/Generated.h b/src/gen/Generated.h index 40bb742..f4bff64 100644 --- a/src/gen/Generated.h +++ b/src/gen/Generated.h @@ -224,6 +224,7 @@ namespace Gen static constexpr uint16_t TypeIdx = 31; SavedEntityRenderData TabletBackgroundRenderData = {}; SavedEntityRenderData TabletStatusRenderData = {}; + TextureHandle TabletStatusNotSolvedTexture = {}; TextureHandle TabletStatusSolvedTexture = {}; SavedEntityRenderData TabletResetRenderData = {}; }; @@ -351,11 +352,11 @@ namespace Gen TypeDef{sizeof(PlacedPuzzleCard), 3555575973, "PlacedPuzzleCard", 4, {24, 21, 4, 8}, {0, 0, 0, 0}, {{313, 7}, {320, 8}, {328, 8}, {336, 8}}}, TypeDef{sizeof(PuzzleData), 3349686056, "PuzzleData", 10, {5, 11, 4, 4, 6, 27, 28, 32, 6, 21}, {0, 64, 0, 0, 0, 16, 256, 1024, 0, 16}, {{344, 2}, {346, 10}, {356, 10}, {366, 11}, {377, 18}, {395, 14}, {409, 11}, {420, 15}, {435, 17}, {452, 13}}}, TypeDef{sizeof(SavedEntityRenderData), 3172756855, "SavedEntityRenderData", 7, {14, 14, 17, 33, 20, 19, 8}, {0, 0, 0, 0, 0, 0, 0}, {{465, 9}, {474, 14}, {488, 2}, {490, 8}, {498, 7}, {505, 5}, {510, 7}}}, - TypeDef{sizeof(SavedPlayerConfig), 2457294139, "SavedPlayerConfig", 4, {30, 30, 20, 30}, {0, 0, 0, 0}, {{517, 26}, {543, 22}, {565, 25}, {590, 21}}}, + TypeDef{sizeof(SavedPlayerConfig), 3685229621, "SavedPlayerConfig", 5, {30, 30, 20, 20, 30}, {0, 0, 0, 0, 0}, {{517, 26}, {543, 22}, {565, 28}, {593, 25}, {618, 21}}}, TypeDef{sizeof(PuzzleElementType::Enum), 2983807453, "PuzzleElementType", 0, {}, {}, {}}, TypeDef{sizeof(EMaterial::Enum), 2024002654, "EMaterial", 0, {}, {}, {}}, }; - char MemberNameBuffer[64*64*64]{"xyxyzxyzwMMMMIPositionRotationScaleIdxModelIdxAssetTextureIdxAssetXYModelConnectionDirectionElementsBaseModelHandleNorthCoverHandleEastCoverHandleSouthCoverHandleWestCoverHandleSocketsModelTextureHandleBoardTextureHandleIdxTileBaseColorTileDotColorTestDisabledCardTintCardsVisualsRefCardMaxAvailableCountUsedCountRefCardPositionRotationIsLockedIDPuzzleNameWidthTilesHeightTilesAvailableCardCountAvailableCardsPlacedCardsBackgroundTilesGoalPositionCountGoalPositionsBaseColorHighlightColorTFMaterialTextureModelVisibleTabletBackgroundRenderDataTabletStatusRenderDataTabletStatusSolvedTextureTabletResetRenderData"}; + char MemberNameBuffer[64*64*64]{"xyxyzxyzwMMMMIPositionRotationScaleIdxModelIdxAssetTextureIdxAssetXYModelConnectionDirectionElementsBaseModelHandleNorthCoverHandleEastCoverHandleSouthCoverHandleWestCoverHandleSocketsModelTextureHandleBoardTextureHandleIdxTileBaseColorTileDotColorTestDisabledCardTintCardsVisualsRefCardMaxAvailableCountUsedCountRefCardPositionRotationIsLockedIDPuzzleNameWidthTilesHeightTilesAvailableCardCountAvailableCardsPlacedCardsBackgroundTilesGoalPositionCountGoalPositionsBaseColorHighlightColorTFMaterialTextureModelVisibleTabletBackgroundRenderDataTabletStatusRenderDataTabletStatusNotSolvedTextureTabletStatusSolvedTextureTabletResetRenderData"}; }; constexpr MetadataTable Metadata;