Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions assets/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,36 @@
"channel1": "InvRed",
"channelSourceTexture0": 0,
"channelSourceTexture1": 1
},
{
"input": "UI/ToolsMenu/ToolsMenu_Tool0.png",
"output": "UI/ToolsMenu/ToolsMenu_Tool0.dds",
"method": "Texture"
},
{
"input": "UI/ToolsMenu/ToolsMenu_Tool1.png",
"output": "UI/ToolsMenu/ToolsMenu_Tool1.dds",
"method": "Texture"
},
{
"input": "UI/ToolsMenu/ToolsMenu_Tool2.png",
"output": "UI/ToolsMenu/ToolsMenu_Tool2.dds",
"method": "Texture"
},
{
"input": "UI/ToolsMenu/ToolsMenu_Tool3.png",
"output": "UI/ToolsMenu/ToolsMenu_Tool3.dds",
"method": "Texture"
},
{
"input": "UI/ToolsMenu/ToolsMenu_Tool4.png",
"output": "UI/ToolsMenu/ToolsMenu_Tool4.dds",
"method": "Texture"
},
{
"input": "UI/ToolsMenu/ToolsMenu_Tool5.png",
"output": "UI/ToolsMenu/ToolsMenu_Tool5.dds",
"method": "Texture"
}
]
}
58 changes: 58 additions & 0 deletions include/ClientLib/ToolsMenu.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (C) 2026 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "This Space Of Mine" project
// For conditions of distribution and use, see copyright notice in LICENSE

#pragma once

#ifndef TSOM_CLIENTLIB_TOOLSMENU_HPP
#define TSOM_CLIENTLIB_TOOLSMENU_HPP

#include <ClientLib/Export.hpp>
#include <Nazara/Widgets/Canvas.hpp>

#include <string>

namespace Nz
{
class FilesystemAppComponent;
class ImageWidget;
class LabelWidget;
}

namespace tsom
{
class TSOM_CLIENTLIB_API ToolsMenu : public Nz::BaseWidget
{
public:
ToolsMenu(Nz::BaseWidget* parent, Nz::FilesystemAppComponent& filesystem, uint8_t selectedTool);
ToolsMenu(const ToolsMenu&) = delete;
ToolsMenu(ToolsMenu&&) = delete;
~ToolsMenu() = default;

ToolsMenu& operator=(const ToolsMenu&) = delete;
ToolsMenu& operator=(ToolsMenu&&) = delete;

void HandleMouseMoved(float mouseX, float mouseY, uint8_t selectedTool);

inline const uint8_t GetHoveredTool() const;

private:
void Layout() override;

void HoverTool(uint8_t toolIndex, uint8_t oldToolIndex);

uint8_t m_hoveredTool;

Nz::FilesystemAppComponent& m_filesystem;

std::array<std::shared_ptr<Nz::MaterialInstance>, 6> m_imageMaterials;
Nz::ImageWidget* m_image;

std::array<std::string, 6> m_toolTexts = { "None", "Block", "Place\nEntity", "Remove\nEntity", "Connect", "Grab" };
std::array<Nz::LabelWidget*, 6> m_toolLabelWidgets;
};
}

#include <ClientLib/ToolsMenu.inl>

#endif // TSOM_CLIENTLIB_TOOLSMENU_HPP
11 changes: 11 additions & 0 deletions include/ClientLib/ToolsMenu.inl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (C) 2026 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "This Space Of Mine" project
// For conditions of distribution and use, see copyright notice in LICENSE

namespace tsom
{
inline const uint8_t ToolsMenu::GetHoveredTool() const
{
return m_hoveredTool;
}
}
85 changes: 85 additions & 0 deletions src/ClientLib/ToolsMenu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (C) 2026 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "This Space Of Mine" project
// For conditions of distribution and use, see copyright notice in LICENSE

#include <ClientLib/ToolsMenu.hpp>

