This commit is contained in:
Asuro
2025-05-21 02:48:36 +02:00
parent 15dc65530d
commit bd2962fc38
10 changed files with 557 additions and 412 deletions

151
src/game/Entity.h Normal file
View File

@@ -0,0 +1,151 @@
#pragma once
#include "../gen/Generated.h"
#include "Log.h"
#include "Puzzle.h" // TODO: remove
#include "rendering/Rendering.h"
#include <cstdint>
#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 <typename T, typename HandleT, uint32_t C> 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<T*>(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<UIQuadEntity, UIQuadEntityHandle, Puzzle::Config::MaxTilesInPuzzle * 2> UIQuadEntityManager;
} // namespace Game

View File

@@ -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<float>(Data.WidthTiles / Puzzle::Config::CardSize) - 1,
static_cast<float>(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;
}
}
}
}
}

View File

@@ -1,166 +1,21 @@
#pragma once
#include "../engine/Shared.h"
#include "Log.h"
#include "Puzzle.h"
#include "UI.h"
#include "rendering/Rendering.h"
#include <bgfx/bgfx.h>
#include <cstdint>
#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 <typename T, typename HandleT, uint32_t C> 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<T*>(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<TestEntity, TestEntityHandle, 32> Tests;
EntityManager<PuzzleTileEntity, PuzzleTileEntityHandle, Puzzle::Config::MaxTilesTotal> PuzzleTiles;
EntityManager<PuzzleTileCover, PuzzleTileCoverHandle, Puzzle::Config::MaxCoversTotal> PuzzleTileCovers;
EntityManager<UIQuadEntity, UIQuadEntityHandle, Puzzle::Config::MaxTilesInPuzzle * 2> UIQuads;
UIQuadEntityManager UIQuads;
EntityManager<LevelEntity, LevelEntityHandle, 64> 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);

View File

@@ -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();

333
src/game/UI.cpp Normal file
View File

@@ -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<float>(Data.WidthTiles / Puzzle::Config::CardSize) - 1,
static_cast<float>(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

41
src/game/UI.h Normal file
View File

@@ -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

Binary file not shown.

View File

@@ -170,6 +170,7 @@ type SavedPlayerConfig
{
SavedEntityRenderData TabletBackgroundRenderData
SavedEntityRenderData TabletStatusRenderData
TextureHandle TabletStatusNotSolvedTexture
TextureHandle TabletStatusSolvedTexture
SavedEntityRenderData TabletResetRenderData
}

View File

@@ -2452,6 +2452,7 @@ namespace Gen
{
isOk = Save(&obj[i].TabletBackgroundRenderData, 1, serializer) && isOk;
isOk = Save(&obj[i].TabletStatusRenderData, 1, serializer) && isOk;
isOk = Save(&obj[i].TabletStatusNotSolvedTexture, 1, serializer) && isOk;
isOk = Save(&obj[i].TabletStatusSolvedTexture, 1, serializer) && isOk;
isOk = Save(&obj[i].TabletResetRenderData, 1, serializer) && isOk;
}
@@ -2472,6 +2473,7 @@ namespace Gen
{
isOk = Load(&obj[i].TabletBackgroundRenderData, 1, serializer) && isOk;
isOk = Load(&obj[i].TabletStatusRenderData, 1, serializer) && isOk;
isOk = Load(&obj[i].TabletStatusNotSolvedTexture, 1, serializer) && isOk;
isOk = Load(&obj[i].TabletStatusSolvedTexture, 1, serializer) && isOk;
isOk = Load(&obj[i].TabletResetRenderData, 1, serializer) && isOk;
}
@@ -2509,6 +2511,10 @@ namespace Gen
{
WriteDestinations[i] = offsetof(SavedPlayerConfig, TabletStatusRenderData);
}
if (bx::strCmp(memberName, "TabletStatusNotSolvedTexture") == 0 && bx::strCmp(memberTypeName, "TextureHandle") == 0)
{
WriteDestinations[i] = offsetof(SavedPlayerConfig, TabletStatusNotSolvedTexture);
}
if (bx::strCmp(memberName, "TabletStatusSolvedTexture") == 0 && bx::strCmp(memberTypeName, "TextureHandle") == 0)
{
WriteDestinations[i] = offsetof(SavedPlayerConfig, TabletStatusSolvedTexture);
@@ -2549,6 +2555,12 @@ namespace Gen
isOk = Load(fieldPtr, 1, serializer) && isOk;
continue;
}
if (bx::strCmp(memberName, "TabletStatusNotSolvedTexture") == 0)
{
auto* fieldPtr = reinterpret_cast<TextureHandle*>(objBasePtr + WriteDestinations[j]);
isOk = Load(fieldPtr, 1, serializer) && isOk;
continue;
}
if (bx::strCmp(memberName, "TabletStatusSolvedTexture") == 0)
{
auto* fieldPtr = reinterpret_cast<TextureHandle*>(objBasePtr + WriteDestinations[j]);

View File

@@ -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;