save works!

This commit is contained in:
Asuro
2025-03-10 22:45:15 +01:00
parent ff00119e5b
commit c7377c3452
11 changed files with 538 additions and 217 deletions

View File

@@ -70,7 +70,7 @@ namespace Game
needReset |= PuzzleTiles.Setup(storagePtr, needReset);
needReset |= UIQuads.Setup(storagePtr, needReset);
PuzzleData.Setup();
Generated::Setup(PuzzleData);
if (Cubes.Count == 0)
{
@@ -255,7 +255,7 @@ namespace Game
for (uint32_t cardI = 0; cardI < Data.PlacedCardCount; ++cardI)
{
const Puzzle::PlacedPuzzleCard& card = Data.PlacedCards[cardI];
const Generated::PlacedPuzzleCard& card = Data.PlacedCards[cardI];
Level& level = GetInstance().GameLevel;
TileHandles[cardI] = level.PuzzleTiles.New();
UIPlacedCards[cardI] = level.UIQuads.New();
@@ -283,9 +283,10 @@ namespace Game
for (int32_t cardI = 0; cardI < Data.PlacedCardCount; ++cardI)
{
Puzzle::PlacedPuzzleCard& card = Data.PlacedCards[cardI];
if (!card.RefCard.IsValid()) continue;
const Puzzle::StaticPuzzleCard& cData = Puzzle::StaticPuzzleData::Get().GetCard(card.RefCard);
Generated::PlacedPuzzleCard& card = Data.PlacedCards[cardI];
if (!Generated::IsValid(card.RefCard)) continue;
const Generated::StaticPuzzleCard& cData =
Generated::GetCard(Generated::GetStaticPuzzleData(), card.RefCard);
level.PuzzleTiles.Get(TileHandles[cardI]).EData.ModelHandle = cData.ModelHandle;
auto& quad = level.UIQuads.Get(UIPlacedCards[cardI]);

View File

@@ -128,7 +128,7 @@ namespace Game
struct WorldPuzzle
{
static constexpr Vec2 WorldCardSize{10.0f, 10.0f};
Puzzle::PuzzleData Data;
Generated::PuzzleData Data;
Vec3 WorldPosition;
PuzzleTileEntityHandle TileHandles[Puzzle::Config::MaxCardsInPuzzle];
UIQuadEntityHandle UIPlacedCards[Puzzle::Config::MaxCardsInPuzzle];
@@ -146,7 +146,7 @@ namespace Game
EntityManager<UIQuadEntity, UIQuadEntityHandle, 1024> UIQuads;
public:
Puzzle::StaticPuzzleData PuzzleData;
Generated::StaticPuzzleData PuzzleData;
WorldPuzzle Puzzles[1];
public:

View File

@@ -9,73 +9,79 @@
namespace
{
static constexpr Puzzle::PuzPos Dirs[4]{
static constexpr Generated::PuzPos Dirs[4]{
{-1, 0},
{0, -1},
{0, 1},
{1, 0},
};
Puzzle::StaticPuzzleData* StaticDataInstance = nullptr;
Generated::StaticPuzzleData* StaticDataInstance = nullptr;
} // namespace
namespace Puzzle
namespace Generated
{
void StaticPuzzleData::Setup()
void Setup(StaticPuzzleData& data)
{
StaticDataInstance = this;
StaticDataInstance = &data;
LOG("Setting up static puzzle data");
for (int32_t i = 0; i < BX_COUNTOF(Cards); ++i)
for (int32_t i = 0; i < BX_COUNTOF(data.Cards); ++i)
{
Cards[i].ModelHandle = Game::GameRendering::Get().GetModelHandleFromPath("models/w straight.glb");
data.Cards[i].ModelHandle = Game::GameRendering::Get().GetModelHandleFromPath("models/w straight.glb");
}
}
StaticPuzzleData& StaticPuzzleData::Get()
StaticPuzzleData& GetStaticPuzzleData()
{
assert(StaticDataInstance != nullptr);
return *StaticDataInstance;
}
const StaticPuzzleCard& StaticPuzzleData::GetCard(StaticPuzzleCardHandle H) const
const StaticPuzzleCard& GetCard(const StaticPuzzleData& data, StaticPuzzleCardHandle H)
{
assert(H.IsValid());
return Cards[H.Idx];
// assert(IsValid(H));
return data.Cards[H.Idx];
}
bool PuzzleNode::HasElement(PuzzleElementType search) const
bool HasElement(const PuzzleNode& node, PuzzleElementType::Enum search)
{
for (int32_t i = 0; i < Config::MaxElementsPerTile; ++i)
for (int32_t i = 0; i < Puzzle::Config::MaxElementsPerTile; ++i)
{
if (PlacedTypes[i] == search) return true;
if (node.PlacedTypes[i] == search) return true;
}
return false;
}
bool PuzzleNode::IsEmpty() const
bool IsEmpty(const PuzzleNode& node)
{
for (int32_t i = 0; i < Config::MaxElementsPerTile; ++i)
for (int32_t i = 0; i < Puzzle::Config::MaxElementsPerTile; ++i)
{
if (PlacedTypes[i] != PuzzleElementType::None) return false;
if (node.PlacedTypes[i] != PuzzleElementType::None) return false;
}
return true;
}
uint8_t PuzzleCardStack::GetRemainingCount()
bool IsValid(StaticPuzzleCardHandle h)
{
assert(MaxAvailableCount >= UsedCount);
return MaxAvailableCount - UsedCount;
return h.Idx != UINT16_MAX;
}
const PuzzleNode& PuzzleData::GetNodeAt(PuzPos pos) const
uint8_t GetRemainingCount(const PuzzleCardStack& stack)
{
assert(pos.X < Config::MaxPuzzleSizeCards && pos.Y < Config::MaxPuzzleSizeCards && pos.X >= 0 && pos.Y >= 0);
return PlacedNodes[pos.Y * Config::MaxPuzzleSizeCards + pos.X];
assert(stack.MaxAvailableCount >= stack.UsedCount);
return stack.MaxAvailableCount - stack.UsedCount;
}
PuzzleElementType PuzzleData::GetElementAt(ElemPos pos) const
const PuzzleNode& GetNodeAt(const PuzzleData& puz, PuzPos pos)
{
assert(pos.ElemIdx < Config::MaxElementsPerTile);
const PuzzleNode& node = GetNodeAt(pos.Position);
assert(pos.X < Puzzle::Config::MaxPuzzleSizeCards && pos.Y < Puzzle::Config::MaxPuzzleSizeCards && pos.X >= 0 &&
pos.Y >= 0);
return puz.PlacedNodes[pos.Y * Puzzle::Config::MaxPuzzleSizeCards + pos.X];
}
PuzzleElementType::Enum GetElementAt(const PuzzleData& puz, ElemPos pos)
{
assert(pos.ElemIdx < Puzzle::Config::MaxElementsPerTile);
const PuzzleNode& node = GetNodeAt(puz, pos.Position);
return node.PlacedTypes[pos.ElemIdx];
}
@@ -95,26 +101,26 @@ namespace Puzzle
bool PuzzleSolver::IsExitSatisfied(const PuzzleData& puzzle, ElemPos pos)
{
const PuzzleNode& goalNode = puzzle.GetNodeAt(pos.Position);
PuzzleElementType goalType = puzzle.GetElementAt(pos);
const PuzzleNode& goalNode = GetNodeAt(puzzle, pos.Position);
PuzzleElementType::Enum goalType = GetElementAt(puzzle, pos);
uint32_t currentPositionQueueIdx = 0;
uint32_t positionQueueCount = 0;
PuzPos positionQueue[Config::MaxTilesInPuzzle];
PuzPos positionQueue[Puzzle::Config::MaxTilesInPuzzle];
positionQueue[0] = pos.Position;
positionQueueCount++;
while (positionQueueCount > 0)
{
assert(currentPositionQueueIdx < Config::MaxTilesInPuzzle);
assert(currentPositionQueueIdx < Puzzle::Config::MaxTilesInPuzzle);
PuzPos currentPos = positionQueue[currentPositionQueueIdx];
for (PuzPos dir : Dirs)
{
PuzPos nextPos = currentPos + dir;
if (IsValidGoalConnection(puzzle, nextPos, currentPos, goalType))
{
const PuzzleNode& node = puzzle.GetNodeAt(nextPos);
for (PuzzleElementType e : node.PlacedTypes)
const PuzzleNode& node = GetNodeAt(puzzle, nextPos);
for (PuzzleElementType::Enum e : node.PlacedTypes)
{
if (IsValidSource(e, goalType))
{
@@ -133,46 +139,50 @@ namespace Puzzle
bool PuzzleSolver::IsValidGoalConnection(const PuzzleData& puzzle,
PuzPos flowFrom,
PuzPos flowTo,
PuzzleElementType goalType)
PuzzleElementType::Enum goalType)
{
const PuzzleNode& from = puzzle.GetNodeAt(flowFrom);
const PuzzleNode& to = puzzle.GetNodeAt(flowTo);
const PuzzleNode& from = GetNodeAt(puzzle, flowFrom);
const PuzzleNode& to = GetNodeAt(puzzle, flowTo);
if (goalType == PuzzleElementType::WaterGoal)
{
return from.HasElement(PuzzleElementType::WaterIn) || from.HasElement(PuzzleElementType::WaterChannel) ||
from.HasElement(PuzzleElementType::WaterGoal);
return HasElement(from, PuzzleElementType::WaterIn) || HasElement(from, PuzzleElementType::WaterChannel) ||
HasElement(from, PuzzleElementType::WaterGoal);
}
else if (goalType == PuzzleElementType::ElectricGoal)
{
return from.HasElement(PuzzleElementType::ElectricIn) || from.HasElement(PuzzleElementType::ElectricGoal) ||
from.IsEmpty();
return HasElement(from, PuzzleElementType::ElectricIn) ||
HasElement(from, PuzzleElementType::ElectricGoal) || IsEmpty(from);
}
assert(false);
return false;
}
bool PuzzleSolver::IsValidSource(PuzzleElementType sourceType, PuzzleElementType goalType)
bool PuzzleSolver::IsValidSource(PuzzleElementType::Enum sourceType, PuzzleElementType::Enum goalType)
{
return (sourceType == PuzzleElementType::WaterIn && goalType == PuzzleElementType::WaterGoal) ||
(sourceType == PuzzleElementType::ElectricIn && goalType == PuzzleElementType::ElectricGoal);
}
bool PuzzleData::RenderDebugUI()
} // namespace Generated
namespace Generated
{
bool RenderDebugUI(PuzzleData& obj)
{
bool dataChanged = false;
bool isVisible = true;
if (ImGui::Begin("Puzzle", &isVisible))
{
ImGui::Text("%s", PuzzleName);
ImGui::Text("%s", obj.PuzzleName);
int32_t W = S.WidthTiles;
int32_t H = S.HeightTiles;
int32_t W = obj.WidthTiles;
int32_t H = obj.HeightTiles;
ImGui::PushID("width");
ImGui::SetNextItemWidth(40);
if (ImGui::DragInt("", &W, 0.3f, 0, Config::MaxPuzzleSizeTiles))
if (ImGui::DragInt("", &W, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
{
S.WidthTiles = uint8_t(W);
obj.WidthTiles = uint8_t(W);
dataChanged = true;
}
ImGui::PopID();
@@ -181,9 +191,9 @@ namespace Puzzle
ImGui::SameLine();
ImGui::SetNextItemWidth(40);
ImGui::PushID("height");
if (ImGui::DragInt("", &H, 0.3f, 0, Config::MaxPuzzleSizeTiles))
if (ImGui::DragInt("", &H, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
{
S.HeightTiles = uint8_t(H);
obj.HeightTiles = uint8_t(H);
dataChanged = true;
}
ImGui::PopID();
@@ -193,10 +203,32 @@ namespace Puzzle
if (dataChanged)
{
char path[128]{0};
bx::snprintf(path, sizeof(path), "game/data/%s.pzl", PuzzleName);
SerializeStruct(&S, sizeof(S), path);
bx::snprintf(path, sizeof(path), "game/data/%s.pzl", obj.PuzzleName);
Serializer ser;
ser.Init(path);
if (Save(&obj, 1, ser))
{
LOG("Saved to %s", path);
}
else
{
LOG_ERROR("Failed to save to %s", path);
}
ser.Finish();
}
return isVisible;
}
} // namespace Puzzle
PuzPos operator+=(PuzPos lhs, const PuzPos& rhs)
{
lhs.X += rhs.X;
lhs.Y += rhs.Y;
return lhs;
}
PuzPos operator+(PuzPos lhs, const PuzPos& rhs)
{
lhs += rhs;
return lhs;
}
} // namespace Generated

View File

@@ -19,125 +19,139 @@ namespace Puzzle
static constexpr uint32_t MaxGoalPositions = 16;
};
struct PuzPos
{
int8_t X = 0;
int8_t Y = 0;
// struct PuzPos
// {
// int8_t X = 0;
// int8_t Y = 0;
PuzPos& operator+=(const PuzPos& rhs)
{
X += rhs.X;
Y += rhs.Y;
return *this;
}
// PuzPos& operator+=(const PuzPos& rhs)
// {
// X += rhs.X;
// Y += rhs.Y;
// return *this;
// }
friend PuzPos operator+(PuzPos lhs, const PuzPos& rhs)
{
lhs += rhs;
return lhs;
}
};
// friend PuzPos operator+(PuzPos lhs, const PuzPos& rhs)
// {
// lhs += rhs;
// return lhs;
// }
// };
struct ElemPos
{
PuzPos Position;
uint8_t ElemIdx = 0;
};
// struct ElemPos
// {
// PuzPos Position;
// uint8_t ElemIdx = 0;
// };
enum class PuzzleElementType : uint8_t
{
None,
WaterIn,
WaterGoal,
WaterChannel,
ElectricIn,
ElectricGoal,
Blocked,
Bridge,
};
// enum class PuzzleElementType : uint8_t
// {
// None,
// WaterIn,
// WaterGoal,
// WaterChannel,
// ElectricIn,
// ElectricGoal,
// Blocked,
// Bridge,
// };
struct PuzzleNode
{
PuzzleElementType PlacedTypes[Config::MaxElementsPerTile]{PuzzleElementType::None};
// struct PuzzleNode
// {
// PuzzleElementType PlacedTypes[Config::MaxElementsPerTile]{PuzzleElementType::None};
bool HasElement(PuzzleElementType search) const;
bool IsEmpty() const;
};
// bool HasElement(PuzzleElementType search) const;
// bool IsEmpty() const;
// };
struct StaticPuzzleCard
{
PuzzleNode Nodes[Config::NodesPerCard];
uint16_t ModelHandle = 0;
};
// struct StaticPuzzleCard
// {
// PuzzleNode Nodes[Config::NodesPerCard];
// uint16_t ModelHandle = 0;
// };
struct StaticPuzzleCardHandle
{
uint16_t Idx = UINT16_MAX;
bool IsValid()
{
return Idx != UINT16_MAX;
}
};
// struct StaticPuzzleCardHandle
// {
// uint16_t Idx = UINT16_MAX;
// bool IsValid()
// {
// return Idx != UINT16_MAX;
// }
// };
struct StaticPuzzleData
{
StaticPuzzleCard Cards[64];
// struct StaticPuzzleData
// {
// StaticPuzzleCard Cards[64];
void Setup();
static StaticPuzzleData& Get();
const StaticPuzzleCard& GetCard(StaticPuzzleCardHandle H) const;
};
// void Setup();
// static StaticPuzzleData& Get();
// const StaticPuzzleCard& GetCard(StaticPuzzleCardHandle H) const;
// };
struct PlacedPuzzleCard
{
StaticPuzzleCardHandle RefCard;
PuzPos Position;
uint8_t Rotation = 0;
bool IsLocked = false;
};
// struct PlacedPuzzleCard
// {
// StaticPuzzleCardHandle RefCard;
// PuzPos Position;
// uint8_t Rotation = 0;
// bool IsLocked = false;
// };
struct PuzzleCardStack
{
StaticPuzzleCardHandle RefCard;
uint8_t MaxAvailableCount = 0;
uint8_t UsedCount = 0;
// struct PuzzleCardStack
// {
// StaticPuzzleCardHandle RefCard;
// uint8_t MaxAvailableCount = 0;
// uint8_t UsedCount = 0;
uint8_t GetRemainingCount();
};
// uint8_t GetRemainingCount();
// };
struct PuzzleData
{
SER_HEADER(1, "PZZL");
Generated::PuzzleData S;
// struct PuzzleData
// {
// uint32_t AvailableCardCount = 0;
// PuzzleCardStack AvailableCards[Config::MaxAvailableStacks];
// uint32_t PlacedCardCount = 0;
// PlacedPuzzleCard PlacedCards[Config::MaxCardsInPuzzle];
uint32_t AvailableCardCount = 0;
PuzzleCardStack AvailableCards[Config::MaxAvailableStacks];
uint32_t PlacedCardCount = 0;
PlacedPuzzleCard PlacedCards[Config::MaxCardsInPuzzle];
// // Indexed by board position
// PuzzleNode PlacedNodes[Config::MaxTilesInPuzzle];
// Indexed by board position
PuzzleNode PlacedNodes[Config::MaxTilesInPuzzle];
// uint32_t GoalPositionCount = 0;
// ElemPos GoalPositions[Config::MaxGoalPositions];
uint32_t GoalPositionCount = 0;
ElemPos GoalPositions[Config::MaxGoalPositions];
// const PuzzleNode& GetNodeAt(PuzPos pos) const;
// PuzzleElementType GetElementAt(ElemPos pos) const;
const PuzzleNode& GetNodeAt(PuzPos pos) const;
PuzzleElementType GetElementAt(ElemPos pos) const;
// char PuzzleName[32]{"Unnamed"};
// bool RenderDebugUI();
// };
} // namespace Puzzle
char PuzzleName[32]{"Unnamed"};
bool RenderDebugUI();
};
namespace Generated
{
PuzPos operator+=(PuzPos lhs, const PuzPos& rhs);
PuzPos operator+(PuzPos lhs, const PuzPos& rhs);
void Setup(StaticPuzzleData& data);
StaticPuzzleData& GetStaticPuzzleData();
const StaticPuzzleCard& GetCard(const StaticPuzzleData& data, StaticPuzzleCardHandle H);
bool HasElement(const PuzzleNode& node, PuzzleElementType::Enum search);
bool IsEmpty(const PuzzleNode& node);
bool IsValid(StaticPuzzleCardHandle h);
uint8_t GetRemainingCount(const PuzzleCardStack& stack);
const PuzzleNode& GetNodeAt(const PuzzleData& puz, PuzPos pos);
PuzzleElementType::Enum GetElementAt(const PuzzleData& puz, ElemPos pos);
struct PuzzleSolver
{
bool IsPuzzleSolved(const PuzzleData& puzzle);
bool IsExitSatisfied(const PuzzleData& puzzle, ElemPos pos);
bool IsPuzzleSolved(const Generated::PuzzleData& puzzle);
bool IsExitSatisfied(const Generated::PuzzleData& puzzle, ElemPos pos);
// This assumes flowFrom is already verified to be connected.
bool IsValidGoalConnection(const PuzzleData& puzzle,
bool IsValidGoalConnection(const Generated::PuzzleData& puzzle,
PuzPos flowFrom,
PuzPos flowTo,
PuzzleElementType goalType);
bool IsValidSource(PuzzleElementType sourceType, PuzzleElementType goalType);
PuzzleElementType::Enum goalType);
bool IsValidSource(PuzzleElementType::Enum sourceType, PuzzleElementType::Enum goalType);
};
} // namespace Puzzle
bool RenderDebugUI(PuzzleData& obj);
} // namespace Generated

View File

@@ -69,4 +69,5 @@ type PuzzleData
PuzzleNode PlacedNodes Arr(1024)
u32 GoalPositionCount
ElemPos GoalPositions Arr(16)
str PuzzleName Arr(64)
}

View File

@@ -511,6 +511,7 @@ namespace Game
if (ImGui::Begin("Puzzles"))
{
ImGui::Text("List");
ImGui::Separator();
for (int32_t i = 0; i < BX_COUNTOF(level.Puzzles); ++i)
{
auto& puzzleData = level.Puzzles[i].Data;
@@ -524,10 +525,10 @@ namespace Game
if (isSelected)
{
ImGui::PushID("edit field");
ImGui::InputText("", puzzleData.PuzzleName, sizeof(Puzzle::PuzzleData::PuzzleName));
ImGui::InputText("", puzzleData.PuzzleName, sizeof(Generated::PuzzleData::PuzzleName));
ImGui::PopID();
if (!puzzleData.RenderDebugUI())
if (!Generated::RenderDebugUI(puzzleData))
{
debug.SelectedDebugLevel = UINT16_MAX;
}
@@ -540,13 +541,6 @@ namespace Game
GetInstance().GameLevel.Update();
GetInstance().GameLevel.Render(MainViewID, Models, Materials);
// bgfx::dbgTextPrintf(1, 1, 0x0F, "Time: %.1fs", GetInstance().Time.Now);
// for (int32_t i = 0; i < (int32_t)PerfCounterType::COUNT; ++i)
// {
// bgfx::dbgTextPrintf(
// 1, 2 + i, 0x0F, "%s Max: %.3fs", PerfCounterNames[i], shared.Window.PerfCounters[i].GetMax());
// }
// Finish Frame
imguiEndFrame();