#include <Nazara/Core/FilesystemAppComponent.hpp>
#include <Nazara/Graphics/TextureAsset.hpp>
#include <Nazara/TextRenderer/SimpleTextDrawer.hpp>
#include <Nazara/Widgets.hpp>

#include <fmt/format.h>

namespace tsom
{
ToolsMenu::ToolsMenu(Nz::BaseWidget* parent, Nz::FilesystemAppComponent& filesystem, uint8_t selectedTool) :
BaseWidget(parent),
m_hoveredTool(selectedTool),
m_filesystem(filesystem)
{
//EnableBackground(true);
//SetBackgroundColor(Nz::Color(0, 0, 0, 0.6f));

for (size_t i = 0; i < m_imageMaterials.size(); i++) {
std::shared_ptr<Nz::MaterialInstance>& imageMaterial = m_imageMaterials[i];
imageMaterial = Nz::MaterialInstance::Instantiate(Nz::MaterialType::Basic, Nz::MaterialInstancePreset::UI);
imageMaterial->SetTextureProperty("BaseColorMap", m_filesystem.Open<Nz::TextureAsset>(fmt::format("CookedAssets/UI/ToolsMenu/ToolsMenu_Tool{0}.dds", i), { .sRGB = true }));
}

m_image = Add<Nz::ImageWidget>(m_imageMaterials[m_hoveredTool]);
m_image->Resize({ 600.f, 600.f });

for (size_t i = 0; i < m_toolLabelWidgets.size(); i++) {
Nz::LabelWidget*& toolLabelWidget = m_toolLabelWidgets[i];
toolLabelWidget = Add<Nz::LabelWidget>();
toolLabelWidget->UpdateText(Nz::SimpleTextDrawer::Draw(m_toolTexts[i], 30, Nz::TextStyle_Regular, (i == m_hoveredTool ? Nz::Color::Black() : Nz::Color::White())));
}

Resize(m_image->GetSize());
}

void ToolsMenu::HandleMouseMoved(float mouseX, float mouseY, uint8_t selectedTool)
{
Nz::Vector3f centerPosition = m_image->GetGlobalPosition() + m_image->GetSize() / 2.f;
Nz::Vector2f centeredMousePosition = { mouseX - centerPosition.x , mouseY - centerPosition.y };

uint8_t previousHoveredTool = m_hoveredTool;
if (centeredMousePosition.GetLength() >= m_image->GetSize()[0] / 4.f)
{
float angle = std::atan2(-centeredMousePosition.x, centeredMousePosition.y);
m_hoveredTool = static_cast<uint8_t>(round(m_imageMaterials.size() * (angle / Nz::Pi<float>() + 1.f) / 2.f)) % m_imageMaterials.size();
}
else
{
m_hoveredTool = selectedTool;
}

HoverTool(m_hoveredTool, previousHoveredTool);
Layout();
}

void ToolsMenu::HoverTool(uint8_t newToolIndex, uint8_t oldToolIndex)
{
m_image->SetMaterial(m_imageMaterials[newToolIndex % m_imageMaterials.size()]);
m_toolLabelWidgets[oldToolIndex]->UpdateText(Nz::SimpleTextDrawer::Draw(m_toolTexts[oldToolIndex], 30, Nz::TextStyle_Regular, Nz::Color::White()));
m_toolLabelWidgets[newToolIndex]->UpdateText(Nz::SimpleTextDrawer::Draw(m_toolTexts[newToolIndex], 30, Nz::TextStyle_Regular, Nz::Color::Black()));
}

void ToolsMenu::Layout()
{
BaseWidget::Layout();

m_image->Center();

float distFromCenter = m_image->GetSize()[0] * 3.f / 8.f;
for (size_t i = 0; i < m_toolLabelWidgets.size(); i++) {
Nz::LabelWidget*& toolLabelWidget = m_toolLabelWidgets[i];

float angle = i * 2.f * Nz::Pi<float>() / m_toolLabelWidgets.size();

toolLabelWidget->Center();
toolLabelWidget->Move(Nz::Vector2f({ distFromCenter * sinf(angle), distFromCenter * cosf(angle) }));
}
}
}
91 changes: 71 additions & 20 deletions src/Game/States/GameState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <ClientLib/Components/EnvironmentComponent.hpp>
#include <ClientLib/Systems/AnimationSystem.hpp>
#include <ClientLib/Systems/CameraFollowerSystem.hpp>
#include <ClientLib/ToolsMenu.hpp>
#include <ClientLib/Tools/BlockTool.hpp>
#include <ClientLib/Tools/ConnectTool.hpp>
#include <ClientLib/Tools/GrabEntityTool.hpp>
Expand Down Expand Up @@ -317,6 +318,12 @@ namespace tsom
UpdateMouseLock();
});

