694 lines
30 KiB
C++
694 lines
30 KiB
C++
#include "../gen/Def.h"
|
|
#include "Gen.h"
|
|
#include "Global.h"
|
|
#include "Input.h"
|
|
#include "Instance.h"
|
|
#include "Level.h"
|
|
#include "Log.h"
|
|
#include "Puzzle.h"
|
|
#include "bx/bx.h"
|
|
#include "rendering/Rendering.h"
|
|
|
|
#include "SDL3/SDL_mouse.h"
|
|
#include "bgfx/bgfx.h"
|
|
#include "bx/error.h"
|
|
#include "bx/file.h"
|
|
#include "bx/filepath.h"
|
|
#include "bx/string.h"
|
|
#include "imgui.h"
|
|
#include <SDL3/SDL.h>
|
|
#include <bx/math.h>
|
|
#include <cstdint>
|
|
#include <tracy/Tracy.hpp>
|
|
|
|
using namespace Gen;
|
|
|
|
namespace Game
|
|
{
|
|
void EntityRenderData::Render(const Model* models, const Material* materials, const Texture* textures)
|
|
{
|
|
if (models == nullptr || materials == nullptr || textures == nullptr) return;
|
|
if (!Gen::IsValid(ModelH) || MaterialHandle >= EMaterial::EntryCount) return;
|
|
if (!Visible) return;
|
|
auto& rendering = GameRendering::Get();
|
|
|
|
UpdateMatrix(Transform);
|
|
bgfx::setTransform(Transform.M.M);
|
|
|
|
const Model& currentModel = models[ModelH.ModelIdx];
|
|
const Material& currentMaterial = materials[(uint16_t)MaterialHandle];
|
|
|
|
if (!isValid(currentModel.IndexBuffer) || !isValid(currentModel.VertexBuffer)) return;
|
|
if (!isValid(currentMaterial.Shader)) return;
|
|
|
|
bgfx::setVertexBuffer(0, currentModel.VertexBuffer);
|
|
bgfx::setIndexBuffer(currentModel.IndexBuffer);
|
|
bgfx::setState(currentMaterial.State);
|
|
|
|
float timeValues[4]{0.0f};
|
|
timeValues[0] = GetInstance().Time.Now;
|
|
|
|
float texInfo[4]{0.0f};
|
|
texInfo[0] = textures[0].Info.width;
|
|
texInfo[1] = textures[0].Info.height;
|
|
texInfo[2] = rendering.DitherTextures.DitherTexWH;
|
|
texInfo[3] = rendering.DitherTextures.DitherTexDepth;
|
|
|
|
bgfx::UniformHandle sampler = rendering.DefaultSampler;
|
|
bgfx::TextureHandle tex = rendering.Textures[0].RenderHandle;
|
|
if (IsValid(TextureHandle))
|
|
{
|
|
sampler = textures[TextureHandle.TextureIdx].SamplerHandle;
|
|
tex = textures[TextureHandle.TextureIdx].RenderHandle;
|
|
texInfo[0] = textures[TextureHandle.TextureIdx].Info.width;
|
|
texInfo[1] = textures[TextureHandle.TextureIdx].Info.height;
|
|
}
|
|
|
|
bgfx::setTexture(0, sampler, tex);
|
|
bgfx::setTexture(1, rendering.DitherTextures.DitherSampler, rendering.DitherTextures.FinalTex);
|
|
bgfx::setTexture(2, rendering.DitherTextures.RampSampler, rendering.DitherTextures.RampTex);
|
|
bgfx::setUniform(currentMaterial.Uniforms[Material::UTime], timeValues);
|
|
bgfx::setUniform(currentMaterial.Uniforms[Material::UDotColor], &DotColor.x);
|
|
bgfx::setUniform(currentMaterial.Uniforms[Material::UTexInfo], texInfo);
|
|
bgfx::setUniform(currentMaterial.Uniforms[Material::UBaseColor], &BaseColor.x);
|
|
|
|
bgfx::submit(currentMaterial.ViewID, currentMaterial.Shader);
|
|
}
|
|
|
|
void EntityRenderData::LoadFromSaved(const Gen::SavedEntityRenderData& saved)
|
|
{
|
|
DotColor = saved.HighlightColor;
|
|
BaseColor = saved.BaseColor;
|
|
Transform = saved.TF;
|
|
// TODO: fix handle indices
|
|
MaterialHandle = saved.Material;
|
|
TextureHandle = saved.Texture;
|
|
ModelH = saved.Model;
|
|
Visible = saved.Visible;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
void UpdatePlayerInputMode()
|
|
{
|
|
bool IsGaming = GetInstance().Player.InputM == InputMode::Game;
|
|
bool captureMouse = IsGaming && GetInstance().Player.InteractionM == InteractionMode::Walk;
|
|
SDL_SetWindowRelativeMouseMode(GetShared().Window.SDLWindow, captureMouse);
|
|
|
|
auto& rendering = GameRendering::Get();
|
|
if (rendering.SetupData.UseImgui)
|
|
{
|
|
auto& IO = ImGui::GetIO();
|
|
IO.ConfigFlags =
|
|
FlagBool(IO.ConfigFlags, ImGuiConfigFlags_NoMouse | ImGuiConfigFlags_NoKeyboard, IsGaming);
|
|
}
|
|
rendering.UIVisible = IsGaming ? UIVisibilityState::Game : UIVisibilityState::Debug;
|
|
}
|
|
} // namespace
|
|
|
|
void Level::Setup(GameData& data)
|
|
{
|
|
LOG("Level setup");
|
|
uint8_t* storagePtr = data.EntityArena.Base;
|
|
bool needReset = false;
|
|
needReset |= Cubes.Setup(storagePtr, needReset);
|
|
needReset |= Tests.Setup(storagePtr, needReset);
|
|
needReset |= PuzzleTiles.Setup(storagePtr, needReset);
|
|
needReset |= PuzzleTileCovers.Setup(storagePtr, needReset);
|
|
needReset |= UIQuads.Setup(storagePtr, needReset);
|
|
needReset |= LevelEntities.Setup(storagePtr, needReset);
|
|
|
|
Puzzle::Setup();
|
|
UIQuads.Count = 0;
|
|
PuzzleTiles.Count = 0;
|
|
|
|
bx::Error err;
|
|
bx::DirectoryReader dirIter;
|
|
bx::FileInfo info;
|
|
bx::FilePath puzzleDirPath{Puzzle::PuzzleFileDir};
|
|
if (dirIter.open(puzzleDirPath, &err))
|
|
{
|
|
while (true)
|
|
{
|
|
int32_t readCount = dirIter.read(&info, sizeof(info), &err);
|
|
if (readCount == 0) break;
|
|
if (err.isOk())
|
|
{
|
|
if (info.type != bx::FileType::File) continue;
|
|
bx::StringView pathEnd = info.filePath.getExt();
|
|
if (bx::strCmpI(pathEnd, ".pzl") != 0) continue;
|
|
|
|
bx::FilePath fullPath = puzzleDirPath;
|
|
fullPath.join(info.filePath);
|
|
LOG("Loading %s", fullPath.getCPtr());
|
|
|
|
Gen::Deserializer ser;
|
|
Gen::PuzzleData dataBuf;
|
|
if (ser.Init(fullPath, "PZZL") && ser.ReadT(dataBuf))
|
|
{
|
|
if (dataBuf.ID >= BX_COUNTOF(Puzzles))
|
|
{
|
|
LOG_ERROR("Puzzle ID out of bounds: %u", dataBuf.ID);
|
|
ser.Finish();
|
|
continue;
|
|
}
|
|
Puzzles[dataBuf.ID].Data = dataBuf;
|
|
Puzzles[dataBuf.ID].Setup();
|
|
}
|
|
else
|
|
{
|
|
LOG_WARN("Failed to load puzzle!");
|
|
}
|
|
ser.Finish();
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("Failed parsing file name at %s:\n%s", puzzleDirPath.getCPtr(), err.getMessage());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("Failed to open puzzle dir at %s:\n%s", puzzleDirPath.getCPtr(), err.getMessage().getCPtr());
|
|
}
|
|
|
|
if (!IsValid(PlayerOutsideViewCube))
|
|
{
|
|
PlayerOutsideViewCube = Cubes.New();
|
|
Cubes.Get(PlayerOutsideViewCube).Setup();
|
|
}
|
|
|
|
UIQuads.Count = 0;
|
|
PuzzleTiles.Count = 0;
|
|
PuzzleTileCovers.Count = 0;
|
|
for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i)
|
|
{
|
|
if (Puzzles[i].Data.ID != UINT16_MAX)
|
|
{
|
|
Puzzles[i].Setup();
|
|
}
|
|
}
|
|
TabletHandle = UIQuads.New();
|
|
UpdatePlayerInputMode();
|
|
|
|
{
|
|
Deserializer d;
|
|
d.Init("game/data/static/uiconfig.dat", "UICO");
|
|
d.ReadT(GetInstance().Player.Config);
|
|
d.Finish();
|
|
}
|
|
}
|
|
|
|
void Level::Update()
|
|
{
|
|
ZoneScopedN("Level update");
|
|
START_PERF();
|
|
PlayerData& player = GetInstance().Player;
|
|
|
|
// Input
|
|
float delta = GetInstance().Time.Delta;
|
|
float moveSpeed = player.MovementSpeed;
|
|
float rotSpeed = player.MouseSensitivity;
|
|
|
|
float forwardInput = (GetKey(ScanCode::W) ? 1.0f : 0.0f) + (GetKey(ScanCode::S) ? -1.0f : 0.0f);
|
|
float rightInput = (GetKey(ScanCode::D) ? 1.0f : 0.0f) + (GetKey(ScanCode::A) ? -1.0f : 0.0f);
|
|
bx::Vec3 moveInput = bx::Vec3{rightInput, forwardInput, 0.0f};
|
|
moveInput = bx::normalize(moveInput);
|
|
bx::Vec3 inputVec = {moveInput.x * delta * moveSpeed, 0.0f, moveInput.y * delta * moveSpeed};
|
|
Vec2 mouseMovement = GetMouseMovement();
|
|
bx::Vec3 rotInput = {mouseMovement.y * delta * -rotSpeed, mouseMovement.x * delta * -rotSpeed, 0.0f};
|
|
|
|
if (GetKeyPressedNow(ScanCode::F1))
|
|
{
|
|
player.CameraM = player.CameraM == CameraMode::Walk ? CameraMode::Freefly : CameraMode::Walk;
|
|
}
|
|
|
|
if (GetKeyPressedNow(ScanCode::F2))
|
|
{
|
|
if (player.InputM == InputMode::Game)
|
|
{
|
|
player.InputM = InputMode::UI;
|
|
}
|
|
else
|
|
{
|
|
player.InputM = InputMode::Game;
|
|
}
|
|
UpdatePlayerInputMode();
|
|
}
|
|
|
|
if (player.CameraM == CameraMode::Freefly)
|
|
{
|
|
if (GetMouseButton(MouseButton::Left))
|
|
{
|
|
player.FreeflyXRot += rotInput.x;
|
|
player.FreeflyYRot += rotInput.y;
|
|
bx::mtxRotateXYZ(player.FreeflyCamTransform.Rotation.M, player.FreeflyXRot, player.FreeflyYRot, 0.0f);
|
|
}
|
|
|
|
TranslateLocal(player.FreeflyCamTransform, {0.0f, 0.0f, inputVec.z});
|
|
TranslateLocal(player.FreeflyCamTransform, {inputVec.x, 0.0f, 0.0f});
|
|
}
|
|
else if (player.CameraM == CameraMode::Walk)
|
|
{
|
|
TranslateLocal(player.PlayerCamTransform, {0.0f, 0.0f, inputVec.z});
|
|
TranslateLocal(player.PlayerCamTransform, {inputVec.x, 0.0f, 0.0f});
|
|
player.PlayerCamTransform.Position.y = 3.0f;
|
|
|
|
player.WalkXRot += rotInput.x;
|
|
player.WalkYRot += rotInput.y;
|
|
bx::mtxRotateXYZ(player.PlayerCamTransform.Rotation.M, player.WalkXRot, player.WalkYRot, 0.0f);
|
|
}
|
|
|
|
if (GetKeyPressedNow(ScanCode::SPACE))
|
|
{
|
|
player.InteractionM = player.InteractionM == InteractionMode::ReadTablet ? InteractionMode::Walk
|
|
: InteractionMode::ReadTablet;
|
|
UpdatePlayerInputMode();
|
|
}
|
|
|
|
// UI Tablet
|
|
if (IsValid(TabletHandle))
|
|
{
|
|
auto& tablet = UIQuads.Get(TabletHandle);
|
|
tablet.EData.LoadFromSaved(player.Config.TabletBackgroundRenderData);
|
|
UpdateMatrix(player.PlayerCamTransform);
|
|
tablet.EData.Transform.Rotation = player.PlayerCamTransform.Rotation;
|
|
Rotate(tablet.EData.Transform, {0.5f * bx::kPi, 0.0f, 0.0f});
|
|
tablet.EData.Transform.Position =
|
|
player.PlayerCamTransform.Position + AxisForward(player.PlayerCamTransform.M) * 1.0f;
|
|
}
|
|
|
|
// Cubes
|
|
for (uint16_t i = 0; i < Cubes.Count; ++i)
|
|
{
|
|
Cubes.Get({i}).Update();
|
|
}
|
|
|
|
// Puzzle tiles
|
|
for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i)
|
|
{
|
|
Puzzles[i].IsActive = GetInstance().DebugData.SelectedDebugLevel == i;
|
|
Puzzles[i].Update();
|
|
}
|
|
|
|
END_PERF(GetShared().Window.PerfCounters, PerfCounterType::GameLevelUpdate, GetShared().Window.FrameCounter);
|
|
}
|
|
|
|
void Level::Render(uint16_t viewId, const Model* models, const Material* materials, const Texture* textures)
|
|
{
|
|
ZoneScopedN("Level Render");
|
|
auto& shared = GetShared();
|
|
auto& player = GetInstance().Player;
|
|
|
|
bx::mtxProj(&player.Projection.M[0],
|
|
75.0f,
|
|
float(shared.Window.WindowWidth) / float(shared.Window.WindowHeight),
|
|
0.1f,
|
|
1000.0f,
|
|
bgfx::getCaps()->homogeneousDepth);
|
|
bx::mtxInverse(&player.ProjectionInverse.M[0], &player.Projection.M[0]);
|
|
|
|
bool isFreefly = player.CameraM == CameraMode::Freefly;
|
|
Cubes.Get(PlayerOutsideViewCube).EData.Visible = isFreefly;
|
|
if (isFreefly)
|
|
{
|
|
UpdateMatrix(player.FreeflyCamTransform);
|
|
bgfx::setViewTransform(viewId, player.FreeflyCamTransform.MI.M, &player.Projection.M[0]);
|
|
}
|
|
else
|
|
{
|
|
UpdateMatrix(player.PlayerCamTransform);
|
|
bgfx::setViewTransform(viewId, player.PlayerCamTransform.MI.M, &player.Projection.M[0]);
|
|
}
|
|
|
|
bgfx::touch(viewId);
|
|
|
|
Cubes.Render(models, materials, textures);
|
|
Tests.Render(models, materials, textures);
|
|
PuzzleTiles.Render(models, materials, textures);
|
|
PuzzleTileCovers.Render(models, materials, textures);
|
|
if (player.InteractionM == InteractionMode::ReadTablet)
|
|
{
|
|
UIQuads.Render(models, materials, textures);
|
|
}
|
|
}
|
|
|
|
void Cube::Setup()
|
|
{
|
|
EData.MaterialHandle = EMaterial::UI;
|
|
EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf");
|
|
}
|
|
|
|
void Cube::Update()
|
|
{
|
|
EData.Transform.Position = GetInstance().Player.PlayerCamTransform.Position;
|
|
EData.Transform.Rotation = GetInstance().Player.PlayerCamTransform.Rotation;
|
|
EData.Transform.Scale = {0.2f, 0.2f, 0.2f};
|
|
}
|
|
|
|
void TestEntity::Setup()
|
|
{
|
|
EData.MaterialHandle = EMaterial::Default;
|
|
EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf");
|
|
}
|
|
|
|
void WorldPuzzle::Setup()
|
|
{
|
|
auto& level = GetInstance().GameLevel;
|
|
for (int32_t i = 0; i < Puzzle::Config::MaxCardsInPuzzle; ++i)
|
|
{
|
|
TileHandles[i] = level.PuzzleTiles.New();
|
|
auto& tile = level.PuzzleTiles.Get(TileHandles[i]);
|
|
tile.EData.MaterialHandle = EMaterial::Default;
|
|
|
|
for (int32_t j = 0; j < Puzzle::Config::MaxCoversInTile; ++j)
|
|
{
|
|
int32_t idx = i * Puzzle::Config::MaxCoversInTile + j;
|
|
CoverHandles[idx] = level.PuzzleTileCovers.New();
|
|
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)
|
|
{
|
|
for (int8_t x = 0; x < Data.WidthTiles / Puzzle::Config::CardSize; ++x)
|
|
{
|
|
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];
|
|
|
|
// World Tile
|
|
tile.EData.Visible = IsActive;
|
|
tile.EData.ModelH = staticCard.BaseModelHandle;
|
|
|
|
tile.EData.DotColor = visuals.TileDotColor;
|
|
tile.EData.BaseColor = visuals.TileBaseColor;
|
|
|
|
Vec3 cardPos = {
|
|
(float)card.Position.X * Puzzle::Config::CardScaleWorld,
|
|
-5.0f,
|
|
(float)card.Position.Y * Puzzle::Config::CardScaleWorld,
|
|
};
|
|
if (!isValid)
|
|
{
|
|
cardPos = {x * Puzzle::Config::CardScaleWorld, -5.0f, y * Puzzle::Config::CardScaleWorld};
|
|
}
|
|
tile.EData.Transform.Position = cardPos;
|
|
bx::mtxRotateY(tile.EData.Transform.Rotation.M, card.Rotation * bx::kPi * 0.5f);
|
|
|
|
// Covers
|
|
if (IsValid(staticCard.BaseModelHandle))
|
|
{
|
|
auto& model = GameRendering::Get().Models[staticCard.BaseModelHandle.ModelIdx];
|
|
for (int32_t i = 0; i < model.SocketCount; ++i)
|
|
{
|
|
auto& cover =
|
|
level.PuzzleTileCovers.Get(CoverHandles[cardIdx * Puzzle::Config::MaxCoversInTile + i]);
|
|
cover.EData.Visible = IsActive;
|
|
cover.EData.ModelH = staticCard.Sockets[i].Model;
|
|
cover.EData.Transform = tile.EData.Transform;
|
|
Gen::TranslateLocal(cover.EData.Transform, model.Sockets[i].Pos);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // namespace Game
|