Better tuning
This commit is contained in:
parent
b6e866c729
commit
20f3c023dd
@ -309,7 +309,23 @@ bool game::Player::ProcessMenuActionMsg(net::InMessage& msg)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!remote_menu_ || remote_menu_->GetId() != id)
|
if (!remote_menu_ || remote_menu_->GetId() != id)
|
||||||
return true; // not illegal, might be just a late message
|
{
|
||||||
|
// not illegal, might be just a late message
|
||||||
|
// need to skip specific amount of bytes :((((
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case net::MA_CLICK:
|
||||||
|
return msg.Skip(sizeof(net::MenuItemId));
|
||||||
|
case net::MA_SELECT:
|
||||||
|
return msg.Skip(sizeof(net::MenuItemId) + sizeof(net::MenuSelectDir));
|
||||||
|
case net::MA_HOVER:
|
||||||
|
return msg.Skip(sizeof(net::MenuItemId));
|
||||||
|
case net::MA_EXIT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return remote_menu_->ProcessActionMsg(msg, type);
|
return remote_menu_->ProcessActionMsg(msg, type);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,11 +50,19 @@ bool game::RemoteMenu::ProcessActionMsg(net::InMessage& msg, net::MenuActionType
|
|||||||
return ProcessSelectMsg(msg);
|
return ProcessSelectMsg(msg);
|
||||||
case net::MA_HOVER:
|
case net::MA_HOVER:
|
||||||
return ProcessHoverMsg(msg);
|
return ProcessHoverMsg(msg);
|
||||||
|
case net::MA_EXIT:
|
||||||
|
return ProcessExitMsg(msg);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::RemoteMenu::SetHoveredIdx(int hovered)
|
||||||
|
{
|
||||||
|
hovered_ = hovered;
|
||||||
|
synced_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
void game::RemoteMenu::Update()
|
void game::RemoteMenu::Update()
|
||||||
{
|
{
|
||||||
if (synced_ && items_synced_)
|
if (synced_ && items_synced_)
|
||||||
@ -66,6 +74,7 @@ void game::RemoteMenu::Update()
|
|||||||
auto msg = BeginMenuMsg(net::MMSG_UPDATE);
|
auto msg = BeginMenuMsg(net::MMSG_UPDATE);
|
||||||
msg.Write(net::MenuTitle(title_));
|
msg.Write(net::MenuTitle(title_));
|
||||||
msg.Write<net::MenuItemCount>(items_.size());
|
msg.Write<net::MenuItemCount>(items_.size());
|
||||||
|
msg.Write<net::MenuItemId>(hovered_);
|
||||||
for (auto& item : items_)
|
for (auto& item : items_)
|
||||||
{
|
{
|
||||||
msg.Write(item->type_);
|
msg.Write(item->type_);
|
||||||
@ -124,7 +133,7 @@ bool game::RemoteMenu::ProcessClickMsg(net::InMessage& msg)
|
|||||||
if (!msg.Read(id))
|
if (!msg.Read(id))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (id > items_.size())
|
if (id >= items_.size())
|
||||||
return true; // not illegal
|
return true; // not illegal
|
||||||
|
|
||||||
auto& cb = items_[id]->on_click_;
|
auto& cb = items_[id]->on_click_;
|
||||||
@ -142,7 +151,7 @@ bool game::RemoteMenu::ProcessSelectMsg(net::InMessage& msg)
|
|||||||
if (!msg.Read(id) || !msg.Read(dir))
|
if (!msg.Read(id) || !msg.Read(dir))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (id > items_.size())
|
if (id >= items_.size())
|
||||||
return true; // not illegal
|
return true; // not illegal
|
||||||
|
|
||||||
int idir = dir ? 1 : -1;
|
int idir = dir ? 1 : -1;
|
||||||
@ -161,7 +170,7 @@ bool game::RemoteMenu::ProcessHoverMsg(net::InMessage& msg)
|
|||||||
if (!msg.Read(id))
|
if (!msg.Read(id))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (id > items_.size())
|
if (id >= items_.size())
|
||||||
return true; // not illegal
|
return true; // not illegal
|
||||||
|
|
||||||
if (id == hovered_)
|
if (id == hovered_)
|
||||||
@ -175,3 +184,10 @@ bool game::RemoteMenu::ProcessHoverMsg(net::InMessage& msg)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool game::RemoteMenu::ProcessExitMsg(net::InMessage& msg)
|
||||||
|
{
|
||||||
|
if (on_exit_)
|
||||||
|
on_exit_();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class RemoteMenuItem;
|
|||||||
using RemoteMenuClickCallback = std::function<void()>;
|
using RemoteMenuClickCallback = std::function<void()>;
|
||||||
using RemoteMenuHoveredCallback = std::function<void()>;
|
using RemoteMenuHoveredCallback = std::function<void()>;
|
||||||
using RemoteMenuSelectCallback = std::function<void(int)>;
|
using RemoteMenuSelectCallback = std::function<void(int)>;
|
||||||
|
using RemoteMenuExitCallback = std::function<void()>;
|
||||||
|
|
||||||
class RemoteMenuItem
|
class RemoteMenuItem
|
||||||
{
|
{
|
||||||
@ -37,6 +38,7 @@ public:
|
|||||||
void SetOnSelect(RemoteMenuSelectCallback on_select) { on_select_ = std::move(on_select); }
|
void SetOnSelect(RemoteMenuSelectCallback on_select) { on_select_ = std::move(on_select); }
|
||||||
void SetOnHovered(RemoteMenuHoveredCallback on_hovered) { on_hovered_ = std::move(on_hovered); }
|
void SetOnHovered(RemoteMenuHoveredCallback on_hovered) { on_hovered_ = std::move(on_hovered); }
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RemoteMenu& menu_;
|
RemoteMenu& menu_;
|
||||||
RemoteMenuItemType type_ = RM_LABEL;
|
RemoteMenuItemType type_ = RM_LABEL;
|
||||||
@ -63,6 +65,10 @@ public:
|
|||||||
|
|
||||||
bool ProcessActionMsg(net::InMessage& msg, net::MenuActionType type);
|
bool ProcessActionMsg(net::InMessage& msg, net::MenuActionType type);
|
||||||
|
|
||||||
|
void SetHoveredIdx(int hovered);
|
||||||
|
|
||||||
|
void SetOnExit(RemoteMenuExitCallback on_exit) { on_exit_ = std::move(on_exit); }
|
||||||
|
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -72,6 +78,7 @@ private:
|
|||||||
bool ProcessClickMsg(net::InMessage& msg);
|
bool ProcessClickMsg(net::InMessage& msg);
|
||||||
bool ProcessSelectMsg(net::InMessage& msg);
|
bool ProcessSelectMsg(net::InMessage& msg);
|
||||||
bool ProcessHoverMsg(net::InMessage& msg);
|
bool ProcessHoverMsg(net::InMessage& msg);
|
||||||
|
bool ProcessExitMsg(net::InMessage& msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
net::MenuId id_;
|
net::MenuId id_;
|
||||||
@ -79,7 +86,9 @@ private:
|
|||||||
bool synced_ = false;
|
bool synced_ = false;
|
||||||
bool items_synced_ = false;
|
bool items_synced_ = false;
|
||||||
std::vector<std::unique_ptr<RemoteMenuItem>> items_;
|
std::vector<std::unique_ptr<RemoteMenuItem>> items_;
|
||||||
int hovered_ = -1;
|
int hovered_ = 0;
|
||||||
|
|
||||||
|
RemoteMenuExitCallback on_exit_;
|
||||||
|
|
||||||
friend class RemoteMenuItem;
|
friend class RemoteMenuItem;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -53,171 +53,150 @@ const std::string& game::TuningWorld::GetOccupantName() const
|
|||||||
void game::TuningWorld::Setup()
|
void game::TuningWorld::Setup()
|
||||||
{
|
{
|
||||||
tuning_ = vehicle_->GetTuning();
|
tuning_ = vehicle_->GetTuning();
|
||||||
// UpdateTuningVals();
|
OpenMainTuningMenu();
|
||||||
DisplayTuningMenu();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// void game::TuningWorld::UpdateTuningVals()
|
void game::TuningWorld::OpenMainTuningMenu()
|
||||||
// {
|
|
||||||
// tun_primary_color_ = ColorU32ToU8Vec3(tuning_.primary_color);
|
|
||||||
|
|
||||||
// tun_wheel_idx_ = tuning_.wheels_idx;
|
|
||||||
// tun_wheel_color_ = ColorU32ToU8Vec3(tuning_.wheel_color);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void game::TuningWorld::UpdateTuning()
|
|
||||||
// {
|
|
||||||
// tuning_.primary_color = ColorU8Vec3ToU32(tun_primary_color_);
|
|
||||||
|
|
||||||
// tuning_.wheels_idx = tun_wheel_idx_;
|
|
||||||
// tuning_.wheel_color = ColorU8Vec3ToU32(tun_wheel_color_);
|
|
||||||
|
|
||||||
// vehicle_->SetTuning(tuning_);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static void AddColorChannelSlider(game::RemoteMenu& menu, uint8_t& ch, std::string name, std::function<void()> on_change)
|
|
||||||
// {
|
|
||||||
// auto& slider = menu.AddItem(game::RM_SELECT, std::move(name));
|
|
||||||
|
|
||||||
// auto on_select = [&slider, &ch, on_change = std::move(on_change)] (int dir) {
|
|
||||||
// int new_val = ch + dir * 5;
|
|
||||||
// if (new_val < 0)
|
|
||||||
// ch = 0;
|
|
||||||
// else if (new_val > 255)
|
|
||||||
// ch = 255;
|
|
||||||
// else
|
|
||||||
// ch = new_val;
|
|
||||||
|
|
||||||
// slider.SetSelection(std::to_string(ch));
|
|
||||||
// on_change();
|
|
||||||
// };
|
|
||||||
|
|
||||||
// on_select(0);
|
|
||||||
// slider.SetOnSelect(on_select);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static void AddColorSliders(game::RemoteMenu& menu, glm::u8vec3& color, std::string name, std::function<void()> on_change)
|
|
||||||
// {
|
|
||||||
// AddColorChannelSlider(menu, color.r, name + " ^f00R", on_change);
|
|
||||||
// AddColorChannelSlider(menu, color.g, name + " ^0f0G", on_change);
|
|
||||||
// AddColorChannelSlider(menu, color.b, name + " ^00fB", on_change);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static void AddWheelTypeSlider(game::RemoteMenu& menu, const assets::VehicleTuningList& tuning_list, size_t& idx, std::function<void()> on_change)
|
|
||||||
// {
|
|
||||||
// auto& slider = menu.AddItem(game::RM_SELECT, "kola");
|
|
||||||
|
|
||||||
// auto on_select = [&slider, &idx, &tuning_list, on_change = std::move(on_change)] (int dir) {
|
|
||||||
// auto& wheels = tuning_list.wheels;
|
|
||||||
|
|
||||||
// if (dir < 0 && idx == 0)
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// if (dir > 0 && (idx + 1) >= wheels.size())
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// idx += dir;
|
|
||||||
|
|
||||||
// slider.SetSelection(tuning_list.wheels[idx].displayname);
|
|
||||||
// on_change();
|
|
||||||
// };
|
|
||||||
|
|
||||||
// on_select(0);
|
|
||||||
// slider.SetOnSelect(on_select);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
void game::TuningWorld::AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group)
|
|
||||||
{
|
{
|
||||||
size_t num_parts = group.parts.size();
|
CloseMenu();
|
||||||
if (num_parts == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
struct SliderState
|
|
||||||
{
|
|
||||||
const VehicleTuningGroup* group;
|
|
||||||
int idx = 0;
|
|
||||||
std::vector<std::string> ids;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string current_part;
|
|
||||||
|
|
||||||
auto current_it = tuning_.parts.find(group.id);
|
|
||||||
if (current_it != tuning_.parts.end())
|
|
||||||
{
|
|
||||||
current_part = current_it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto state = std::make_shared<SliderState>();
|
|
||||||
state->group = &group;
|
|
||||||
|
|
||||||
state->ids.reserve(num_parts);
|
|
||||||
size_t i = 0;
|
|
||||||
for (const auto& part : group.parts)
|
|
||||||
{
|
|
||||||
state->ids.push_back(part.first);
|
|
||||||
|
|
||||||
if (part.first == current_part)
|
|
||||||
state->idx = i;
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& slider = menu.AddItem(game::RM_SELECT, group.displayname);
|
|
||||||
|
|
||||||
auto on_select = [&slider, this, state](int dir) mutable {
|
|
||||||
if (dir < 0 && state->idx == 0)
|
|
||||||
state->idx = state->ids.size() - 1;
|
|
||||||
else if (dir > 0 && (state->idx + 1) >= state->ids.size())
|
|
||||||
state->idx = 0;
|
|
||||||
else
|
|
||||||
state->idx += dir;
|
|
||||||
|
|
||||||
auto& part_id = state->ids[state->idx];
|
|
||||||
tuning_.parts[state->group->id] = part_id;
|
|
||||||
slider.SetSelection(state->group->parts.at(part_id).displayname);
|
|
||||||
|
|
||||||
if (dir != 0)
|
|
||||||
vehicle_->SetTuning(tuning_);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
on_select(0);
|
|
||||||
slider.SetOnSelect(on_select);
|
|
||||||
}
|
|
||||||
|
|
||||||
void game::TuningWorld::DisplayTuningMenu()
|
|
||||||
{
|
|
||||||
// display menu
|
// display menu
|
||||||
auto& menu = player_->DisplayMenu("tuning");
|
auto& menu = player_->DisplayMenu("tuning");
|
||||||
menu_ = &menu;
|
menu_ = &menu;
|
||||||
|
|
||||||
// auto on_change = [this] { UpdateTuning(); };
|
size_t i = 0;
|
||||||
|
|
||||||
// AddColorSliders(menu, tun_primary_color_, "primární", on_change);
|
|
||||||
|
|
||||||
// if (!tuning_list_->wheels.empty())
|
|
||||||
// {
|
|
||||||
// AddWheelTypeSlider(menu, *tuning_list_, tun_wheel_idx_, on_change);
|
|
||||||
// AddColorSliders(menu, tun_wheel_color_, "kola", on_change);
|
|
||||||
// }
|
|
||||||
|
|
||||||
for (const auto& group : tuning_list_->groups)
|
for (const auto& group : tuning_list_->groups)
|
||||||
{
|
{
|
||||||
AddTuningGroupSelect(menu, group);
|
auto& btn = menu.AddItem(game::RM_BUTTON, group.displayname);
|
||||||
|
|
||||||
|
btn.SetOnClick([this, i, &group]() {
|
||||||
|
main_menu_hover_ = i;
|
||||||
|
OpenGroupMenu(group);
|
||||||
|
});
|
||||||
|
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& exit_btn = menu.AddItem(RM_BUTTON, "vylézt");
|
menu.SetOnExit([this]() {
|
||||||
exit_btn.SetOnClick([this] {
|
|
||||||
Reset();
|
Reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
menu.SetHoveredIdx(main_menu_hover_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::TuningWorld::OpenGroupMenu(const VehicleTuningGroup& group)
|
||||||
|
{
|
||||||
|
CloseMenu();
|
||||||
|
|
||||||
|
preview_tuning_ = tuning_;
|
||||||
|
|
||||||
|
auto& menu = player_->DisplayMenu(group.displayname);
|
||||||
|
menu_ = &menu;
|
||||||
|
|
||||||
|
int current_idx = -1;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
std::string state_text;
|
||||||
|
|
||||||
|
for (const auto& [part_id, part] : group.parts)
|
||||||
|
{
|
||||||
|
auto& btn = menu.AddItem(RM_BUTTON, part.displayname);
|
||||||
|
|
||||||
|
btn.SetOnHovered([this, &group, &part_id]() {
|
||||||
|
preview_tuning_.parts[group.id] = part_id;
|
||||||
|
vehicle_->SetTuning(preview_tuning_);
|
||||||
|
});
|
||||||
|
|
||||||
|
btn.SetOnClick([this, &btn, &group_id = group.id, &part_id]() {
|
||||||
|
std::string prev_part_id = GetCurrentPartId(group_id);
|
||||||
|
|
||||||
|
if (prev_part_id == part_id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tuning_.parts[group_id] = part_id; // apply
|
||||||
|
|
||||||
|
std::string state_text;
|
||||||
|
|
||||||
|
if (mounted_part_menu_item_)
|
||||||
|
{
|
||||||
|
GetPartState(group_id, prev_part_id, &state_text);
|
||||||
|
mounted_part_menu_item_->SetSelection(state_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPartState(group_id, part_id, &state_text);
|
||||||
|
btn.SetSelection(state_text);
|
||||||
|
|
||||||
|
mounted_part_menu_item_ = &btn;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (GetPartState(group.id, part_id, &state_text))
|
||||||
|
{
|
||||||
|
current_idx = i;
|
||||||
|
mounted_part_menu_item_ = &btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.SetSelection(state_text);
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_idx >= 0)
|
||||||
|
{
|
||||||
|
menu.SetHoveredIdx(current_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.SetOnExit([this]() {
|
||||||
|
mounted_part_menu_item_ = nullptr;
|
||||||
|
vehicle_->SetTuning(tuning_); // undo preview
|
||||||
|
OpenMainTuningMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string game::TuningWorld::GetCurrentPartId(const std::string& group_id)
|
||||||
|
{
|
||||||
|
auto part_it = tuning_.parts.find(group_id);
|
||||||
|
if (part_it != tuning_.parts.end())
|
||||||
|
{
|
||||||
|
return part_it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game::TuningWorld::GetPartState(const std::string& group_name, const std::string& part_id,
|
||||||
|
std::string* state_text)
|
||||||
|
{
|
||||||
|
bool mounted = part_id == GetCurrentPartId(group_name);
|
||||||
|
|
||||||
|
if (state_text)
|
||||||
|
{
|
||||||
|
*state_text = mounted ? "aktuální" : "0 Kč";
|
||||||
|
}
|
||||||
|
|
||||||
|
return mounted;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void game::TuningWorld::CloseMenu()
|
||||||
|
{
|
||||||
|
if (!menu_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
player_->CloseMenu(*menu_);
|
||||||
|
menu_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::TuningWorld::Reset()
|
void game::TuningWorld::Reset()
|
||||||
{
|
{
|
||||||
if (player_)
|
if (player_)
|
||||||
{
|
{
|
||||||
player_->CloseMenu(*menu_);
|
CloseMenu();
|
||||||
|
|
||||||
|
// make sure to undo preview tuning
|
||||||
|
vehicle_->SetTuning(tuning_);
|
||||||
|
|
||||||
game_.MovePlayerToWorld(*player_, exit_world_, true, exit_pos_, exit_yaw_);
|
game_.MovePlayerToWorld(*player_, exit_world_, true, exit_pos_, exit_yaw_);
|
||||||
|
|
||||||
if (exit_cb_)
|
if (exit_cb_)
|
||||||
@ -228,4 +207,5 @@ void game::TuningWorld::Reset()
|
|||||||
vehicle_ = nullptr;
|
vehicle_ = nullptr;
|
||||||
tuning_list_ = nullptr;
|
tuning_list_ = nullptr;
|
||||||
menu_ = nullptr;
|
menu_ = nullptr;
|
||||||
|
main_menu_hover_ = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "enterable_world.hpp"
|
#include "enterable_world.hpp"
|
||||||
#include "drivable_vehicle.hpp"
|
#include "drivable_vehicle.hpp"
|
||||||
|
#include "remote_menu.hpp"
|
||||||
|
|
||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
@ -29,7 +30,12 @@ private:
|
|||||||
void Setup();
|
void Setup();
|
||||||
|
|
||||||
void AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group);
|
void AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group);
|
||||||
void DisplayTuningMenu();
|
|
||||||
|
void OpenMainTuningMenu();
|
||||||
|
void OpenGroupMenu(const VehicleTuningGroup& group);
|
||||||
|
std::string GetCurrentPartId(const std::string& group_id);
|
||||||
|
bool GetPartState(const std::string& group_id, const std::string& part_id, std::string* state_text); // mounted?
|
||||||
|
void CloseMenu();
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
@ -46,7 +52,12 @@ private:
|
|||||||
|
|
||||||
game::RemoteMenu* menu_ = nullptr;
|
game::RemoteMenu* menu_ = nullptr;
|
||||||
|
|
||||||
|
size_t main_menu_hover_ = 0;
|
||||||
|
|
||||||
VehicleTuning tuning_;
|
VehicleTuning tuning_;
|
||||||
|
VehicleTuning preview_tuning_;
|
||||||
|
|
||||||
|
RemoteMenuItem* mounted_part_menu_item_ = nullptr;
|
||||||
|
|
||||||
std::function<void()> exit_cb_;
|
std::function<void()> exit_cb_;
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,9 @@ bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg)
|
|||||||
{
|
{
|
||||||
net::MenuTitle title;
|
net::MenuTitle title;
|
||||||
net::MenuItemCount itemcount;
|
net::MenuItemCount itemcount;
|
||||||
|
net::MenuItemId hovered_idx;
|
||||||
|
|
||||||
if (!msg.Read(title) || !msg.Read(itemcount))
|
if (!msg.Read(title) || !msg.Read(itemcount) || !msg.Read(hovered_idx))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SetTitle(title);
|
SetTitle(title);
|
||||||
@ -44,6 +45,7 @@ bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg)
|
|||||||
case RM_BUTTON:
|
case RM_BUTTON:
|
||||||
{
|
{
|
||||||
auto& btn = Add<gui::ButtonMenuItem>(text);
|
auto& btn = Add<gui::ButtonMenuItem>(text);
|
||||||
|
btn.SetText2(selection);
|
||||||
btn.SetClickCallback([this, i] { OnItemClick(i); });
|
btn.SetClickCallback([this, i] { OnItemClick(i); });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -63,6 +65,7 @@ bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetFocusedItemIndex(hovered_idx);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -105,11 +108,18 @@ bool game::view::RemoteMenuView::ProcessItemUpdateSelectionMsg(net::InMessage& m
|
|||||||
|
|
||||||
auto& item = GetItem(idx);
|
auto& item = GetItem(idx);
|
||||||
|
|
||||||
auto select = dynamic_cast<gui::SelectMenuItem*>(&item);
|
if (auto select = dynamic_cast<gui::SelectMenuItem*>(&item); select)
|
||||||
if (!select)
|
{
|
||||||
return false;
|
|
||||||
|
|
||||||
select->SetSelectionText(selection);
|
select->SetSelectionText(selection);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto btn = dynamic_cast<gui::ButtonMenuItem*>(&item); btn)
|
||||||
|
{
|
||||||
|
btn->SetText2(selection);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +145,11 @@ void game::view::RemoteMenuView::OnFocusChanged()
|
|||||||
msg.Write<net::MenuItemId>(GetFocusedItemIndex());
|
msg.Write<net::MenuItemId>(GetFocusedItemIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::view::RemoteMenuView::OnExit()
|
||||||
|
{
|
||||||
|
BeginActionMsg(net::MA_EXIT);
|
||||||
|
}
|
||||||
|
|
||||||
net::OutMessage game::view::RemoteMenuView::BeginActionMsg(net::MenuActionType type)
|
net::OutMessage game::view::RemoteMenuView::BeginActionMsg(net::MenuActionType type)
|
||||||
{
|
{
|
||||||
auto msg = session_.BeginMsg(net::MSG_MENUACTION);
|
auto msg = session_.BeginMsg(net::MSG_MENUACTION);
|
||||||
|
|||||||
@ -32,6 +32,7 @@ private:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnFocusChanged() override;
|
void OnFocusChanged() override;
|
||||||
|
void OnExit() override;
|
||||||
|
|
||||||
net::OutMessage BeginActionMsg(net::MenuActionType type);
|
net::OutMessage BeginActionMsg(net::MenuActionType type);
|
||||||
|
|
||||||
|
|||||||
111
src/gui/menu.cpp
111
src/gui/menu.cpp
@ -10,6 +10,7 @@ void gui::Menu::Clear()
|
|||||||
{
|
{
|
||||||
items_.clear();
|
items_.clear();
|
||||||
focus_ = 0;
|
focus_ = 0;
|
||||||
|
UpdateScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const
|
void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const
|
||||||
@ -23,15 +24,37 @@ void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const
|
|||||||
ctx.DrawRect(pos, pos + title_size, 0x55000000);
|
ctx.DrawRect(pos, pos + title_size, 0x55000000);
|
||||||
ctx.DrawTextAligned(title_, pos + title_size * 0.5f, glm::vec2(-0.5f));
|
ctx.DrawTextAligned(title_, pos + title_size * 0.5f, glm::vec2(-0.5f));
|
||||||
|
|
||||||
|
auto res_itemsize = itemsize_;
|
||||||
|
|
||||||
|
// draw scrollbar
|
||||||
|
if (items_.size() > max_items_)
|
||||||
|
{
|
||||||
|
glm::vec2 scrollbar_size(5.0f, size.y - title_size.y);
|
||||||
|
glm::vec2 scrollbar_pos(pos.x + size.x - scrollbar_size.x, pos.y + title_size.y);
|
||||||
|
|
||||||
|
// scrollbar background
|
||||||
|
ctx.DrawRect(scrollbar_pos, scrollbar_pos + scrollbar_size, 0x55000000);
|
||||||
|
|
||||||
|
// scroll box
|
||||||
|
float y0 = static_cast<float>(scroll_) / static_cast<float>(items_.size());
|
||||||
|
float y1 = y0 + static_cast<float>(max_items_) / static_cast<float>(items_.size());
|
||||||
|
glm::vec2 scrollbox_p0(scrollbar_pos.x, scrollbar_pos.y + y0 * scrollbar_size.y);
|
||||||
|
glm::vec2 scrollbox_p1(scrollbar_pos.x + scrollbar_size.x, scrollbar_pos.y + y1 * scrollbar_size.y);
|
||||||
|
ctx.DrawRect(scrollbox_p0, scrollbox_p1, 0x44FFFFFF);
|
||||||
|
|
||||||
|
res_itemsize.x -= scrollbar_size.x;
|
||||||
|
}
|
||||||
|
|
||||||
// draw items
|
// draw items
|
||||||
DrawMenuItemArgs args(ctx);
|
DrawMenuItemArgs args(ctx);
|
||||||
args.size = itemsize_;
|
args.size = res_itemsize;
|
||||||
args.pos.x = pos.x;
|
args.pos.x = pos.x;
|
||||||
|
|
||||||
for (size_t i = 0; i < items_.size(); ++i)
|
size_t end = glm::min(items_.size(), scroll_ + max_items_);
|
||||||
|
for (size_t i = scroll_; i < end; ++i)
|
||||||
{
|
{
|
||||||
args.focused = focus_ == i;
|
args.focused = focus_ == i;
|
||||||
args.pos.y = pos.y + menu_title_height + static_cast<float>(i) * itemsize_.y;
|
args.pos.y = pos.y + menu_title_height + static_cast<float>(i - scroll_) * res_itemsize.y;
|
||||||
|
|
||||||
items_[i]->Draw(args);
|
items_[i]->Draw(args);
|
||||||
}
|
}
|
||||||
@ -49,6 +72,10 @@ void gui::Menu::Input(MenuInput in)
|
|||||||
SwitchFocus(1);
|
SwitchFocus(1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MI_BACK:
|
||||||
|
OnExit();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (!items_.empty())
|
if (!items_.empty())
|
||||||
items_[focus_]->Input(in);
|
items_[focus_]->Input(in);
|
||||||
@ -61,9 +88,18 @@ void gui::Menu::SetTitle(std::string title)
|
|||||||
title_ = std::move(title);
|
title_ = std::move(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gui::Menu::SetFocusedItemIndex(size_t idx)
|
||||||
|
{
|
||||||
|
if (idx >= items_.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
focus_ = idx;
|
||||||
|
UpdateScroll();
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec2 gui::Menu::MeasureSize() const
|
glm::vec2 gui::Menu::MeasureSize() const
|
||||||
{
|
{
|
||||||
return glm::vec2(itemsize_.x, menu_title_height + itemsize_.y * static_cast<float>(items_.size()));
|
return glm::vec2(itemsize_.x, menu_title_height + itemsize_.y * static_cast<float>(glm::min(items_.size(), max_items_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui::Menu::SwitchFocus(int dir)
|
void gui::Menu::SwitchFocus(int dir)
|
||||||
@ -81,9 +117,34 @@ void gui::Menu::SwitchFocus(int dir)
|
|||||||
if (focus_ != old_focus)
|
if (focus_ != old_focus)
|
||||||
{
|
{
|
||||||
OnFocusChanged();
|
OnFocusChanged();
|
||||||
|
UpdateScroll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gui::Menu::UpdateScroll()
|
||||||
|
{
|
||||||
|
const size_t num_items = items_.size();
|
||||||
|
|
||||||
|
if (scroll_ + max_items_ > num_items)
|
||||||
|
{
|
||||||
|
if (num_items > max_items_)
|
||||||
|
scroll_ = num_items - max_items_;
|
||||||
|
else
|
||||||
|
scroll_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focus_ >= scroll_ + max_items_)
|
||||||
|
{
|
||||||
|
scroll_ = focus_ - max_items_ + 1;
|
||||||
|
}
|
||||||
|
else if (focus_ < scroll_)
|
||||||
|
{
|
||||||
|
scroll_ = focus_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// ButtonMenuItem
|
// ButtonMenuItem
|
||||||
|
|
||||||
gui::ButtonMenuItem::ButtonMenuItem(std::string text)
|
gui::ButtonMenuItem::ButtonMenuItem(std::string text)
|
||||||
@ -94,8 +155,36 @@ gui::ButtonMenuItem::ButtonMenuItem(std::string text)
|
|||||||
void gui::ButtonMenuItem::Draw(const DrawMenuItemArgs& args) const
|
void gui::ButtonMenuItem::Draw(const DrawMenuItemArgs& args) const
|
||||||
{
|
{
|
||||||
Super::Draw(args);
|
Super::Draw(args);
|
||||||
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);
|
uint32_t text_color = args.focused ? COLOR_FOCUSED : COLOR_INACTIVE;
|
||||||
|
uint32_t arrow_color = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
const float padding = 30.0f;
|
||||||
|
glm::vec2 text_pos = args.pos + glm::vec2(padding, args.size.y * 0.5f);
|
||||||
|
|
||||||
|
// text 1
|
||||||
|
auto text_size = args.ctx.MeasureText(text_);
|
||||||
|
args.ctx.DrawText(text_, text_pos + text_size * glm::vec2(0.0f, -0.5f), text_color);
|
||||||
|
|
||||||
|
// text 2
|
||||||
|
if (!text2_.empty())
|
||||||
|
{
|
||||||
|
glm::vec2 text2_pos = args.pos + glm::vec2(args.size.x - padding, args.size.y * 0.5f);
|
||||||
|
args.ctx.DrawTextAligned(text2_, text2_pos, glm::vec2(-1.0f, -0.5f), text_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hover arrows
|
||||||
|
if (args.focused)
|
||||||
|
{
|
||||||
|
auto arrow_size = args.ctx.MeasureText("» ");
|
||||||
|
|
||||||
|
// left arrow
|
||||||
|
args.ctx.DrawText("» ", text_pos + arrow_size * glm::vec2(-1.0f, -0.5f), arrow_color);
|
||||||
|
|
||||||
|
// right arrow
|
||||||
|
glm::vec2 right_arrow_pos(text2_.empty() ? (text_pos.x + text_size.x) : (args.pos.x + args.size.x - padding), text_pos.y);
|
||||||
|
args.ctx.DrawText(" «", right_arrow_pos + arrow_size * glm::vec2(0.0f, -0.5f), arrow_color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui::ButtonMenuItem::Input(MenuInput in)
|
void gui::ButtonMenuItem::Input(MenuInput in)
|
||||||
@ -118,16 +207,18 @@ gui::SelectMenuItem::SelectMenuItem(std::string text)
|
|||||||
|
|
||||||
void gui::SelectMenuItem::Draw(const DrawMenuItemArgs& args) const
|
void gui::SelectMenuItem::Draw(const DrawMenuItemArgs& args) const
|
||||||
{
|
{
|
||||||
Super::Draw(args);
|
// Super::Draw(args);
|
||||||
|
uint32_t text_color = args.focused ? COLOR_FOCUSED : COLOR_INACTIVE;
|
||||||
|
uint32_t arrow_color = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
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), text_color);
|
||||||
|
|
||||||
auto text_size = args.ctx.MeasureText(select_text_);
|
auto text_size = args.ctx.MeasureText(select_text_);
|
||||||
|
|
||||||
glm::vec2 cursor = args.pos + glm::vec2(args.size.x - 10.0f, args.size.y * 0.5f);
|
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
|
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;
|
float arrow_width = 0.0f;
|
||||||
if (args.focused)
|
if (args.focused)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -52,6 +52,7 @@ public:
|
|||||||
void SetTitle(std::string title);
|
void SetTitle(std::string title);
|
||||||
void SetItemSize(const glm::vec2& itemsize) { itemsize_ = itemsize; }
|
void SetItemSize(const glm::vec2& itemsize) { itemsize_ = itemsize; }
|
||||||
|
|
||||||
|
void SetFocusedItemIndex(size_t idx);
|
||||||
size_t GetFocusedItemIndex() const { return focus_; }
|
size_t GetFocusedItemIndex() const { return focus_; }
|
||||||
|
|
||||||
size_t GetNumItems() const { return items_.size(); }
|
size_t GetNumItems() const { return items_.size(); }
|
||||||
@ -61,15 +62,20 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void OnFocusChanged() {}
|
virtual void OnFocusChanged() {}
|
||||||
|
virtual void OnExit() {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SwitchFocus(int dir);
|
void SwitchFocus(int dir);
|
||||||
|
|
||||||
|
void UpdateScroll();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string title_;
|
std::string title_;
|
||||||
std::vector<std::unique_ptr<MenuItem>> items_;
|
std::vector<std::unique_ptr<MenuItem>> items_;
|
||||||
glm::vec2 itemsize_ = glm::vec2(300.0f, 40.0f);
|
glm::vec2 itemsize_ = glm::vec2(400.0f, 35.0f);
|
||||||
size_t focus_ = 0;
|
size_t focus_ = 0;
|
||||||
|
size_t max_items_ = 15;
|
||||||
|
size_t scroll_ = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,11 +112,15 @@ public:
|
|||||||
void SetClickCallback(std::function<void()> click_cb);
|
void SetClickCallback(std::function<void()> click_cb);
|
||||||
|
|
||||||
void SetText(std::string text) { text_ = std::move(text); }
|
void SetText(std::string text) { text_ = std::move(text); }
|
||||||
|
void SetText2(std::string text2) { text2_ = std::move(text2); }
|
||||||
|
|
||||||
virtual ~ButtonMenuItem() = default;
|
virtual ~ButtonMenuItem() = default;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
std::string text_;
|
std::string text_;
|
||||||
|
std::string text2_;
|
||||||
|
|
||||||
|
private:
|
||||||
std::function<void()> click_cb_;
|
std::function<void()> click_cb_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -166,6 +166,7 @@ enum MenuMessageType
|
|||||||
MMSG_UPDATE,
|
MMSG_UPDATE,
|
||||||
MMSG_ITEM_UPDATE_TEXT,
|
MMSG_ITEM_UPDATE_TEXT,
|
||||||
MMSG_ITEM_UPDATE_SELECTION,
|
MMSG_ITEM_UPDATE_SELECTION,
|
||||||
|
MMSG_SET_HOVER,
|
||||||
MMSG_CLOSE,
|
MMSG_CLOSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -185,6 +186,7 @@ enum MenuActionType
|
|||||||
MA_CLICK,
|
MA_CLICK,
|
||||||
MA_SELECT,
|
MA_SELECT,
|
||||||
MA_HOVER,
|
MA_HOVER,
|
||||||
|
MA_EXIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
using MenuSelectDir = uint8_t; // 0=left, 1=right
|
using MenuSelectDir = uint8_t; // 0=left, 1=right
|
||||||
|
|||||||
@ -131,6 +131,15 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Skip(size_t count)
|
||||||
|
{
|
||||||
|
if (!CheckAvail(count))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ptr_ += count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SubMessage(InMessage& sub, size_t n)
|
bool SubMessage(InMessage& sub, size_t n)
|
||||||
{
|
{
|
||||||
if (!CheckAvail(n))
|
if (!CheckAvail(n))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user