m_toolsMenu = CreateWidget<ToolsMenu>(filesystem, m_toolIndex);
m_toolsMenu->OnWidgetVisibilityUpdated.Connect([&](const Nz::BaseWidget* /*widget*/, bool /*isVisible*/)
{
UpdateMouseLock();
});

m_chatBox = CreateWidget<Chatbox>();
m_chatBox->OnChatMessage.Connect([&](const std::string& message)
{
Expand Down Expand Up @@ -402,19 +409,13 @@ namespace tsom

case Nz::Keyboard::Scancode::Tab:
{
m_tools[m_toolIndex]->OnDeactivate();

m_toolIndex++;
if (m_toolIndex >= m_tools.size())
m_toolIndex = 0;

m_tools[m_toolIndex]->OnActivate();
spdlog::info("New tool: {}", m_tools[m_toolIndex]->GetName());

UpdateMouseLock();
if (!m_toolsMenu->IsVisible() && !m_escapeMenu->IsVisible())
{
Nz::Mouse::SetPosition(static_cast<Nz::Vector2f>(stateData.renderTarget->GetSize()) / 2.f, *stateData.window);
m_toolsMenu->Show();
UpdateMouseLock();
}

m_blockSelectionBar->Show(m_toolIndex == 1 /*BlockTool*/ && !m_pilotedShip.has_value());
LayoutWidgets(Nz::Vector2f(GetStateData().renderTarget->GetSize()));
break;
}

Expand All @@ -431,7 +432,12 @@ namespace tsom
else if (m_pilotedShip)
stateData.networkSession->SendPacket(Packets::C_ExitShipControl{});
else
{
if (m_toolsMenu->IsVisible())
m_toolsMenu->Hide();

m_escapeMenu->Show();
}

UpdateMouseLock();
break;
Expand Down Expand Up @@ -560,6 +566,41 @@ namespace tsom
}
});

m_onUnhandledKeyReleased.Connect(stateData.canvas->OnUnhandledKeyReleased, [this](const Nz::WindowEventHandler*, const Nz::WindowEvent::KeyEvent& event)
{
auto& stateData = GetStateData();

switch (event.scancode)
{
case Nz::Keyboard::Scancode::Tab:
{
if (m_toolsMenu->IsVisible())
{
m_toolsMenu->Hide();

m_tools[m_toolIndex]->OnDeactivate();

size_t newTool = static_cast<size_t>(m_toolsMenu->GetHoveredTool());
if (newTool != m_toolIndex)
{
m_toolIndex = newTool;
m_tools[m_toolIndex]->OnActivate();
spdlog::info("New tool: {}", m_tools[m_toolIndex]->GetName());

UpdateMouseLock();

m_blockSelectionBar->Show(m_toolIndex == 1 /*BlockTool*/ && !m_pilotedShip.has_value());
LayoutWidgets(Nz::Vector2f(stateData.renderTarget->GetSize()));
}
}
break;
}

default:
break;
}
});

