new puzzle tool

This commit is contained in:
Till W
2025-05-25 13:07:52 +02:00
parent 410f401aef
commit cac2ef3330
12 changed files with 275 additions and 122 deletions

View File

@@ -52,10 +52,10 @@ namespace Game
struct InstanceDebugData
{
uint16_t SelectedDebugLevel = UINT16_MAX;
static constexpr uint32_t MaxAssets = 128;
uint16_t SelectedDebugLevel = 0;
uint64_t ImguiIniSize = 0;
char ImguiIni[4096]{0};
static constexpr uint32_t MaxAssets = 128;
uint32_t AssetCount = 0;
Gen::AssetHandle AssetHandles[MaxAssets]{0};
char AssetHandlePaths[MaxAssets][128];

View File

@@ -22,6 +22,7 @@ namespace
StaticPuzzleData StaticData;
StaticPuzzleCard InvalidCard;
PlacedPuzzleCard InvalidPlacedCard;
} // namespace
namespace Puzzle
@@ -104,6 +105,24 @@ namespace Puzzle
return puz.BackgroundTiles[pos.Y * Puzzle::Config::MaxPuzzleSizeTiles + pos.X];
}
PuzzleElementType::Enum GetInitialNodeAt(const PuzzleData& puz, PuzPos pos)
{
assert(pos.X < Puzzle::Config::MaxPuzzleSizeTiles && pos.Y < Puzzle::Config::MaxPuzzleSizeTiles && pos.X >= 0 &&
pos.Y >= 0);
int32_t cardIdxX = pos.X / Puzzle::Config::CardSize;
int32_t cardIdxY = pos.Y / Puzzle::Config::CardSize;
auto& card = puz.InitialPlacedCards[cardIdxY * Puzzle::Config::MaxPuzzleSizeCards + cardIdxX];
int32_t offsetX = pos.X - (cardIdxX * Config::CardSize);
int32_t offsetY = pos.Y - (cardIdxY * Config::CardSize);
if (offsetX >= 0 && offsetX < Puzzle::Config::CardSize && offsetY >= 0 && offsetY < Puzzle::Config::CardSize &&
IsValid(card.RefCard))
{
PuzzleElementType::Enum cardVal = GetCardNodeAt(GetCard(card.RefCard), card.Rotation, offsetX, offsetY);
if (cardVal != PuzzleElementType::None) return cardVal;
}
return puz.BackgroundTiles[pos.Y * Puzzle::Config::MaxPuzzleSizeTiles + pos.X];
}
PuzzleElementType::Enum GetCardNodeAt(const StaticPuzzleCard& card, uint8_t rotation, int8_t x, int8_t y)
{
assert(x >= 0 && x < Puzzle::Config::CardSize);
@@ -288,12 +307,32 @@ namespace Puzzle
bx::snprintf(buf, bufSize, "%s/%u.pzl", Puzzle::PuzzleFileDir, puzID);
}
PlacedPuzzleCard& GetCardAt(PuzzleData& obj, PuzPos pos)
{
if (pos.X < 0 || pos.X >= Config::MaxPuzzleSizeCards || pos.Y < 0 || pos.Y >= Config::MaxPuzzleSizeCards)
{
LOG_ERROR("Invalid card access at %i %i!!", pos.X, pos.Y);
return InvalidPlacedCard;
}
return obj.PlacedCards[pos.Y * Config::MaxPuzzleSizeCards + pos.X];
}
PlacedPuzzleCard& GetInitialCardAt(PuzzleData& obj, PuzPos pos)
{
if (pos.X < 0 || pos.X >= Config::MaxPuzzleSizeCards || pos.Y < 0 || pos.Y >= Config::MaxPuzzleSizeCards)
{
LOG_ERROR("Invalid card access at %i %i!!", pos.X, pos.Y);
return InvalidPlacedCard;
}
return obj.InitialPlacedCards[pos.Y * Config::MaxPuzzleSizeCards + pos.X];
}
bool ReturnPlacedCard(PuzzleData& obj, PuzPos targetPos)
{
auto& placedCard = obj.PlacedCards[targetPos.Y * Config::MaxPuzzleSizeCards + targetPos.X];
PlacedPuzzleCard& placedCard = GetCardAt(obj, targetPos);
if (IsValid(placedCard.RefCard))
{
if (placedCard.IsLocked)
if (GetFlag(placedCard.Flags, PlacedPuzzleCardFlags::Locked))
{
LOG_WARN("Card at %i %i is locked!", targetPos.X, targetPos.Y);
return false;
@@ -330,9 +369,9 @@ namespace Puzzle
auto& draggedCard = obj.AvailableCards[availIdx];
draggedCard.UsedCount++;
auto& placedCard = obj.PlacedCards[targetPos.Y * Config::MaxPuzzleSizeCards + targetPos.X];
PlacedPuzzleCard& placedCard = GetCardAt(obj, targetPos);
placedCard.RefCard = draggedCard.RefCard;
placedCard.IsLocked = false;
placedCard.Flags = (PlacedPuzzleCardFlags::Enum)ClearFlags(placedCard.Flags, PlacedPuzzleCardFlags::Locked);
placedCard.Position = targetPos;
placedCard.Rotation = rotation;
return true;
@@ -340,9 +379,6 @@ namespace Puzzle
bool RenderDebugUI(PuzzleData& obj)
{
bool dataChanged = false;
uint8_t debugRot = Game::GetInstance().DebugData.DebugCardRotation;
bool isVisible = true;
if (ImGui::Begin("Puzzle", &isVisible))
{
@@ -356,17 +392,34 @@ namespace Puzzle
return false;
}
ImGui::SameLine();
if (ImGui::Button("Reload"))
if (ImGui::Button("Reset"))
{
// TODO
ResetPuzzle(obj);
}
ImGui::SameLine();
if (ImGui::Button("Save"))
{
ResetPuzzle(obj);
char path[128]{0};
WritePuzzleFilePath(path, sizeof(path), obj.ID);
Serializer ser;
if (ser.Init(path, "PZZL") && ser.WriteT(obj))
{
LOG("Saved to %s", path);
}
else
{
LOG_ERROR("Failed to save to %s", path);
}
ser.Finish();
}
int32_t val = obj.ID;
if (ImGui::InputInt("ID", &val))
{
obj.ID = val;
dataChanged = true;
}
dataChanged = ImGui::InputText("Name", obj.PuzzleName, sizeof(obj.PuzzleName)) || dataChanged;
ImGui::InputText("Name", obj.PuzzleName, sizeof(obj.PuzzleName));
int32_t W = obj.WidthTiles;
int32_t H = obj.HeightTiles;
@@ -375,7 +428,6 @@ namespace Puzzle
if (ImGui::DragInt("", &W, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
{
obj.WidthTiles = uint8_t(W);
dataChanged = true;
}
ImGui::PopID();
ImGui::SameLine();
@@ -386,7 +438,6 @@ namespace Puzzle
if (ImGui::DragInt("", &H, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
{
obj.HeightTiles = uint8_t(H);
dataChanged = true;
}
ImGui::PopID();
@@ -401,7 +452,7 @@ namespace Puzzle
{
ImGui::PushID(x);
PuzPos nodePos = {int8_t(x), int8_t(y)};
auto node = GetNodeAt(obj, nodePos);
auto node = GetInitialNodeAt(obj, nodePos);
if (node == PuzzleElementType::WaterGoal)
{
obj.GoalPositions[obj.GoalPositionCount] = nodePos;
@@ -414,36 +465,29 @@ namespace Puzzle
ImGui::SetCursorScreenPos(pos);
if (x % Puzzle::Config::CardSize == 0 && y % Puzzle::Config::CardSize == 0)
{
int32_t cardX = x / Config::CardSize;
int32_t cardY = y / Config::CardSize;
PuzPos cardPos = {int8_t(cardX), int8_t(cardY)};
auto& placedCard = obj.PlacedCards[cardY * Config::MaxPuzzleSizeCards + cardX];
PuzPos cardPos = {int8_t(x / Config::CardSize), int8_t(y / Config::CardSize)};
PlacedPuzzleCard& placedCard = GetInitialCardAt(obj, cardPos);
bool isLocked = GetFlag(placedCard.Flags, PlacedPuzzleCardFlags::Locked);
ImGui::InvisibleButton("bn", {UIPuzBoxSize * 2, UIPuzBoxSize * 2});
if (ImGui::IsItemClicked(ImGuiMouseButton_Right))
{
placedCard.Rotation += 1;
if (placedCard.Rotation >= 4) placedCard.Rotation = 0;
dataChanged = true;
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Middle) && IsValid(placedCard.RefCard))
if (ImGui::IsItemClicked(ImGuiMouseButton_Middle))
{
if (placedCard.IsLocked)
if (ImGui::IsKeyDown(ImGuiKey_LeftShift))
{
placedCard.IsLocked = false;
placedCard.RefCard = {};
dataChanged = true;
placedCard = {};
}
else
{
if (!ReturnPlacedCard(obj, cardPos))
{
placedCard.IsLocked = false;
placedCard.RefCard = {};
dataChanged = true;
}
placedCard.Flags =
(PlacedPuzzleCardFlags::Enum)(placedCard.Flags ^ PlacedPuzzleCardFlags::Locked);
}
}
if (!placedCard.IsLocked && IsValid(placedCard.RefCard))
if (!isLocked && IsValid(placedCard.RefCard))
{
ImVec2 s = {puzCursorStart.x + x * UIPuzBoxSize, puzCursorStart.y + y * UIPuzBoxSize};
drawList.AddRectFilled(s,
@@ -458,17 +502,10 @@ namespace Puzzle
uint32_t CardIdx = *reinterpret_cast<uint32_t*>(payload->Data);
placedCard = {};
placedCard.RefCard = {(uint16_t)CardIdx};
placedCard.Rotation = debugRot;
placedCard.Rotation = 0;
placedCard.Position = cardPos;
placedCard.IsLocked = true;
dataChanged = true;
}
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("availcard", 0))
{
DragAvailableCardTo(obj,
cardPos,
*(int32_t*)payload->Data,
Game::GetInstance().DebugData.DebugCardRotation);
placedCard.Flags = (PlacedPuzzleCardFlags::Enum)SetFlags(placedCard.Flags,
PlacedPuzzleCardFlags::Locked);
}
ImGui::EndDragDropTarget();
}
@@ -534,7 +571,6 @@ namespace Puzzle
obj.AvailableCardCount++;
}
}
dataChanged = true;
}
ImGui::EndDragDropTarget();
}
@@ -547,14 +583,8 @@ namespace Puzzle
ImGui::SetCursorScreenPos(localPos);
ImGui::PushID(i);
auto& card = obj.AvailableCards[i];
DrawCard(GetCard(card.RefCard), debugRot, ImGui::GetCursorScreenPos());
DrawCard(GetCard(card.RefCard), 0, ImGui::GetCursorScreenPos());
int displayCount = card.MaxAvailableCount - card.UsedCount;
if (displayCount > 0 && ImGui::BeginDragDropSource())
{
ImGui::SetDragDropPayload("availcard", &i, sizeof(i));
DrawCard(GetCard(card.RefCard), debugRot, ImGui::GetCursorScreenPos());
ImGui::EndDragDropSource();
}
ImGui::SetCursorScreenPos({localPos.x, localPos.y + 55.0f});
ImGui::SetNextItemWidth(35);
@@ -563,7 +593,6 @@ namespace Puzzle
{
int diff = displayCount - (card.MaxAvailableCount - card.UsedCount);
card.MaxAvailableCount = bx::max(0, card.MaxAvailableCount + diff);
dataChanged = true;
}
ImGui::PopID();
ImGui::SameLine(0, 3);
@@ -582,22 +611,17 @@ namespace Puzzle
}
ImGui::End();
if (dataChanged)
{
char path[128]{0};
WritePuzzleFilePath(path, sizeof(path), obj.ID);
Serializer ser;
if (ser.Init(path, "PZZL") && ser.WriteT(obj))
{
LOG("Saved to %s", path);
}
else
{
LOG_ERROR("Failed to save to %s", path);
}
ser.Finish();
}
return isVisible;
}
void ResetPuzzle(PuzzleData& puz)
{
for (int32_t i = 0; i < puz.AvailableCardCount; ++i)
{
puz.AvailableCards[i].UsedCount = 0;
}
for (int32_t i = 0; i < BX_COUNTOF(puz.PlacedCards); ++i)
{
puz.PlacedCards[i] = puz.InitialPlacedCards[i];
}
}
} // namespace Puzzle

View File

@@ -35,15 +35,19 @@ namespace Puzzle
bool IsValid(StaticPuzzleCardHandle h);
uint8_t GetRemainingCount(const PuzzleCardStack& stack);
PuzzleElementType::Enum GetNodeAt(const PuzzleData& puz, PuzPos pos);
PuzzleElementType::Enum GetInitialNodeAt(const PuzzleData& puz, PuzPos pos);
PuzzleElementType::Enum GetCardNodeAt(const StaticPuzzleCard& card, uint8_t rotation, int8_t x, int8_t y);
PuzzleElementType::Enum& EditCardNodeAt(StaticPuzzleCard& card, uint8_t rotation, int8_t x, int8_t y);
void DrawCard(const StaticPuzzleCard& card, uint8_t rotation, ImVec2 pos);
void RotateCard(PlacedPuzzleCard& card);
PlacedPuzzleCard& GetCardAt(PuzzleData& obj, PuzPos pos);
PlacedPuzzleCard& GetInitialCardAt(PuzzleData& obj, PuzPos pos);
// TODO: targetPos is of type CardPos
bool ReturnPlacedCard(PuzzleData& obj, PuzPos targetPos);
// TODO: targetPos is of type CardPos
bool DragAvailableCardTo(PuzzleData& obj, PuzPos targetPos, int32_t availIdx, uint8_t rotation);
void ResetPuzzle(PuzzleData& puz);
struct PuzzleSolver
{

View File

@@ -483,6 +483,7 @@ namespace Tools
{
ImGui::Checkbox("Arenas", &debug.ShowArenaUsage);
ImGui::Checkbox("ImGui Demo", &debug.ShowImguiDemo);
ImGui::SliderFloat("Font Scale", &ImGui::GetIO().FontGlobalScale, 0.5f, 4.0f);
}
ImGui::End();
@@ -496,6 +497,23 @@ namespace Tools
}
ImGui::End();
}
if (ImGui::Begin("Entities"))
{
ImGui::Text("UIQuads");
for (uint16_t i = 0; i < level.UIQuads.Count; ++i)
{
ImGui::PushID(i);
auto& quad = level.UIQuads.Get({i});
ImGui::Checkbox("Visible", &quad.EData.Visible);
TextureDropdown(quad.EData.TextureHandle);
MaterialDropdown(quad.EData.MaterialHandle);
ImGui::DragFloat3("Pos", &quad.EData.Transform.Position.x);
ImGui::DragFloat3("UI Pos", &quad.UIPos.x);
ImGui::PopID();
}
}
ImGui::End();
}
if (debug.ShowStats)
{

View File

@@ -6,6 +6,7 @@
#include "Instance.h"
#include "Level.h"
#include "Puzzle.h"
#include "bx/math.h"
using namespace Gen;
@@ -177,11 +178,22 @@ namespace Game
}
}
Vec3 CalcBoardOffset(const Gen::PuzzleData& puzData)
{
return Vec3{
(float)(puzData.WidthTiles) / (2.0f * Puzzle::Config::CardSize),
(float)(puzData.HeightTiles) / (2.0f * Puzzle::Config::CardSize),
0.0f,
} *
2.0f * WorldPuzzleUI::UICardScale * WorldPuzzleUI::UICardScale;
}
void WorldPuzzleUI::UpdateBoardCards(Gen::PuzzleData& Data)
{
auto& level = GetInstance().GameLevel;
auto& staticCards = Puzzle::GetStaticPuzzleData().Cards;
Vec3 quadPlaneIntersectPos;
Vec3 boardOffset = CalcBoardOffset(Data);
for (int8_t y = 0; y < Data.HeightTiles / Puzzle::Config::CardSize; ++y)
{
@@ -191,22 +203,24 @@ namespace Game
int32_t cardIdx = y * Puzzle::Config::MaxPuzzleSizeCards + x;
PlacedPuzzleCard& card = Data.PlacedCards[cardIdx];
bool isValid = Puzzle::IsValid(card.RefCard);
bool isLocked = GetFlag(card.Flags, PlacedPuzzleCardFlags::Locked);
auto& quad = level.UIQuads.Get(UIPlacedCards[cardIdx]);
quad.UIPos = Vec3{(float)card.Position.X, (float)card.Position.Y, 0.0f} * UICardOffset * UICardScale;
quad.UIPos -= boardOffset;
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.DotColor =
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 (!isLocked && DraggedCard.X == -1 && DraggedAvailableCardIdx == UINT16_MAX)
{
if (GetMouseButtonPressedNow(MouseButton::Left))
{
@@ -318,8 +332,7 @@ namespace Game
if (GetMouseButtonPressedNow(MouseButton::Left) &&
IsQuadHovered(resetQuad.EData.Transform, StaticData.MousePosWorld, hoverPosWorld))
{
LOG_WARN("TODO!");
// TODO
Puzzle::ResetPuzzle(Data);
}
UpdateAvailableCards(Data);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -127,12 +127,18 @@ type PuzzleCardStack
u8 UsedCount
}
enum PlacedPuzzleCardFlags(u8) Flags
{
None
Locked
}
type PlacedPuzzleCard
{
StaticPuzzleCardHandle RefCard
PuzPos Position
u8 Rotation
b IsLocked
PlacedPuzzleCardFlags Flags
}
type PuzzleData
@@ -144,6 +150,7 @@ type PuzzleData
u32 AvailableCardCount
PuzzleCardStack AvailableCards Arr(16)
PlacedPuzzleCard PlacedCards Arr(256)
PlacedPuzzleCard InitialPlacedCards Arr(256)
PuzzleElementType BackgroundTiles Arr(1024)
u32 GoalPositionCount
PuzPos GoalPositions Arr(16)