Compare commits
3 Commits
3e284af672
...
35008f9304
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35008f9304 | ||
|
|
e4aded6099 | ||
|
|
b6f6b28c98 |
@ -114,6 +114,8 @@ set(CLIENT_ONLY_SOURCES
|
|||||||
"src/gui/context.cpp"
|
"src/gui/context.cpp"
|
||||||
"src/gui/font.hpp"
|
"src/gui/font.hpp"
|
||||||
"src/gui/font.cpp"
|
"src/gui/font.cpp"
|
||||||
|
"src/gui/menu.hpp"
|
||||||
|
"src/gui/menu.cpp"
|
||||||
"src/utils/files.cpp"
|
"src/utils/files.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -35,27 +35,6 @@ void App::Frame()
|
|||||||
delta_time_ = 0.1f; // Cap delta time to avoid large jumps
|
delta_time_ = 0.1f; // Cap delta time to avoid large jumps
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect inputs originating in this frame
|
|
||||||
//game::PlayerInputFlags new_input = input_ & ~prev_input_;
|
|
||||||
|
|
||||||
// detect input changes
|
|
||||||
for (size_t i = 0; i < game::IN__COUNT; ++i)
|
|
||||||
{
|
|
||||||
auto in_old = prev_input_ & (1 << i);
|
|
||||||
auto in_new = input_ & (1 << i);
|
|
||||||
|
|
||||||
if (in_old > in_new) // released
|
|
||||||
{
|
|
||||||
SendInput(static_cast<game::PlayerInputType>(i), false);
|
|
||||||
}
|
|
||||||
else if (in_new > in_old) // pressed
|
|
||||||
{
|
|
||||||
SendInput(static_cast<game::PlayerInputType>(i), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prev_input_ = input_;
|
|
||||||
|
|
||||||
if (session_)
|
if (session_)
|
||||||
{
|
{
|
||||||
game::view::UpdateInfo updinfo;
|
game::view::UpdateInfo updinfo;
|
||||||
@ -76,18 +55,31 @@ void App::Frame()
|
|||||||
|
|
||||||
gui_.Begin();
|
gui_.Begin();
|
||||||
|
|
||||||
const game::view::WorldView* world;
|
// draw session
|
||||||
if (session_)
|
if (session_)
|
||||||
{
|
{
|
||||||
session_->Draw(dlist_, params, gui_);
|
session_->Draw(dlist_, params, gui_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// draw stats
|
||||||
|
UpdateStats();
|
||||||
|
DrawStats();
|
||||||
|
|
||||||
// draw chat
|
// draw chat
|
||||||
UpdateChat();
|
UpdateChat();
|
||||||
DrawChat();
|
DrawChat();
|
||||||
|
|
||||||
|
// draw menu
|
||||||
|
if (menu_)
|
||||||
|
{
|
||||||
|
auto menu_size = menu_->MeasureSize();
|
||||||
|
menu_->Draw(gui_, glm::vec2(viewport_size_) - menu_size - 10.0f);
|
||||||
|
}
|
||||||
|
|
||||||
gui_.Render();
|
gui_.Render();
|
||||||
renderer_.DrawList(dlist_, params);
|
renderer_.DrawList(dlist_, params);
|
||||||
|
|
||||||
|
++stat_frames_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::Connected()
|
void App::Connected()
|
||||||
@ -97,11 +89,6 @@ void App::Connected()
|
|||||||
|
|
||||||
// init session
|
// init session
|
||||||
session_ = std::make_unique<game::view::ClientSession>(*this);
|
session_ = std::make_unique<game::view::ClientSession>(*this);
|
||||||
|
|
||||||
// send login
|
|
||||||
auto msg = BeginMsg(net::MSG_ID);
|
|
||||||
net::PlayerName name;
|
|
||||||
msg.Write(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::ProcessMessage(net::InMessage& msg)
|
void App::ProcessMessage(net::InMessage& msg)
|
||||||
@ -118,6 +105,12 @@ void App::ProcessMessage(net::InMessage& msg)
|
|||||||
{
|
{
|
||||||
std::cerr << "FAILED to process message!" << std::endl;
|
std::cerr << "FAILED to process message!" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// record stats
|
||||||
|
++stat_msgs_;
|
||||||
|
stat_msglen_total_ += s;
|
||||||
|
stat_msglen_min_ = std::min(stat_msglen_min_, s);
|
||||||
|
stat_msglen_max_ = std::max(stat_msglen_max_, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::Disconnected(const std::string& reason)
|
void App::Disconnected(const std::string& reason)
|
||||||
@ -130,6 +123,40 @@ void App::Disconnected(const std::string& reason)
|
|||||||
session_.reset();
|
session_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool InputToMenuInput(game::PlayerInputType in, gui::MenuInput& mi)
|
||||||
|
{
|
||||||
|
switch (in)
|
||||||
|
{
|
||||||
|
case game::IN_FORWARD: mi = gui::MI_UP; return true;
|
||||||
|
case game::IN_BACKWARD: mi = gui::MI_DOWN; return true;
|
||||||
|
case game::IN_LEFT: mi = gui::MI_LEFT; return true;
|
||||||
|
case game::IN_RIGHT: mi = gui::MI_RIGHT; return true;
|
||||||
|
case game::IN_JUMP: mi = gui::MI_ENTER; return true;
|
||||||
|
case game::IN_CROUCH: mi = gui::MI_BACK; return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void App::Input(game::PlayerInputType in, bool pressed, bool repeated)
|
||||||
|
{
|
||||||
|
if (in == game::IN_MENU && pressed)
|
||||||
|
{
|
||||||
|
OpenSettings();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gui::MenuInput mi;
|
||||||
|
if (menu_ && pressed && InputToMenuInput(in, mi))
|
||||||
|
{
|
||||||
|
menu_->Input(mi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session_)
|
||||||
|
session_->Input(in, pressed, repeated);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void App::MouseMove(const glm::vec2& delta)
|
void App::MouseMove(const glm::vec2& delta)
|
||||||
{
|
{
|
||||||
float sensitivity = 0.002f; // Sensitivity factor for mouse movement
|
float sensitivity = 0.002f; // Sensitivity factor for mouse movement
|
||||||
@ -156,15 +183,6 @@ void App::AddChatMessagePrefix(const std::string& prefix, const std::string& tex
|
|||||||
|
|
||||||
App::~App() {}
|
App::~App() {}
|
||||||
|
|
||||||
void App::SendInput(game::PlayerInputType type, bool enable)
|
|
||||||
{
|
|
||||||
auto msg = BeginMsg(net::MSG_IN);
|
|
||||||
uint8_t val = type;
|
|
||||||
if (enable)
|
|
||||||
val |= 128;
|
|
||||||
msg.Write(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void App::UpdateChat()
|
void App::UpdateChat()
|
||||||
{
|
{
|
||||||
// remove expired or over the limit messages
|
// remove expired or over the limit messages
|
||||||
@ -191,3 +209,74 @@ void App::DrawChat()
|
|||||||
gui_.DrawText(chat_[i].text, pos, color);
|
gui_.DrawText(chat_[i].text, pos, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void AddSlider(gui::Menu& menu, std::string text, int& value, int min, int max)
|
||||||
|
{
|
||||||
|
auto& slider = menu.Add<gui::SelectMenuItem>(std::move(text));
|
||||||
|
auto on_switch = [&slider, &value, min, max] (int v) {
|
||||||
|
value += v;
|
||||||
|
|
||||||
|
// clamp
|
||||||
|
if (value < min)
|
||||||
|
value = min;
|
||||||
|
else if (value > max)
|
||||||
|
value = max;
|
||||||
|
|
||||||
|
slider.SetSelectionText(std::to_string(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
slider.SetSwitchCallback(on_switch);
|
||||||
|
on_switch(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::OpenSettings()
|
||||||
|
{
|
||||||
|
menu_ = std::make_unique<gui::Menu>();
|
||||||
|
|
||||||
|
AddSlider(*menu_, "jak moc to řve", volume_, 0, 100);
|
||||||
|
|
||||||
|
auto& ok = menu_->Add<gui::ButtonMenuItem>("0k");
|
||||||
|
ok.SetClickCallback([this] { menu_.reset(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
#define COL_LABEL "^ccc"
|
||||||
|
#define COL_VALUE "^5ff"
|
||||||
|
|
||||||
|
void App::UpdateStats()
|
||||||
|
{
|
||||||
|
if (time_ < stats_time_ + 1.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
stats_time_ = time_;
|
||||||
|
|
||||||
|
fps_text_.clear();
|
||||||
|
fps_text_ += COL_VALUE;
|
||||||
|
fps_text_ += std::to_string(stat_frames_);
|
||||||
|
fps_text_ += COL_LABEL " fps";
|
||||||
|
|
||||||
|
if (stat_msgs_ > 0)
|
||||||
|
{
|
||||||
|
msglen_text_ = COL_LABEL "net: n=" COL_VALUE;
|
||||||
|
msglen_text_ += std::to_string(stat_msgs_);
|
||||||
|
msglen_text_ += COL_LABEL " min=" COL_VALUE;
|
||||||
|
msglen_text_ += std::to_string(stat_msglen_min_);
|
||||||
|
msglen_text_ += COL_LABEL " max=" COL_VALUE;
|
||||||
|
msglen_text_ += std::to_string(stat_msglen_max_);
|
||||||
|
msglen_text_ += COL_LABEL " total=" COL_VALUE;
|
||||||
|
msglen_text_ += std::to_string(stat_msglen_total_);
|
||||||
|
}
|
||||||
|
|
||||||
|
stat_frames_ = 0;
|
||||||
|
stat_msgs_ = 0;
|
||||||
|
stat_msglen_total_ = 0;
|
||||||
|
stat_msglen_min_ = SIZE_MAX;
|
||||||
|
stat_msglen_max_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::DrawStats()
|
||||||
|
{
|
||||||
|
glm::vec2 pos(viewport_size_.x - 5.0f, 5.0f);
|
||||||
|
gui_.DrawTextAligned(fps_text_, pos, glm::vec2(-1.0f, 0.0f));
|
||||||
|
pos.y += 30.0f;
|
||||||
|
gui_.DrawTextAligned(msglen_text_, pos, glm::vec2(-1.0f, 0.0f));
|
||||||
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
#include "audio/master.hpp"
|
#include "audio/master.hpp"
|
||||||
#include "net/msg_producer.hpp"
|
#include "net/msg_producer.hpp"
|
||||||
#include "net/inmessage.hpp"
|
#include "net/inmessage.hpp"
|
||||||
|
#include "gui/menu.hpp"
|
||||||
#include "gameview/client_session.hpp"
|
#include "gameview/client_session.hpp"
|
||||||
|
|
||||||
struct ChatMessage
|
struct ChatMessage
|
||||||
@ -20,7 +20,7 @@ struct ChatMessage
|
|||||||
glm::vec4 color = glm::vec4(1.0f);
|
glm::vec4 color = glm::vec4(1.0f);
|
||||||
};
|
};
|
||||||
|
|
||||||
class App : public net::MsgProducer
|
class App
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
App();
|
App();
|
||||||
@ -33,12 +33,15 @@ public:
|
|||||||
|
|
||||||
void SetTime(float time) { time_ = time; }
|
void SetTime(float time) { time_ = time; }
|
||||||
void SetViewportSize(int width, int height) { viewport_size_ = {width, height}; }
|
void SetViewportSize(int width, int height) { viewport_size_ = {width, height}; }
|
||||||
void SetInput(game::PlayerInputFlags input) { input_ = input; }
|
|
||||||
|
void Input(game::PlayerInputType in, bool pressed, bool repeated);
|
||||||
void MouseMove(const glm::vec2& delta);
|
void MouseMove(const glm::vec2& delta);
|
||||||
|
|
||||||
float GetTime() const { return time_; }
|
float GetTime() const { return time_; }
|
||||||
float GetDeltaTime() const { return delta_time_; }
|
float GetDeltaTime() const { return delta_time_; }
|
||||||
|
|
||||||
|
game::view::ClientSession* GetSession() { return session_.get(); }
|
||||||
|
|
||||||
audio::Master& GetAudioMaster() { return audiomaster_; }
|
audio::Master& GetAudioMaster() { return audiomaster_; }
|
||||||
|
|
||||||
void AddChatMessage(const std::string& text);
|
void AddChatMessage(const std::string& text);
|
||||||
@ -47,16 +50,17 @@ public:
|
|||||||
~App();
|
~App();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SendInput(game::PlayerInputType type, bool enable);
|
|
||||||
|
|
||||||
void UpdateChat();
|
void UpdateChat();
|
||||||
void DrawChat();
|
void DrawChat();
|
||||||
|
|
||||||
|
void OpenSettings();
|
||||||
|
|
||||||
|
void UpdateStats();
|
||||||
|
void DrawStats();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float time_ = 0.0f;
|
float time_ = 0.0f;
|
||||||
glm::ivec2 viewport_size_ = {800, 600};
|
glm::ivec2 viewport_size_ = {800, 600};
|
||||||
game::PlayerInputFlags input_ = 0;
|
|
||||||
game::PlayerInputFlags prev_input_ = 0;
|
|
||||||
|
|
||||||
float prev_time_ = 0.0f;
|
float prev_time_ = 0.0f;
|
||||||
float delta_time_ = 0.0f;
|
float delta_time_ = 0.0f;
|
||||||
@ -70,4 +74,19 @@ private:
|
|||||||
std::unique_ptr<game::view::ClientSession> session_;
|
std::unique_ptr<game::view::ClientSession> session_;
|
||||||
|
|
||||||
std::deque<ChatMessage> chat_;
|
std::deque<ChatMessage> chat_;
|
||||||
|
|
||||||
|
std::unique_ptr<gui::Menu> menu_;
|
||||||
|
|
||||||
|
// settings
|
||||||
|
int volume_ = 50;
|
||||||
|
|
||||||
|
// stats
|
||||||
|
float stats_time_ = 0.0f;
|
||||||
|
size_t stat_frames_ = 0;
|
||||||
|
size_t stat_msgs_ = 0;
|
||||||
|
size_t stat_msglen_total_ = 0;
|
||||||
|
size_t stat_msglen_min_ = SIZE_MAX;
|
||||||
|
size_t stat_msglen_max_ = 0;
|
||||||
|
std::string fps_text_ = { 0 };
|
||||||
|
std::string msglen_text_ = { 0 };
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
@ -138,6 +139,20 @@ static void ShutdownSDL()
|
|||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const std::map<SDL_Scancode, game::PlayerInputType> s_inputmap = {
|
||||||
|
{ SDL_SCANCODE_W, game::IN_FORWARD },
|
||||||
|
{ SDL_SCANCODE_S, game::IN_BACKWARD },
|
||||||
|
{ SDL_SCANCODE_A, game::IN_LEFT },
|
||||||
|
{ SDL_SCANCODE_D, game::IN_RIGHT },
|
||||||
|
{ SDL_SCANCODE_SPACE, game::IN_JUMP },
|
||||||
|
{ SDL_SCANCODE_LCTRL, game::IN_CROUCH },
|
||||||
|
{ SDL_SCANCODE_E, game::IN_USE },
|
||||||
|
{ SDL_SCANCODE_F3, game::IN_DEBUG1 },
|
||||||
|
{ SDL_SCANCODE_F4, game::IN_DEBUG2 },
|
||||||
|
{ SDL_SCANCODE_F5, game::IN_DEBUG3 },
|
||||||
|
{ SDL_SCANCODE_TAB, game::IN_MENU },
|
||||||
|
};
|
||||||
|
|
||||||
static void PollEvents()
|
static void PollEvents()
|
||||||
{
|
{
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
@ -150,13 +165,27 @@ static void PollEvents()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case SDL_MOUSEMOTION:
|
case SDL_MOUSEMOTION:
|
||||||
int xrel = event.motion.xrel;
|
|
||||||
int yrel = event.motion.yrel;
|
|
||||||
if (xrel != 0 || yrel != 0)
|
|
||||||
{
|
{
|
||||||
s_app->MouseMove(glm::vec2(static_cast<float>(xrel), static_cast<float>(yrel)));
|
int xrel = event.motion.xrel;
|
||||||
|
int yrel = event.motion.yrel;
|
||||||
|
if (xrel != 0 || yrel != 0)
|
||||||
|
{
|
||||||
|
s_app->MouseMove(glm::vec2(static_cast<float>(xrel), static_cast<float>(yrel)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
case SDL_KEYUP:
|
||||||
|
{
|
||||||
|
auto in_it = s_inputmap.find(event.key.keysym.scancode);
|
||||||
|
if (in_it != s_inputmap.end())
|
||||||
|
{
|
||||||
|
s_app->Input(in_it->second, event.key.state == SDL_PRESSED, event.key.repeat != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -326,55 +355,29 @@ static void Frame()
|
|||||||
game::PlayerInputFlags input = 0;
|
game::PlayerInputFlags input = 0;
|
||||||
const uint8_t* kbd_state = SDL_GetKeyboardState(nullptr);
|
const uint8_t* kbd_state = SDL_GetKeyboardState(nullptr);
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_w)])
|
|
||||||
input |= (1 << game::IN_FORWARD);
|
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_s)])
|
|
||||||
input |= (1 << game::IN_BACKWARD);
|
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_a)])
|
|
||||||
input |= (1 << game::IN_LEFT);
|
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_d)])
|
|
||||||
input |= (1 << game::IN_RIGHT);
|
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_SPACE)])
|
|
||||||
input |= (1 << game::IN_JUMP);
|
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_LCTRL)])
|
|
||||||
input |= (1 << game::IN_CROUCH);
|
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_e)])
|
|
||||||
input |= (1 << game::IN_USE);
|
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_F3)])
|
|
||||||
input |= (1 << game::IN_DEBUG1);
|
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_F4)])
|
|
||||||
input |= (1 << game::IN_DEBUG2);
|
|
||||||
|
|
||||||
if (kbd_state[SDL_GetScancodeFromKey(SDLK_F5)])
|
|
||||||
input |= (1 << game::IN_DEBUG3);
|
|
||||||
|
|
||||||
int mouse_state = SDL_GetMouseState(nullptr, nullptr);
|
int mouse_state = SDL_GetMouseState(nullptr, nullptr);
|
||||||
|
|
||||||
if (mouse_state & SDL_BUTTON(SDL_BUTTON_LEFT))
|
if (mouse_state & SDL_BUTTON(SDL_BUTTON_LEFT))
|
||||||
input |= (1 << game::IN_ATTACK);
|
input |= (1 << game::IN_ATTACK);
|
||||||
|
|
||||||
s_app->SetInput(input);
|
|
||||||
|
|
||||||
s_app->Frame();
|
s_app->Frame();
|
||||||
|
|
||||||
if (s_ws_connected)
|
auto session = s_app->GetSession();
|
||||||
|
if (session)
|
||||||
{
|
{
|
||||||
auto msg = s_app->GetMsg();
|
if (s_ws_connected)
|
||||||
if (!msg.empty())
|
|
||||||
{
|
{
|
||||||
WSSend(msg);
|
auto msg = session->GetMsg();
|
||||||
|
if (!msg.empty())
|
||||||
|
{
|
||||||
|
WSSend(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
s_app->ResetMsg();
|
session->ResetMsg();
|
||||||
|
}
|
||||||
|
|
||||||
SDL_GL_SwapWindow(s_window);
|
SDL_GL_SwapWindow(s_window);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ namespace game
|
|||||||
IN_DEBUG3,
|
IN_DEBUG3,
|
||||||
IN_DEBUG4,
|
IN_DEBUG4,
|
||||||
IN_DEBUG5,
|
IN_DEBUG5,
|
||||||
|
IN_MENU,
|
||||||
|
|
||||||
IN__COUNT,
|
IN__COUNT,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,7 +6,13 @@
|
|||||||
|
|
||||||
#include "vehicleview.hpp"
|
#include "vehicleview.hpp"
|
||||||
|
|
||||||
game::view::ClientSession::ClientSession(App& app) : app_(app) {}
|
game::view::ClientSession::ClientSession(App& app) : app_(app)
|
||||||
|
{
|
||||||
|
// send login
|
||||||
|
auto msg = BeginMsg(net::MSG_ID);
|
||||||
|
net::PlayerName name;
|
||||||
|
msg.Write(name);
|
||||||
|
}
|
||||||
|
|
||||||
bool game::view::ClientSession::ProcessMessage(net::InMessage& msg)
|
bool game::view::ClientSession::ProcessMessage(net::InMessage& msg)
|
||||||
{
|
{
|
||||||
@ -48,6 +54,17 @@ bool game::view::ClientSession::ProcessSingleMessage(net::MessageType type, net:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::view::ClientSession::Input(game::PlayerInputType in, bool pressed, bool repeated)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (repeated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SendInput(in, pressed);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void game::view::ClientSession::ProcessMouseMove(float delta_yaw, float delta_pitch)
|
void game::view::ClientSession::ProcessMouseMove(float delta_yaw, float delta_pitch)
|
||||||
{
|
{
|
||||||
yaw_ = glm::mod(yaw_ + delta_yaw, glm::two_pi<float>());
|
yaw_ = glm::mod(yaw_ + delta_yaw, glm::two_pi<float>());
|
||||||
@ -171,6 +188,15 @@ void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListPar
|
|||||||
GetAudioMaster().SetListenerOrientation(camera_world);
|
GetAudioMaster().SetListenerOrientation(camera_world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::view::ClientSession::SendInput(game::PlayerInputType type, bool enable)
|
||||||
|
{
|
||||||
|
auto msg = BeginMsg(net::MSG_IN);
|
||||||
|
uint8_t val = type;
|
||||||
|
if (enable)
|
||||||
|
val |= 128;
|
||||||
|
msg.Write(val);
|
||||||
|
}
|
||||||
|
|
||||||
void game::view::ClientSession::SendViewAngles(float time)
|
void game::view::ClientSession::SendViewAngles(float time)
|
||||||
{
|
{
|
||||||
if (time - last_send_time_ < 0.040f)
|
if (time - last_send_time_ < 0.040f)
|
||||||
@ -184,7 +210,7 @@ void game::view::ClientSession::SendViewAngles(float time)
|
|||||||
if (yaw_q.value == view_yaw_q_.value && pitch_q.value == view_pitch_q_.value)
|
if (yaw_q.value == view_yaw_q_.value && pitch_q.value == view_pitch_q_.value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto msg = app_.BeginMsg(net::MSG_VIEWANGLES);
|
auto msg = BeginMsg(net::MSG_VIEWANGLES);
|
||||||
msg.Write(yaw_q.value);
|
msg.Write(yaw_q.value);
|
||||||
msg.Write(pitch_q.value);
|
msg.Write(pitch_q.value);
|
||||||
|
|
||||||
|
|||||||
@ -8,13 +8,15 @@
|
|||||||
#include "gfx/renderer.hpp"
|
#include "gfx/renderer.hpp"
|
||||||
#include "net/defs.hpp"
|
#include "net/defs.hpp"
|
||||||
#include "net/inmessage.hpp"
|
#include "net/inmessage.hpp"
|
||||||
|
#include "net/msg_producer.hpp"
|
||||||
|
#include "game/player_input.hpp"
|
||||||
|
|
||||||
class App;
|
class App;
|
||||||
|
|
||||||
namespace game::view
|
namespace game::view
|
||||||
{
|
{
|
||||||
|
|
||||||
class ClientSession
|
class ClientSession : public net::MsgProducer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClientSession(App& app);
|
ClientSession(App& app);
|
||||||
@ -22,15 +24,14 @@ public:
|
|||||||
bool ProcessMessage(net::InMessage& msg);
|
bool ProcessMessage(net::InMessage& msg);
|
||||||
bool ProcessSingleMessage(net::MessageType type, net::InMessage& msg);
|
bool ProcessSingleMessage(net::MessageType type, net::InMessage& msg);
|
||||||
|
|
||||||
|
void Input(game::PlayerInputType in, bool pressed, bool repeated);
|
||||||
void ProcessMouseMove(float delta_yaw, float delta_pitch);
|
void ProcessMouseMove(float delta_yaw, float delta_pitch);
|
||||||
|
|
||||||
void Update(const UpdateInfo& info);
|
void Update(const UpdateInfo& info);
|
||||||
void Draw(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui);
|
void Draw(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui);
|
||||||
|
|
||||||
const WorldView* GetWorld() const { return world_.get(); }
|
const WorldView* GetWorld() const { return world_.get(); }
|
||||||
|
|
||||||
void GetViewInfo(glm::vec3& eye, glm::mat4& view) const;
|
void GetViewInfo(glm::vec3& eye, glm::mat4& view) const;
|
||||||
|
|
||||||
audio::Master& GetAudioMaster() const;
|
audio::Master& GetAudioMaster() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -40,6 +41,8 @@ private:
|
|||||||
bool ProcessChatMsg(net::InMessage& msg);
|
bool ProcessChatMsg(net::InMessage& msg);
|
||||||
|
|
||||||
void DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui);
|
void DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui);
|
||||||
|
|
||||||
|
void SendInput(game::PlayerInputType type, bool enable);
|
||||||
void SendViewAngles(float time);
|
void SendViewAngles(float time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
|
|
||||||
|
#include "assets/cache.hpp"
|
||||||
|
|
||||||
gui::Context::Context(gfx::DrawList& dlist, std::shared_ptr<const Font> default_font) :
|
gui::Context::Context(gfx::DrawList& dlist, std::shared_ptr<const Font> default_font) :
|
||||||
dlist_(dlist),
|
dlist_(dlist),
|
||||||
font_(std::move(default_font)),
|
font_(std::move(default_font)),
|
||||||
va_(gfx::VA_POSITION | gfx::VA_UV | gfx::VA_COLOR, gfx::VF_CREATE_EBO | gfx::VF_DYNAMIC)
|
va_(gfx::VA_POSITION | gfx::VA_UV | gfx::VA_COLOR, gfx::VF_CREATE_EBO | gfx::VF_DYNAMIC)
|
||||||
{
|
{
|
||||||
|
white_tex_ = assets::CacheManager::GetTexture("data/white.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui::Context::Begin()
|
void gui::Context::Begin()
|
||||||
@ -15,6 +17,12 @@ void gui::Context::Begin()
|
|||||||
ranges_.clear();
|
ranges_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gui::Context::DrawRect(const glm::vec2& p0, const glm::vec2& p1, uint32_t color)
|
||||||
|
{
|
||||||
|
BeginTexture(white_tex_.get());
|
||||||
|
PushRect(p0, glm::vec2(0.0f), p1, glm::vec2(1.0f), color);
|
||||||
|
}
|
||||||
|
|
||||||
static uint32_t DecodeUTF8Codepoint(const char*& p, const char* end)
|
static uint32_t DecodeUTF8Codepoint(const char*& p, const char* end)
|
||||||
{
|
{
|
||||||
if (p == end)
|
if (p == end)
|
||||||
@ -131,7 +139,7 @@ void gui::Context::DrawText(std::string_view text, const glm::vec2& pos, uint32_
|
|||||||
BeginTexture(font_->GetTexture().get());
|
BeginTexture(font_->GetTexture().get());
|
||||||
|
|
||||||
uint32_t cp = 0;
|
uint32_t cp = 0;
|
||||||
const float line_height = font_->GetLineHeight();
|
const float line_height = font_->GetLineHeight() * scale;
|
||||||
float space_size = line_height * 0.3f;
|
float space_size = line_height * 0.3f;
|
||||||
|
|
||||||
glm::vec2 cursor = pos;
|
glm::vec2 cursor = pos;
|
||||||
@ -191,7 +199,7 @@ void gui::Context::DrawText(std::string_view text, const glm::vec2& pos, uint32_
|
|||||||
if (!glyph)
|
if (!glyph)
|
||||||
continue; // Dont even have "missing" glyph, font is shit
|
continue; // Dont even have "missing" glyph, font is shit
|
||||||
|
|
||||||
glm::vec2 p0 = cursor + glyph->offset;
|
glm::vec2 p0 = cursor + glyph->offset * scale;
|
||||||
glm::vec2 p1 = p0 + glyph->size * scale;
|
glm::vec2 p1 = p0 + glyph->size * scale;
|
||||||
|
|
||||||
PushRect(p0, glyph->uv0, p1, glyph->uv1, curr_color);
|
PushRect(p0, glyph->uv0, p1, glyph->uv1, curr_color);
|
||||||
@ -199,6 +207,12 @@ void gui::Context::DrawText(std::string_view text, const glm::vec2& pos, uint32_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gui::Context::DrawTextAligned(std::string_view text, const glm::vec2& pos, const glm::vec2& align, uint32_t color, float scale)
|
||||||
|
{
|
||||||
|
auto size = MeasureText(text) * scale;
|
||||||
|
DrawText(text, pos + size * align, color, scale);
|
||||||
|
}
|
||||||
|
|
||||||
void gui::Context::Render()
|
void gui::Context::Render()
|
||||||
{
|
{
|
||||||
va_.SetVBOData(vertices_.data(), vertices_.size() * sizeof(vertices_[0]));
|
va_.SetVBOData(vertices_.data(), vertices_.size() * sizeof(vertices_[0]));
|
||||||
@ -221,7 +235,7 @@ void gui::Context::BeginTexture(const gfx::Texture* texture)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
auto& range = ranges_.emplace_back();
|
auto& range = ranges_.emplace_back();
|
||||||
range.start = indices_.size();
|
range.start = indices_.size() / 3;
|
||||||
range.count = 0;
|
range.count = 0;
|
||||||
range.texture = texture;
|
range.texture = texture;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,8 +34,11 @@ public:
|
|||||||
|
|
||||||
void Begin();
|
void Begin();
|
||||||
|
|
||||||
|
void DrawRect(const glm::vec2& p0, const glm::vec2& p1, uint32_t color);
|
||||||
|
|
||||||
glm::vec2 MeasureText(std::string_view text);
|
glm::vec2 MeasureText(std::string_view text);
|
||||||
void DrawText(std::string_view text, const glm::vec2& pos, uint32_t color = 0xFFFFFFFF, float scale = 1.0f);
|
void DrawText(std::string_view text, const glm::vec2& pos, uint32_t color = 0xFFFFFFFF, float scale = 1.0f);
|
||||||
|
void DrawTextAligned(std::string_view text, const glm::vec2& pos, const glm::vec2& align, uint32_t color = 0xFFFFFFFF, float scale = 1.0f);
|
||||||
|
|
||||||
void Render();
|
void Render();
|
||||||
|
|
||||||
@ -47,9 +50,12 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
gfx::DrawList& dlist_;
|
gfx::DrawList& dlist_;
|
||||||
std::shared_ptr<const Font> font_;
|
|
||||||
gfx::VertexArray va_;
|
gfx::VertexArray va_;
|
||||||
|
|
||||||
|
// assets
|
||||||
|
std::shared_ptr<const Font> font_;
|
||||||
|
std::shared_ptr<const gfx::Texture> white_tex_;
|
||||||
|
|
||||||
// building
|
// building
|
||||||
std::vector<GuiVertex> vertices_;
|
std::vector<GuiVertex> vertices_;
|
||||||
std::vector<uint32_t> indices_;
|
std::vector<uint32_t> indices_;
|
||||||
|
|||||||
130
src/gui/menu.cpp
Normal file
130
src/gui/menu.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "menu.hpp"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
// Menu
|
||||||
|
|
||||||
|
void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const
|
||||||
|
{
|
||||||
|
// background
|
||||||
|
auto size = MeasureSize();
|
||||||
|
ctx.DrawRect(pos, pos + size, 0x55000000);
|
||||||
|
|
||||||
|
glm::vec2 cursor = pos;
|
||||||
|
for (size_t i = 0; i < items_.size(); ++i)
|
||||||
|
{
|
||||||
|
items_[i]->Draw(DrawMenuItemArgs(ctx, cursor, focus_ == i));
|
||||||
|
cursor.y += items_[i]->GetSize().y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::Menu::Input(MenuInput in)
|
||||||
|
{
|
||||||
|
switch (in)
|
||||||
|
{
|
||||||
|
case MI_UP:
|
||||||
|
SwitchFocus(-1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MI_DOWN:
|
||||||
|
SwitchFocus(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (!items_.empty())
|
||||||
|
items_[focus_]->Input(in);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 gui::Menu::MeasureSize() const
|
||||||
|
{
|
||||||
|
glm::vec2 size(0.0f);
|
||||||
|
|
||||||
|
for (const auto& item : items_)
|
||||||
|
{
|
||||||
|
const auto& itemsize = item->GetSize();
|
||||||
|
size.x = glm::max(size.x, itemsize.x);
|
||||||
|
size.y += itemsize.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::Menu::SwitchFocus(int dir)
|
||||||
|
{
|
||||||
|
if (items_.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
focus_ = (focus_ + items_.size() + dir) % items_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ButtonMenuItem
|
||||||
|
|
||||||
|
gui::ButtonMenuItem::ButtonMenuItem(std::string text)
|
||||||
|
: text_(std::move(text))
|
||||||
|
{
|
||||||
|
size_ = glm::vec2(300.0f, 30.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::ButtonMenuItem::Draw(const DrawMenuItemArgs& args) const
|
||||||
|
{
|
||||||
|
Super::Draw(args);
|
||||||
|
glm::vec2 center = args.pos + glm::vec2(0.0f, size_.y * 0.5f);
|
||||||
|
args.ctx.DrawTextAligned(text_, center, glm::vec2(0.0f, -0.5f), args.focused ? COLOR_FOCUSED : COLOR_INACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::ButtonMenuItem::Input(MenuInput in)
|
||||||
|
{
|
||||||
|
if (in == MI_ENTER && click_cb_)
|
||||||
|
click_cb_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::ButtonMenuItem::SetClickCallback(std::function<void()> click_cb)
|
||||||
|
{
|
||||||
|
click_cb_ = std::move(click_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectMenuItem
|
||||||
|
|
||||||
|
gui::SelectMenuItem::SelectMenuItem(std::string text)
|
||||||
|
: ButtonMenuItem(std::move(text))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::SelectMenuItem::Draw(const DrawMenuItemArgs& args) const
|
||||||
|
{
|
||||||
|
Super::Draw(args);
|
||||||
|
|
||||||
|
char buffer[128];
|
||||||
|
size_t len = snprintf(buffer, sizeof(buffer), "< %s >", select_text_.c_str());
|
||||||
|
|
||||||
|
glm::vec2 center_right = args.pos + glm::vec2(size_.x, size_.y * 0.5f);
|
||||||
|
args.ctx.DrawTextAligned(std::string_view(buffer, len), center_right, glm::vec2(-1.0f, -0.5f),
|
||||||
|
args.focused ? COLOR_FOCUSED : COLOR_INACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::SelectMenuItem::Input(MenuInput in)
|
||||||
|
{
|
||||||
|
switch (in)
|
||||||
|
{
|
||||||
|
case MI_LEFT:
|
||||||
|
if (switch_cb_)
|
||||||
|
switch_cb_(-1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MI_RIGHT:
|
||||||
|
if (switch_cb_)
|
||||||
|
switch_cb_(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Super::Input(in);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui::SelectMenuItem::SetSwitchCallback(std::function<void(int)> switch_cb)
|
||||||
|
{
|
||||||
|
switch_cb_ = std::move(switch_cb);
|
||||||
|
}
|
||||||
122
src/gui/menu.hpp
Normal file
122
src/gui/menu.hpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <concepts>
|
||||||
|
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace gui
|
||||||
|
{
|
||||||
|
|
||||||
|
enum MenuInput
|
||||||
|
{
|
||||||
|
MI_UP,
|
||||||
|
MI_DOWN,
|
||||||
|
MI_LEFT,
|
||||||
|
MI_RIGHT,
|
||||||
|
MI_BACK,
|
||||||
|
MI_ENTER,
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuItem;
|
||||||
|
|
||||||
|
struct DrawMenuItemArgs
|
||||||
|
{
|
||||||
|
Context& ctx;
|
||||||
|
glm::vec2 pos;
|
||||||
|
bool focused;
|
||||||
|
|
||||||
|
DrawMenuItemArgs(Context& ctx, const glm::vec2& pos, bool focused) : ctx(ctx), pos(pos), focused(focused) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Menu
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Menu() = default;
|
||||||
|
|
||||||
|
template <std::derived_from<MenuItem> T, typename... TArgs>
|
||||||
|
T& Add(TArgs&&... args)
|
||||||
|
{
|
||||||
|
auto item = std::make_unique<T>(std::forward<TArgs>(args)...);
|
||||||
|
auto& item_ref = *item;
|
||||||
|
items_.emplace_back(std::move(item));
|
||||||
|
return item_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(Context& ctx, const glm::vec2& pos) const;
|
||||||
|
void Input(MenuInput in);
|
||||||
|
|
||||||
|
glm::vec2 MeasureSize() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SwitchFocus(int dir);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<MenuItem>> items_;
|
||||||
|
size_t focus_ = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr static uint32_t COLOR_INACTIVE = 0xFFFFFFFF;
|
||||||
|
constexpr static uint32_t COLOR_FOCUSED = 0xFF00FFFF;
|
||||||
|
|
||||||
|
MenuItem() = default;
|
||||||
|
|
||||||
|
virtual void Draw(const DrawMenuItemArgs& args) const {}
|
||||||
|
virtual void Input(MenuInput in) {}
|
||||||
|
|
||||||
|
const glm::vec2& GetSize() const { return size_; }
|
||||||
|
|
||||||
|
virtual ~MenuItem() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
glm::vec2 size_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ButtonMenuItem : public MenuItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Super = MenuItem;
|
||||||
|
|
||||||
|
ButtonMenuItem(std::string text);
|
||||||
|
|
||||||
|
virtual void Draw(const DrawMenuItemArgs& args) const override;
|
||||||
|
virtual void Input(MenuInput in) override;
|
||||||
|
|
||||||
|
void SetClickCallback(std::function<void()> click_cb);
|
||||||
|
|
||||||
|
virtual ~ButtonMenuItem() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string text_;
|
||||||
|
std::function<void()> click_cb_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SelectMenuItem : public ButtonMenuItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Super = ButtonMenuItem;
|
||||||
|
|
||||||
|
SelectMenuItem(std::string text);
|
||||||
|
|
||||||
|
virtual void Draw(const DrawMenuItemArgs& args) const override;
|
||||||
|
virtual void Input(MenuInput in) override;
|
||||||
|
|
||||||
|
void SetSwitchCallback(std::function<void(int)> switch_cb);
|
||||||
|
void SetSelectionText(std::string select_text) { select_text_ = std::move(select_text); }
|
||||||
|
|
||||||
|
virtual ~SelectMenuItem() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string select_text_;
|
||||||
|
std::function<void(int)> switch_cb_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user