m_onChatMessage.Connect(stateData.sessionHandler->OnChatMessage, [this](const std::string& message)
{
m_chatBox->PrintMessage({
Expand Down Expand Up @@ -780,7 +821,7 @@ namespace tsom

void GameState::UpdateMouseLock()
{
m_isMouseLocked = !m_chatBox->IsTyping() && !m_escapeMenu->IsVisible() && !m_localConsole->IsVisible() && !m_remoteConsole->IsVisible() && !m_tools[m_toolIndex]->IsCursorUnlocked();
m_isMouseLocked = !m_chatBox->IsTyping() && !m_escapeMenu->IsVisible() && !m_toolsMenu->IsVisible() && !m_localConsole->IsVisible() && !m_remoteConsole->IsVisible() && !m_tools[m_toolIndex]->IsCursorUnlocked();
m_chatBox->EnableMouseInput(!m_isMouseLocked);
m_localConsole->EnableMouseInput(!m_isMouseLocked);
m_remoteConsole->EnableMouseInput(!m_isMouseLocked);
Expand All @@ -799,8 +840,10 @@ namespace tsom

m_chatBox->Close();
m_escapeMenu->Hide();
m_toolsMenu->Hide();
m_localConsole->Hide();
m_remoteConsole->Hide();
m_blockSelectionBar->Show(m_toolIndex == 1 /*BlockTool*/ && !m_pilotedShip.has_value());

auto& stateData = GetStateData();
LayoutWidgets(Nz::Vector2f(stateData.renderTarget->GetSize()));
Expand All @@ -814,16 +857,23 @@ namespace tsom
float mouseSensitivity = config.GetFloatValue<float>(Config::Input_MouseSensitivity);
m_mouseMovedSlot.Connect(stateData.canvas->OnUnhandledMouseMoved, [&, mouseSensitivity](const Nz::WindowEventHandler*, const Nz::WindowEvent::MouseMoveEvent& event)
{
if (!m_isMouseLocked)
return;

auto& stateData = GetStateData();

float pitchMod = -event.deltaY * mouseSensitivity;
float yawMod = -event.deltaX * mouseSensitivity;
if (m_isMouseLocked)
{
float pitchMod = -event.deltaY * mouseSensitivity;
float yawMod = -event.deltaX * mouseSensitivity;

m_incomingCameraRotation.pitch += pitchMod;
m_incomingCameraRotation.yaw += yawMod;
m_incomingCameraRotation.pitch += pitchMod;
m_incomingCameraRotation.yaw += yawMod;
}
else
{
if (m_toolsMenu->IsVisible())
{
m_toolsMenu->HandleMouseMoved(event.x, event.y, m_toolIndex);
}
}
});

m_mouseButtonReleasedSlot.Connect(stateData.canvas->OnUnhandledMouseButtonReleased, [&](const Nz::WindowEventHandler*, const Nz::WindowEvent::MouseButtonEvent& event)
Expand Down Expand Up @@ -1255,6 +1305,7 @@ namespace tsom
m_healthOxygen.entity.get<Nz::NodeComponent>().SetPosition({ newSize.x * 0.5f - m_healthOxygen.entity.get<Nz::GraphicsComponent>().GetAABB().x / 2.f, hudNextHeight });

m_escapeMenu->Center();
m_toolsMenu->Center();
}

Nz::Vector3f GameState::RaycastCamera(const Nz::Vector3f& from, const Nz::Vector3f& to)
Expand Down
2 changes: 2 additions & 0 deletions src/Game/States/GameState.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ namespace tsom
class EscapeMenu;
struct StateData;
class ToolBase;
class ToolsMenu;

class GameState : public WidgetState, public GameInterface
{
Expand Down Expand Up @@ -192,6 +193,7 @@ namespace tsom
Console* m_localConsole;
Console* m_remoteConsole;
EscapeMenu* m_escapeMenu;
ToolsMenu* m_toolsMenu;
bool m_isMouseLocked;
float m_currentCameraDistance;
float m_defaultCameraDistance;
Expand Down
Loading