Remote menus - client side part
This commit is contained in:
parent
d43ca2a45a
commit
b69fe6d744
@ -84,6 +84,8 @@ set(CLIENT_ONLY_SOURCES
|
||||
"src/gameview/entityview.cpp"
|
||||
"src/gameview/mapinstanceview.hpp"
|
||||
"src/gameview/mapinstanceview.cpp"
|
||||
"src/gameview/remote_menu_view.hpp"
|
||||
"src/gameview/remote_menu_view.cpp"
|
||||
"src/gameview/simple_entity_view.hpp"
|
||||
"src/gameview/simple_entity_view.cpp"
|
||||
"src/gameview/skinning_ubo.hpp"
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "net/outmessage.hpp"
|
||||
#include "assets/cache.hpp"
|
||||
#include "gameview/worldview.hpp"
|
||||
#include "gameview/utils.hpp"
|
||||
|
||||
App::App() :
|
||||
gui_(dlist_, assets::CacheManager::GetFont("data/comic32.font"))
|
||||
@ -64,7 +65,7 @@ void App::Frame()
|
||||
if (menu_)
|
||||
{
|
||||
auto menu_size = menu_->MeasureSize();
|
||||
menu_->Draw(gui_, glm::vec2(viewport_size_) - menu_size - 10.0f);
|
||||
menu_->Draw(gui_, (glm::vec2(viewport_size_) - menu_size) * 0.5f);
|
||||
}
|
||||
|
||||
gui_.Render();
|
||||
@ -114,20 +115,6 @@ void App::Disconnected(const std::string& reason)
|
||||
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)
|
||||
@ -215,7 +202,7 @@ static void AddSlider(gui::Menu& menu, std::string text, int& value, int min, in
|
||||
else if (value > max)
|
||||
value = max;
|
||||
|
||||
slider.SetSelectionText(std::to_string(value));
|
||||
slider.SetSelectionText(std::to_string(value) + " %");
|
||||
changed();
|
||||
};
|
||||
|
||||
@ -226,6 +213,7 @@ static void AddSlider(gui::Menu& menu, std::string text, int& value, int min, in
|
||||
void App::OpenSettings()
|
||||
{
|
||||
menu_ = std::make_unique<gui::Menu>();
|
||||
menu_->SetTitle("nastavení");
|
||||
|
||||
AddSlider(*menu_, "jak moc to řve", volume_, 0, 100, [this]{
|
||||
ApplyVolume();
|
||||
|
||||
@ -86,6 +86,10 @@ void game::Game::PlayerInput(Player& player, PlayerInputType type, bool enabled)
|
||||
break;
|
||||
}
|
||||
|
||||
case IN_DEBUG3:
|
||||
DisplayTestMenu(player);
|
||||
break;
|
||||
|
||||
default: {
|
||||
auto world = FindPlayerWorld(player);
|
||||
if (world)
|
||||
@ -201,3 +205,36 @@ game::EnterableWorld* game::Game::FindPlayerWorld(Player& player) const
|
||||
|
||||
return it->second.world;
|
||||
}
|
||||
|
||||
void game::Game::DisplayTestMenu(Player& player)
|
||||
{
|
||||
if (player.HasOpenMenu())
|
||||
return;
|
||||
|
||||
auto& menu = player.DisplayMenu("test");
|
||||
|
||||
auto& btn_echo = menu.AddItem(RM_BUTTON, "echo");
|
||||
btn_echo.SetOnClick([&player] {
|
||||
player.SendChat("echo test");
|
||||
});
|
||||
|
||||
auto& btn_bc = menu.AddItem(RM_BUTTON, "broadcast");
|
||||
btn_bc.SetOnClick([this, &player] {
|
||||
BroadcastChat(player.GetName() + "^r mele hovna");
|
||||
});
|
||||
|
||||
int test = 0;
|
||||
auto& sel_test = menu.AddItem(RM_SELECT, "výběr");
|
||||
sel_test.SetOnSelect([test, &sel_test] (int dir) mutable {
|
||||
test += dir;
|
||||
sel_test.SetSelection(std::to_string(test));
|
||||
});
|
||||
sel_test.SetSelection(std::to_string(test));
|
||||
|
||||
|
||||
auto& btn_close = menu.AddItem(RM_BUTTON, "zavřít");
|
||||
btn_close.SetOnClick([&menu, &player] {
|
||||
player.CloseMenu(menu);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@ -42,6 +42,8 @@ private:
|
||||
PlayerGameInfo& GetPlayerInfo(Player& player);
|
||||
EnterableWorld* FindPlayerWorld(Player& player) const;
|
||||
|
||||
void DisplayTestMenu(Player& player);
|
||||
|
||||
private:
|
||||
std::shared_ptr<OpenWorld> openworld_;
|
||||
std::shared_ptr<EnterableWorld> testworld_;
|
||||
|
||||
@ -23,6 +23,9 @@ bool game::Player::ProcessMsg(net::MessageType type, net::InMessage& msg)
|
||||
case net::MSG_VIEWANGLES:
|
||||
return ProcessViewAnglesMsg(msg);
|
||||
|
||||
case net::MSG_MENUACTION:
|
||||
return ProcessMenuActionMsg(msg);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -30,20 +33,8 @@ bool game::Player::ProcessMsg(net::MessageType type, net::InMessage& msg)
|
||||
|
||||
void game::Player::Update()
|
||||
{
|
||||
if (world_ != known_world_)
|
||||
{
|
||||
SendWorldMsg();
|
||||
known_world_ = world_;
|
||||
known_ents_.clear();
|
||||
|
||||
return; // send updates next frame
|
||||
}
|
||||
|
||||
if (world_)
|
||||
{
|
||||
SendWorldUpdateMsg();
|
||||
SyncEntities();
|
||||
}
|
||||
SyncWorld();
|
||||
SendMenuMsgs();
|
||||
}
|
||||
|
||||
void game::Player::SetWorld(World* world)
|
||||
@ -77,11 +68,58 @@ void game::Player::SetUseTarget(const std::string& text, const std::string& erro
|
||||
msg.Write<net::UseDelayQ>(delay);
|
||||
}
|
||||
|
||||
game::RemoteMenu& game::Player::DisplayMenu(std::string title)
|
||||
{
|
||||
if (remote_menu_)
|
||||
throw std::runtime_error("cannot display multiple menus atm");
|
||||
|
||||
net::MenuId id = ++menu_id_;
|
||||
remote_menu_ = std::make_unique<RemoteMenu>(id, std::move(title));
|
||||
|
||||
// send msg
|
||||
auto msg = BeginMsg(net::MSG_REMOTEMENU);
|
||||
msg.Write(id);
|
||||
msg.Write(net::MMSG_CREATE);
|
||||
|
||||
return *remote_menu_;
|
||||
}
|
||||
|
||||
void game::Player::CloseMenu(const RemoteMenu& menu)
|
||||
{
|
||||
if (&menu != remote_menu_.get())
|
||||
return;
|
||||
|
||||
// send msg
|
||||
auto msg = BeginMsg(net::MSG_REMOTEMENU);
|
||||
msg.Write(menu.GetId());
|
||||
msg.Write(net::MMSG_CLOSE);
|
||||
|
||||
remote_menu_.reset();
|
||||
}
|
||||
|
||||
game::Player::~Player()
|
||||
{
|
||||
game_.PlayerLeft(*this);
|
||||
}
|
||||
|
||||
void game::Player::SyncWorld()
|
||||
{
|
||||
if (world_ != known_world_)
|
||||
{
|
||||
SendWorldMsg();
|
||||
known_world_ = world_;
|
||||
known_ents_.clear();
|
||||
|
||||
return; // send updates next frame
|
||||
}
|
||||
|
||||
if (world_)
|
||||
{
|
||||
SendWorldUpdateMsg();
|
||||
SyncEntities();
|
||||
}
|
||||
}
|
||||
|
||||
void game::Player::SendWorldMsg()
|
||||
{
|
||||
MSGDEBUG(std::cout << "seding CHWORLD" << std::endl;)
|
||||
@ -255,6 +293,20 @@ bool game::Player::ProcessViewAnglesMsg(net::InMessage& msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::Player::ProcessMenuActionMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MenuId id;
|
||||
net::MenuActionType type;
|
||||
|
||||
if (!msg.Read(id) || !msg.Read(type))
|
||||
return false;
|
||||
|
||||
if (!remote_menu_ || remote_menu_->GetId() != id)
|
||||
return true; // not illegal, might be just a late message
|
||||
|
||||
return remote_menu_->ProcessActionMsg(msg, type);
|
||||
}
|
||||
|
||||
void game::Player::Input(PlayerInputType type, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
@ -265,3 +317,20 @@ void game::Player::Input(PlayerInputType type, bool enabled)
|
||||
game_.PlayerInput(*this, type, enabled);
|
||||
}
|
||||
|
||||
void game::Player::SendMenuMsgs()
|
||||
{
|
||||
if (!remote_menu_)
|
||||
return;
|
||||
|
||||
remote_menu_->Update();
|
||||
auto menu_msg = remote_menu_->GetMsg();
|
||||
|
||||
if (menu_msg.empty())
|
||||
return;
|
||||
|
||||
auto msg = BeginMsg();
|
||||
msg.Write(menu_msg);
|
||||
|
||||
remote_menu_->ResetMsg();
|
||||
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@
|
||||
|
||||
#include "player_input.hpp"
|
||||
|
||||
#include "remote_menu.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
|
||||
@ -34,6 +36,10 @@ public:
|
||||
void SendChat(const std::string& text);
|
||||
void SetUseTarget(const std::string& text, const std::string& error_text, float delay);
|
||||
|
||||
RemoteMenu& DisplayMenu(std::string title);
|
||||
void CloseMenu(const RemoteMenu& menu);
|
||||
bool HasOpenMenu() const { return (bool)remote_menu_; }
|
||||
|
||||
const std::string& GetName() const { return name_; }
|
||||
|
||||
PlayerInputFlags GetInput() const { return in_; }
|
||||
@ -44,6 +50,7 @@ public:
|
||||
|
||||
private:
|
||||
// world sync
|
||||
void SyncWorld();
|
||||
void SendWorldMsg();
|
||||
void SendWorldUpdateMsg();
|
||||
|
||||
@ -56,10 +63,14 @@ private:
|
||||
// msg handlers
|
||||
bool ProcessInputMsg(net::InMessage& msg);
|
||||
bool ProcessViewAnglesMsg(net::InMessage& msg);
|
||||
bool ProcessMenuActionMsg(net::InMessage& msg);
|
||||
|
||||
// events
|
||||
void Input(PlayerInputType type, bool enabled);
|
||||
|
||||
// menu sync
|
||||
void SendMenuMsgs();
|
||||
|
||||
private:
|
||||
Game& game_;
|
||||
std::string name_;
|
||||
@ -73,6 +84,11 @@ private:
|
||||
|
||||
net::EntNum cam_ent_ = 0;
|
||||
glm::vec3 cull_pos_ = glm::vec3(0.0f);
|
||||
|
||||
// menus
|
||||
// TODO: allow more menus
|
||||
net::MenuId menu_id_ = 0;
|
||||
std::unique_ptr<RemoteMenu> remote_menu_;
|
||||
};
|
||||
|
||||
}
|
||||
@ -1,9 +1,8 @@
|
||||
#include "remote_menu.hpp"
|
||||
#include "net/defs.hpp"
|
||||
|
||||
game::RemoteMenuItem::RemoteMenuItem(RemoteMenu& menu, RemoteMenuItemType type, std::string text) : menu_(menu), type_(type)
|
||||
game::RemoteMenuItem::RemoteMenuItem(RemoteMenu& menu, RemoteMenuItemType type, std::string text) : menu_(menu), type_(type), text_(std::move(text))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void game::RemoteMenuItem::SetText(std::string text)
|
||||
@ -26,7 +25,7 @@ void game::RemoteMenuItem::SetSelection(std::string selection)
|
||||
menu_.items_synced_ = false;
|
||||
}
|
||||
|
||||
game::RemoteMenu::RemoteMenu(net::MenuId id) : id_(id)
|
||||
game::RemoteMenu::RemoteMenu(net::MenuId id, std::string title) : id_(id), title_(std::move(title))
|
||||
{
|
||||
}
|
||||
|
||||
@ -41,6 +40,20 @@ game::RemoteMenuItem& game::RemoteMenu::AddItem(RemoteMenuItemType type, std::st
|
||||
return item_ref;
|
||||
}
|
||||
|
||||
bool game::RemoteMenu::ProcessActionMsg(net::InMessage& msg, net::MenuActionType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case net::MA_CLICK:
|
||||
return ProcessClickMsg(msg);
|
||||
case net::MA_SELECT:
|
||||
return ProcessSelectMsg(msg);
|
||||
case net::MA_HOVER:
|
||||
return ProcessHoverMsg(msg);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void game::RemoteMenu::Update()
|
||||
{
|
||||
@ -51,7 +64,7 @@ void game::RemoteMenu::Update()
|
||||
if (!synced_)
|
||||
{
|
||||
auto msg = BeginMenuMsg(net::MMSG_UPDATE);
|
||||
|
||||
msg.Write(net::MenuTitle(title_));
|
||||
msg.Write<net::MenuItemCount>(items_.size());
|
||||
for (auto& item : items_)
|
||||
{
|
||||
@ -103,3 +116,62 @@ net::OutMessage game::RemoteMenu::BeginMenuMsg(net::MenuMessageType type)
|
||||
msg.Write(type);
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool game::RemoteMenu::ProcessClickMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MenuItemId id;
|
||||
|
||||
if (!msg.Read(id))
|
||||
return false;
|
||||
|
||||
if (id > items_.size())
|
||||
return true; // not illegal
|
||||
|
||||
auto& cb = items_[id]->on_click_;
|
||||
if (cb)
|
||||
cb();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::RemoteMenu::ProcessSelectMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MenuItemId id;
|
||||
net::MenuSelectDir dir;
|
||||
|
||||
if (!msg.Read(id) || !msg.Read(dir))
|
||||
return false;
|
||||
|
||||
if (id > items_.size())
|
||||
return true; // not illegal
|
||||
|
||||
int idir = dir ? 1 : -1;
|
||||
|
||||
auto& cb = items_[id]->on_select_;
|
||||
if (cb)
|
||||
cb(idir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::RemoteMenu::ProcessHoverMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MenuItemId id;
|
||||
|
||||
if (!msg.Read(id))
|
||||
return false;
|
||||
|
||||
if (id > items_.size())
|
||||
return true; // not illegal
|
||||
|
||||
if (id == hovered_)
|
||||
return true; // already hovered
|
||||
|
||||
hovered_ = id;
|
||||
|
||||
auto& cb = items_[id]->on_hovered_;
|
||||
if (cb)
|
||||
cb();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "net/defs.hpp"
|
||||
#include "net/msg_producer.hpp"
|
||||
#include "net/outmessage.hpp"
|
||||
#include "net/inmessage.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
@ -54,17 +55,27 @@ private:
|
||||
class RemoteMenu : public net::MsgProducer
|
||||
{
|
||||
public:
|
||||
RemoteMenu(net::MenuId id);
|
||||
RemoteMenu(net::MenuId id, std::string title);
|
||||
|
||||
RemoteMenuItem& AddItem(RemoteMenuItemType type, std::string text);
|
||||
|
||||
net::MenuId GetId() const { return id_; }
|
||||
|
||||
bool ProcessActionMsg(net::InMessage& msg, net::MenuActionType type);
|
||||
|
||||
void Update();
|
||||
|
||||
private:
|
||||
net::OutMessage BeginMenuMsg(net::MenuMessageType type);
|
||||
|
||||
// action handlers
|
||||
bool ProcessClickMsg(net::InMessage& msg);
|
||||
bool ProcessSelectMsg(net::InMessage& msg);
|
||||
bool ProcessHoverMsg(net::InMessage& msg);
|
||||
|
||||
private:
|
||||
net::MenuId id_;
|
||||
std::string title_;
|
||||
bool synced_ = false;
|
||||
bool items_synced_ = false;
|
||||
std::vector<std::unique_ptr<RemoteMenuItem>> items_;
|
||||
|
||||
@ -4,8 +4,9 @@
|
||||
#include <iostream>
|
||||
// #include <glm/gtx/common.hpp>
|
||||
|
||||
#include "vehicleview.hpp"
|
||||
#include "utils/version.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "vehicleview.hpp"
|
||||
|
||||
game::view::ClientSession::ClientSession(App& app) : app_(app), use_target_hud_(app.GetTime())
|
||||
{
|
||||
@ -49,6 +50,9 @@ bool game::view::ClientSession::ProcessSingleMessage(net::MessageType type, net:
|
||||
case net::MSG_USETARGET:
|
||||
return ProcessUseTargetMsg(msg);
|
||||
|
||||
case net::MSG_REMOTEMENU:
|
||||
return ProcessMenuMsg(msg);
|
||||
|
||||
default:
|
||||
// try pass the msg to world
|
||||
if (world_ && world_->ProcessMsg(type, msg))
|
||||
@ -60,13 +64,13 @@ bool game::view::ClientSession::ProcessSingleMessage(net::MessageType type, net:
|
||||
|
||||
void game::view::ClientSession::Input(game::PlayerInputType in, bool pressed, bool repeated)
|
||||
{
|
||||
|
||||
if (pressed && ProcessMenuInput(in))
|
||||
return;
|
||||
|
||||
if (repeated)
|
||||
return;
|
||||
|
||||
SendInput(in, pressed);
|
||||
|
||||
}
|
||||
|
||||
void game::view::ClientSession::ProcessMouseMove(float delta_yaw, float delta_pitch)
|
||||
@ -102,6 +106,8 @@ void game::view::ClientSession::Draw(gfx::DrawList& dlist, gfx::DrawListParams&
|
||||
}
|
||||
|
||||
use_target_hud_.Draw(gui);
|
||||
|
||||
DrawMenus(gui);
|
||||
}
|
||||
|
||||
void game::view::ClientSession::GetViewInfo(glm::vec3& eye, glm::mat4& view) const
|
||||
@ -129,7 +135,7 @@ void game::view::ClientSession::GetViewInfo(glm::vec3& eye, glm::mat4& view) con
|
||||
|
||||
glm::vec3 end = start - dir * distance;
|
||||
|
||||
//start.z -= 0.5f; // shift this a bit to make it better when occluded
|
||||
// start.z -= 0.5f; // shift this a bit to make it better when occluded
|
||||
eye = world_->CameraSweep(start, end);
|
||||
view = glm::lookAt(eye, eye + dir, glm::vec3(0, 0, 1));
|
||||
}
|
||||
@ -183,6 +189,39 @@ bool game::view::ClientSession::ProcessUseTargetMsg(net::InMessage& msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::view::ClientSession::ProcessMenuMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MenuId id;
|
||||
net::MenuMessageType type;
|
||||
|
||||
if (!msg.Read(id) || !msg.Read(type))
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case net::MMSG_CREATE: {
|
||||
if (FindMenu(id))
|
||||
return false;
|
||||
|
||||
remote_menus_.push_back(std::make_unique<RemoteMenuView>(*this, id));
|
||||
return true;
|
||||
}
|
||||
|
||||
case net::MMSG_CLOSE: {
|
||||
std::erase_if(remote_menus_, [id](auto& menu) { return menu->GetId() == id; });
|
||||
return true;
|
||||
}
|
||||
|
||||
default: {
|
||||
auto menu = FindMenu(id);
|
||||
if (!menu)
|
||||
return false;
|
||||
|
||||
return menu->ProcessMessage(type, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui)
|
||||
{
|
||||
// glm::mat4 view = glm::lookAt(glm::vec3(15.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, -13.0f), glm::vec3(0.0f,
|
||||
@ -199,7 +238,8 @@ void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListPar
|
||||
|
||||
// glm::mat4 fake_view_proj = glm::perspective(glm::radians(30.0f), aspect, 0.1f, 3000.0f) * view;
|
||||
|
||||
game::view::DrawArgs draw_args(dlist, params.env, gui, params.view_proj, eye, glm::ivec2(params.screen_width, params.screen_height), 500.0f);
|
||||
game::view::DrawArgs draw_args(dlist, params.env, gui, params.view_proj, eye,
|
||||
glm::ivec2(params.screen_width, params.screen_height), 500.0f);
|
||||
world_->Draw(draw_args);
|
||||
|
||||
glm::mat4 camera_world = glm::inverse(view);
|
||||
@ -236,3 +276,37 @@ void game::view::ClientSession::SendViewAngles(float time)
|
||||
view_pitch_q_.value = pitch_q.value;
|
||||
last_send_time_ = time;
|
||||
}
|
||||
|
||||
void game::view::ClientSession::DrawMenus(gui::Context& gui) const
|
||||
{
|
||||
if (remote_menus_.empty())
|
||||
return;
|
||||
|
||||
auto& top_menu = *remote_menus_.back();
|
||||
top_menu.Draw(gui, glm::vec2(20.0f, 100.0f));
|
||||
|
||||
}
|
||||
|
||||
bool game::view::ClientSession::ProcessMenuInput(game::PlayerInputType in)
|
||||
{
|
||||
if (remote_menus_.empty())
|
||||
return false;
|
||||
|
||||
gui::MenuInput mi;
|
||||
if (!InputToMenuInput(in, mi))
|
||||
return false;
|
||||
|
||||
remote_menus_.back()->Input(mi);
|
||||
return true;
|
||||
}
|
||||
|
||||
game::view::RemoteMenuView* game::view::ClientSession::FindMenu(net::MenuId id) const
|
||||
{
|
||||
for (auto& menu : remote_menus_)
|
||||
{
|
||||
if (menu->GetId() == id)
|
||||
return menu.get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "net/msg_producer.hpp"
|
||||
#include "game/player_input.hpp"
|
||||
#include "gui/use_target_hud.hpp"
|
||||
#include "remote_menu_view.hpp"
|
||||
|
||||
class App;
|
||||
|
||||
@ -41,12 +42,17 @@ private:
|
||||
bool ProcessCameraMsg(net::InMessage& msg);
|
||||
bool ProcessChatMsg(net::InMessage& msg);
|
||||
bool ProcessUseTargetMsg(net::InMessage& msg);
|
||||
bool ProcessMenuMsg(net::InMessage& msg);
|
||||
|
||||
void DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui);
|
||||
|
||||
void SendInput(game::PlayerInputType type, bool enable);
|
||||
void SendViewAngles(float time);
|
||||
|
||||
void DrawMenus(gui::Context& gui) const;
|
||||
bool ProcessMenuInput(game::PlayerInputType in);
|
||||
RemoteMenuView* FindMenu(net::MenuId id) const;
|
||||
|
||||
private:
|
||||
App& app_;
|
||||
|
||||
@ -60,6 +66,8 @@ private:
|
||||
float last_send_time_ = 0.0f;
|
||||
|
||||
gui::UseTargetHud use_target_hud_;
|
||||
|
||||
std::vector<std::unique_ptr<RemoteMenuView>> remote_menus_;
|
||||
};
|
||||
|
||||
} // namespace game::view
|
||||
144
src/gameview/remote_menu_view.cpp
Normal file
144
src/gameview/remote_menu_view.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
#include "remote_menu_view.hpp"
|
||||
#include "game/remote_menu.hpp"
|
||||
#include "client_session.hpp"
|
||||
|
||||
game::view::RemoteMenuView::RemoteMenuView(ClientSession& session, net::MenuId id) : session_(session), id_(id) {}
|
||||
|
||||
bool game::view::RemoteMenuView::ProcessMessage(net::MenuMessageType type, net::InMessage& msg)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case net::MMSG_UPDATE:
|
||||
return ProcessUpdateMsg(msg);
|
||||
case net::MMSG_ITEM_UPDATE_TEXT:
|
||||
return ProcessItemUpdateTextMsg(msg);
|
||||
case net::MMSG_ITEM_UPDATE_SELECTION:
|
||||
return ProcessItemUpdateSelectionMsg(msg);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MenuTitle title;
|
||||
net::MenuItemCount itemcount;
|
||||
|
||||
if (!msg.Read(title) || !msg.Read(itemcount))
|
||||
return false;
|
||||
|
||||
SetTitle(title);
|
||||
Clear();
|
||||
|
||||
for (net::MenuItemId i = 0; i < itemcount; ++i)
|
||||
{
|
||||
game::RemoteMenuItemType type;
|
||||
net::MenuItemText text;
|
||||
net::MenuItemSelection selection;
|
||||
|
||||
if (!msg.Read(type) || !msg.Read(text) || !msg.Read(selection))
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case RM_BUTTON:
|
||||
{
|
||||
auto& btn = Add<gui::ButtonMenuItem>(text);
|
||||
btn.SetClickCallback([this, i] { OnItemClick(i); });
|
||||
break;
|
||||
}
|
||||
|
||||
case RM_SELECT:
|
||||
{
|
||||
auto& select = Add<gui::SelectMenuItem>(text);
|
||||
select.SetSelectionText(selection);
|
||||
select.SetClickCallback([this, i] { OnItemClick(i); });
|
||||
select.SetSwitchCallback([this, i] (int dir) { OnItemSelectionChange(i, dir); });
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::view::RemoteMenuView::ProcessItemUpdateTextMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MenuItemId idx;
|
||||
net::MenuItemText text;
|
||||
|
||||
if (!msg.Read(idx) || !msg.Read(text))
|
||||
return false;
|
||||
|
||||
if (idx >= GetNumItems())
|
||||
return false;
|
||||
|
||||
auto& item = GetItem(idx);
|
||||
|
||||
if (auto btn = dynamic_cast<gui::ButtonMenuItem*>(&item); btn)
|
||||
{
|
||||
btn->SetText(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::view::RemoteMenuView::ProcessItemUpdateSelectionMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MenuItemId idx;
|
||||
net::MenuItemSelection selection;
|
||||
|
||||
if (!msg.Read(idx) || !msg.Read(selection))
|
||||
return false;
|
||||
|
||||
if (idx >= GetNumItems())
|
||||
return false;
|
||||
|
||||
auto& item = GetItem(idx);
|
||||
|
||||
auto select = dynamic_cast<gui::SelectMenuItem*>(&item);
|
||||
if (!select)
|
||||
return false;
|
||||
|
||||
select->SetSelectionText(selection);
|
||||
return true;
|
||||
}
|
||||
|
||||
void game::view::RemoteMenuView::OnItemClick(net::MenuItemId idx)
|
||||
{
|
||||
auto msg = BeginActionMsg(net::MA_CLICK);
|
||||
msg.Write(idx);
|
||||
}
|
||||
|
||||
void game::view::RemoteMenuView::OnItemSelectionChange(net::MenuItemId idx, int dir)
|
||||
{
|
||||
if (dir == 0)
|
||||
return;
|
||||
|
||||
auto msg = BeginActionMsg(net::MA_SELECT);
|
||||
msg.Write(idx);
|
||||
msg.Write<net::MenuSelectDir>(dir < 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
void game::view::RemoteMenuView::OnFocusChanged()
|
||||
{
|
||||
auto msg = BeginActionMsg(net::MA_HOVER);
|
||||
msg.Write<net::MenuItemId>(GetFocusedItemIndex());
|
||||
}
|
||||
|
||||
net::OutMessage game::view::RemoteMenuView::BeginActionMsg(net::MenuActionType type)
|
||||
{
|
||||
auto msg = session_.BeginMsg(net::MSG_MENUACTION);
|
||||
msg.Write(id_);
|
||||
msg.Write(type);
|
||||
return msg;
|
||||
}
|
||||
46
src/gameview/remote_menu_view.hpp
Normal file
46
src/gameview/remote_menu_view.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "gui/menu.hpp"
|
||||
// #include "net/msg_producer.hpp"
|
||||
#include "net/inmessage.hpp"
|
||||
#include "net/outmessage.hpp"
|
||||
#include "net/defs.hpp"
|
||||
|
||||
namespace game::view
|
||||
{
|
||||
|
||||
class ClientSession;
|
||||
|
||||
class RemoteMenuView : public gui::Menu
|
||||
{
|
||||
public:
|
||||
using Super = gui::Menu;
|
||||
|
||||
RemoteMenuView(ClientSession& session, net::MenuId id);
|
||||
|
||||
bool ProcessMessage(net::MenuMessageType type, net::InMessage& msg);
|
||||
|
||||
net::MenuId GetId() const { return id_; }
|
||||
|
||||
private:
|
||||
bool ProcessUpdateMsg(net::InMessage& msg);
|
||||
bool ProcessItemUpdateTextMsg(net::InMessage& msg);
|
||||
bool ProcessItemUpdateSelectionMsg(net::InMessage& msg);
|
||||
|
||||
void OnItemClick(net::MenuItemId idx);
|
||||
void OnItemSelectionChange(net::MenuItemId idx, int dir);
|
||||
|
||||
protected:
|
||||
void OnFocusChanged() override;
|
||||
|
||||
net::OutMessage BeginActionMsg(net::MenuActionType type);
|
||||
|
||||
private:
|
||||
ClientSession& session_;
|
||||
net::MenuId id_;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
19
src/gameview/utils.hpp
Normal file
19
src/gameview/utils.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/player_input.hpp"
|
||||
#include "gui/menu.hpp"
|
||||
|
||||
inline 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_USE: mi = gui::MI_ENTER; return true;
|
||||
case game::IN_CROUCH: mi = gui::MI_BACK; return true;
|
||||
default: return false;
|
||||
}
|
||||
};
|
||||
@ -96,6 +96,7 @@ glm::vec2 gui::Context::MeasureText(std::string_view text)
|
||||
}
|
||||
else if (cp == '\n')
|
||||
{
|
||||
size.x = glm::max(size.x, cursor.x);
|
||||
cursor.x = 0.0f;
|
||||
cursor.y += line_height;
|
||||
continue;
|
||||
@ -122,9 +123,9 @@ glm::vec2 gui::Context::MeasureText(std::string_view text)
|
||||
continue; // Dont even have "missing" glyph, font is shit
|
||||
|
||||
cursor.x += glyph->advance;
|
||||
size.x = glm::max(size.x, cursor.x);
|
||||
}
|
||||
|
||||
size.x = glm::max(size.x, cursor.x);
|
||||
size.y = cursor.y + line_height;
|
||||
|
||||
return size;
|
||||
|
||||
@ -4,17 +4,36 @@
|
||||
|
||||
// Menu
|
||||
|
||||
static constexpr float menu_title_height = 50.0f;
|
||||
|
||||
void gui::Menu::Clear()
|
||||
{
|
||||
items_.clear();
|
||||
focus_ = 0;
|
||||
}
|
||||
|
||||
void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const
|
||||
{
|
||||
// background
|
||||
auto size = MeasureSize();
|
||||
ctx.DrawRect(pos, pos + size, 0x55000000);
|
||||
ctx.DrawRect(pos, pos + size, 0x88000000);
|
||||
|
||||
// draw title
|
||||
glm::vec2 title_size(size.x, menu_title_height);
|
||||
ctx.DrawRect(pos, pos + title_size, 0x55000000);
|
||||
ctx.DrawTextAligned(title_, pos + title_size * 0.5f, glm::vec2(-0.5f));
|
||||
|
||||
// draw items
|
||||
DrawMenuItemArgs args(ctx);
|
||||
args.size = itemsize_;
|
||||
args.pos.x = pos.x;
|
||||
|
||||
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;
|
||||
args.focused = focus_ == i;
|
||||
args.pos.y = pos.y + menu_title_height + static_cast<float>(i) * itemsize_.y;
|
||||
|
||||
items_[i]->Draw(args);
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,18 +56,14 @@ void gui::Menu::Input(MenuInput in)
|
||||
}
|
||||
}
|
||||
|
||||
void gui::Menu::SetTitle(std::string title)
|
||||
{
|
||||
title_ = std::move(title);
|
||||
}
|
||||
|
||||
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;
|
||||
return glm::vec2(itemsize_.x, menu_title_height + itemsize_.y * static_cast<float>(items_.size()));
|
||||
}
|
||||
|
||||
void gui::Menu::SwitchFocus(int dir)
|
||||
@ -56,7 +71,17 @@ void gui::Menu::SwitchFocus(int dir)
|
||||
if (items_.empty())
|
||||
return;
|
||||
|
||||
size_t old_focus = focus_;
|
||||
|
||||
if (items_.empty())
|
||||
focus_ = 0;
|
||||
else
|
||||
focus_ = (focus_ + items_.size() + dir) % items_.size();
|
||||
|
||||
if (focus_ != old_focus)
|
||||
{
|
||||
OnFocusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// ButtonMenuItem
|
||||
@ -64,13 +89,12 @@ void gui::Menu::SwitchFocus(int dir)
|
||||
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);
|
||||
glm::vec2 center = args.pos + glm::vec2(10.0f, args.size.y * 0.5f);
|
||||
args.ctx.DrawTextAligned(text_, center, glm::vec2(0.0f, -0.5f), args.focused ? COLOR_FOCUSED : COLOR_INACTIVE);
|
||||
}
|
||||
|
||||
@ -96,12 +120,30 @@ 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());
|
||||
auto text_size = args.ctx.MeasureText(select_text_);
|
||||
|
||||
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);
|
||||
glm::vec2 cursor = args.pos + glm::vec2(args.size.x - 10.0f, args.size.y * 0.5f);
|
||||
cursor.y -= text_size.y * 0.5f; // centered
|
||||
|
||||
uint32_t text_color = args.focused ? COLOR_FOCUSED : COLOR_INACTIVE;
|
||||
uint32_t arrow_color = 0xFFFFFFFF;
|
||||
|
||||
float arrow_width = 0.0f;
|
||||
if (args.focused)
|
||||
{
|
||||
arrow_width = args.ctx.MeasureText("<").x;
|
||||
cursor.x -= arrow_width;
|
||||
args.ctx.DrawText(">", cursor, arrow_color);
|
||||
}
|
||||
|
||||
cursor.x -= text_size.x;
|
||||
args.ctx.DrawText(select_text_, cursor, text_color);
|
||||
|
||||
if (args.focused)
|
||||
{
|
||||
cursor.x -= arrow_width;
|
||||
args.ctx.DrawText("<", cursor, arrow_color);
|
||||
}
|
||||
}
|
||||
|
||||
void gui::SelectMenuItem::Input(MenuInput in)
|
||||
|
||||
@ -24,9 +24,10 @@ struct DrawMenuItemArgs
|
||||
{
|
||||
Context& ctx;
|
||||
glm::vec2 pos;
|
||||
glm::vec2 size;
|
||||
bool focused;
|
||||
|
||||
DrawMenuItemArgs(Context& ctx, const glm::vec2& pos, bool focused) : ctx(ctx), pos(pos), focused(focused) {}
|
||||
DrawMenuItemArgs(Context& ctx) : ctx(ctx) {}
|
||||
};
|
||||
|
||||
class Menu
|
||||
@ -43,16 +44,31 @@ public:
|
||||
return item_ref;
|
||||
}
|
||||
|
||||
void Clear();
|
||||
|
||||
void Draw(Context& ctx, const glm::vec2& pos) const;
|
||||
void Input(MenuInput in);
|
||||
|
||||
void SetTitle(std::string title);
|
||||
void SetItemSize(const glm::vec2& itemsize) { itemsize_ = itemsize; }
|
||||
|
||||
size_t GetFocusedItemIndex() const { return focus_; }
|
||||
|
||||
size_t GetNumItems() const { return items_.size(); }
|
||||
MenuItem& GetItem(size_t idx) const { return *items_[idx]; }
|
||||
|
||||
glm::vec2 MeasureSize() const;
|
||||
|
||||
protected:
|
||||
virtual void OnFocusChanged() {}
|
||||
|
||||
private:
|
||||
void SwitchFocus(int dir);
|
||||
|
||||
private:
|
||||
std::string title_;
|
||||
std::vector<std::unique_ptr<MenuItem>> items_;
|
||||
glm::vec2 itemsize_ = glm::vec2(300.0f, 40.0f);
|
||||
size_t focus_ = 0;
|
||||
|
||||
};
|
||||
@ -73,7 +89,7 @@ public:
|
||||
virtual ~MenuItem() = default;
|
||||
|
||||
protected:
|
||||
glm::vec2 size_;
|
||||
glm::vec2 size_ = glm::vec2(0.0f);
|
||||
|
||||
};
|
||||
|
||||
@ -89,6 +105,8 @@ public:
|
||||
|
||||
void SetClickCallback(std::function<void()> click_cb);
|
||||
|
||||
void SetText(std::string text) { text_ = std::move(text); }
|
||||
|
||||
virtual ~ButtonMenuItem() = default;
|
||||
|
||||
private:
|
||||
|
||||
@ -23,6 +23,9 @@ enum MessageType : uint8_t
|
||||
// VIEWANGLES <ViewYawQ> <ViewPitchQ>
|
||||
MSG_VIEWANGLES,
|
||||
|
||||
// MENUACTION <MenuId> <MenuActionType> ...
|
||||
MSG_MENUACTION,
|
||||
|
||||
/*~~~~~~~~ Session ~~~~~~~~*/
|
||||
// CHAT <ChatMessage>
|
||||
MSG_CHAT,
|
||||
@ -37,7 +40,7 @@ enum MessageType : uint8_t
|
||||
// USETARGET ...
|
||||
MSG_USETARGET,
|
||||
|
||||
// REMOTEMENU ...
|
||||
// REMOTEMENU <MenuId> <MenuMessageType> ...
|
||||
MSG_REMOTEMENU,
|
||||
|
||||
/*~~~~~~~~ Entity ~~~~~~~~*/
|
||||
@ -159,6 +162,7 @@ enum MenuMessageType
|
||||
};
|
||||
|
||||
using MenuId = uint8_t;
|
||||
using MenuTitle = FixedStr<64>;
|
||||
|
||||
using MenuItemId = uint8_t;
|
||||
using MenuItemCount = MenuItemId;
|
||||
@ -166,5 +170,16 @@ using MenuItemCount = MenuItemId;
|
||||
using MenuItemText = FixedStr<64>;
|
||||
using MenuItemSelection = FixedStr<64>;
|
||||
|
||||
// menu actions
|
||||
|
||||
enum MenuActionType
|
||||
{
|
||||
MA_CLICK,
|
||||
MA_SELECT,
|
||||
MA_HOVER,
|
||||
};
|
||||
|
||||
using MenuSelectDir = uint8_t; // 0=left, 1=right
|
||||
|
||||
|
||||
} // namespace net
|
||||
Loading…
x
Reference in New Issue
Block a user