1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-10-06 09:07:19 +02:00

Further refactor of menu managment

This commit is contained in:
Filip Gawin 2018-12-26 21:48:16 +01:00
parent d5541ac91f
commit c49b4bbd50
8 changed files with 166 additions and 153 deletions

View File

@ -3,6 +3,7 @@
#include <algorithm>
#include <functional>
#include <initializer_list>
#include <memory>
#include <optional>
#include <string>
@ -71,18 +72,14 @@ public:
}
};
Menu(std::vector<MenuEntry> initial, int font = MenuDefaults::kFont,
Menu(std::initializer_list<MenuEntry>&& initial,
const glm::vec2& offset = glm::vec2(), int font = MenuDefaults::kFont,
float size = 30.f)
: activeEntry(-1), font(font), size(size), entries(std::move(initial)) {
}
/**
* @brief creates a menu from the given menu items
* @return optional of menu with the items
*/
static std::optional<Menu> create(std::vector<MenuEntry> items,
int font = MenuDefaults::kFont, float size = 30.f) {
return std::make_optional<Menu>(std::move(items), font, size);
: activeEntry(-1)
, offset(offset)
, font(font)
, size(size)
, entries(initial) {
}
Menu& lambda(const GameString& n, std::function<void()> callback) {
@ -100,8 +97,6 @@ public:
*/
int activeEntry;
glm::vec2 offset{};
void draw(GameRenderer* r) {
glm::vec2 basis(offset);
for (size_t i = 0; i < entries.size(); ++i) {
@ -159,6 +154,7 @@ public:
}
private:
glm::vec2 offset{};
int font;
float size;
std::vector<MenuEntry> entries;

View File

@ -6,8 +6,23 @@ const ViewCamera defaultView{{-250.f, -550.f, 75.f},
glm::angleAxis(glm::radians(5.f),
glm::vec3(0.f, 1.f, 0.f))};
State::State(RWGame *game) : game(game) {
}
void State::draw(GameRenderer *r) {
auto& menu = getCurrentMenu();
if (menu) {
menu->draw(r);
}
}
std::optional<Menu> &State::getCurrentMenu() {
refreshMenu();
return menu;
}
void State::handleEvent(const SDL_Event& e) {
auto m = getCurrentMenu();
auto& m = getCurrentMenu();
if (!m) return;
switch (e.type) {
@ -52,3 +67,18 @@ GameWorld* State::getWorld() const {
GameWindow& State::getWindow() {
return game->getWindow();
}
bool State::hasExited() const {
return hasExited_;
}
void State::refreshMenu() {
if (nextMenu) {
menu = std::move(nextMenu);
nextMenu = std::nullopt;
}
}
void State::done() {
hasExited_ = true;
}

View File

@ -16,39 +16,25 @@ class StateManager;
class State {
public:
std::optional<Menu> menu;
std::optional<Menu> nextMenu;
RWGame* game;
State(RWGame* game) : game(game) {
}
State(RWGame* game);
virtual void enter() = 0;
virtual void exit() = 0;
virtual void tick(float dt) = 0;
virtual void draw(GameRenderer* r) {
if (getCurrentMenu()) {
getCurrentMenu()->draw(r);
}
}
virtual void draw(GameRenderer* r);
virtual ~State() = default;
template<typename T>
void enterMenu(T&& menu) {
void setNextMenu(T&& menu) {
nextMenu = std::forward<T>(menu);
}
Menu* getCurrentMenu() {
if (nextMenu) {
menu = std::move(nextMenu);
nextMenu = std::nullopt;
}
return &*menu;
}
std::optional<Menu>& getCurrentMenu();
virtual void handleEvent(const SDL_Event& e);
@ -63,17 +49,18 @@ public:
GameWorld* getWorld() const;
GameWindow& getWindow();
bool hasExited() const {
return hasexited_;
}
bool hasExited() const;
private:
bool hasexited_ = false;
bool hasExited_ = false;
void refreshMenu();
protected:
void done() {
hasexited_ = true;
}
std::optional<Menu> menu;
std::optional<Menu> nextMenu;
void done();
};
#endif

View File

@ -33,13 +33,13 @@ static void jumpCharacter(RWGame* game, CharacterObject* player,
}
}
std::optional<Menu> DebugState::createDebugMenu() {
Menu DebugState::createDebugMenu() {
CharacterObject* player = nullptr;
if (game->getWorld()->getPlayer()) {
player = game->getWorld()->getPlayer()->getCharacter();
}
auto menu = Menu::create(
Menu menu{
{{"Jump to Debug Camera",
[=] {
jumpCharacter(game, player,
@ -47,12 +47,12 @@ std::optional<Menu> DebugState::createDebugMenu() {
_debugCam.rotation * glm::vec3(3.f, 0.f, 0.f),
false);
}},
{"-Map", [=] { this->enterMenu(createMapMenu()); }},
{"-Vehicles", [=] { this->enterMenu(createVehicleMenu()); }},
{"-AI", [=] { this->enterMenu(createAIMenu()); }},
{"-Weapons", [=] { this->enterMenu(createWeaponMenu()); }},
{"-Weather", [=] { this->enterMenu(createWeatherMenu()); }},
{"-Missions", [=] { this->enterMenu(createMissionsMenu()); }},
{"-Map", [=] { setNextMenu(createMapMenu()); }},
{"-Vehicles", [=] { setNextMenu(createVehicleMenu()); }},
{"-AI", [=] { setNextMenu(createAIMenu()); }},
{"-Weapons", [=] { setNextMenu(createWeaponMenu()); }},
{"-Weather", [=] { setNextMenu(createWeatherMenu()); }},
{"-Missions", [=] { setNextMenu(createMissionsMenu()); }},
{"Set Super Jump", [=] { player->setJumpSpeed(20.f); }},
{"Set Normal Jump",
[=] { player->setJumpSpeed(CharacterObject::DefaultJumpSpeed); }},
@ -60,14 +60,14 @@ std::optional<Menu> DebugState::createDebugMenu() {
{"Full Armour", [=] { player->getCurrentState().armour = 100.f; }},
{"Cull Here",
[=] { game->getRenderer().setCullOverride(true, _debugCam); }}},
kDebugFont, kDebugEntryHeight);
menu->offset = kDebugMenuOffset;
kDebugMenuOffset,
kDebugFont,
kDebugEntryHeight};
// Optional block if the player is in a vehicle
auto cv = player->getCurrentVehicle();
if (cv) {
menu->lambda("Flip vehicle", [=] {
menu.lambda("Flip vehicle", [=] {
cv->setRotation(cv->getRotation() *
glm::quat(glm::vec3(0.f, glm::pi<float>(), 0.f)));
});
@ -76,14 +76,14 @@ std::optional<Menu> DebugState::createDebugMenu() {
return menu;
}
std::optional<Menu> DebugState::createMapMenu() {
Menu DebugState::createMapMenu() {
CharacterObject* player = nullptr;
if (game->getWorld()->getPlayer()) {
player = game->getWorld()->getPlayer()->getCharacter();
}
auto menu = Menu::create(
{{"Back", [=] { enterMenu(createDebugMenu()); }},
Menu menu{
{{"Back", [=] { setNextMenu(createDebugMenu()); }},
{"Jump to Docks",
[=] {
jumpCharacter(game, player, glm::vec3(1390.f, -837.f, 100.f));
@ -108,24 +108,24 @@ std::optional<Menu> DebugState::createMapMenu() {
}},
{"Unsolid garage doors",
[=] {
static constexpr std::array<char const*, 33> garageDoorModels{{
"8ballsuburbandoor", "amcogaragedoor",
"bankjobdoor", "bombdoor",
"crushercrush", "crushertop",
"door2_garage", "door3_garage",
"door4_garage", "door_bombshop",
"door_col_compnd_01", "door_col_compnd_02",
"door_col_compnd_03", "door_col_compnd_04",
"door_col_compnd_05", "door_jmsgrage",
"door_sfehousegrge", "double_garage_dr",
"impex_door", "impexpsubgrgdoor",
"ind_plyrwoor", "ind_slidedoor",
"jamesgrge_kb", "leveldoor2",
"oddjgaragdoor", "plysve_gragedoor",
"SalvGarage", "shedgaragedoor",
"Sub_sprayshopdoor", "towergaragedoor1",
"towergaragedoor2", "towergaragedoor3",
"vheistlocdoor"}};
static constexpr std::array<char const*, 33> garageDoorModels{
{"8ballsuburbandoor", "amcogaragedoor",
"bankjobdoor", "bombdoor",
"crushercrush", "crushertop",
"door2_garage", "door3_garage",
"door4_garage", "door_bombshop",
"door_col_compnd_01", "door_col_compnd_02",
"door_col_compnd_03", "door_col_compnd_04",
"door_col_compnd_05", "door_jmsgrage",
"door_sfehousegrge", "double_garage_dr",
"impex_door", "impexpsubgrgdoor",
"ind_plyrwoor", "ind_slidedoor",
"jamesgrge_kb", "leveldoor2",
"oddjgaragdoor", "plysve_gragedoor",
"SalvGarage", "shedgaragedoor",
"Sub_sprayshopdoor", "towergaragedoor1",
"towergaragedoor2", "towergaragedoor3",
"vheistlocdoor"}};
auto gw = game->getWorld();
for (auto& [id, instancePtr] : gw->instancePool.objects) {
@ -138,15 +138,20 @@ std::optional<Menu> DebugState::createMapMenu() {
}
}
}}},
kDebugFont, kDebugEntryHeight);
kDebugMenuOffset,
kDebugFont,
kDebugEntryHeight};
menu->offset = kDebugMenuOffset;
return menu;
}
std::optional<Menu> DebugState::createVehicleMenu() {
auto menu = Menu::create({{"Back", [=] { enterMenu(createDebugMenu()); }}},
kDebugFont, kDebugEntryHeight);
Menu DebugState::createVehicleMenu() {
Menu menu{
{{"Back", [=] { setNextMenu(createDebugMenu()); }}},
kDebugMenuOffset,
kDebugFont,
kDebugEntryHeight,
};
static constexpr std::array<std::tuple<char const*, unsigned int>, 19>
kVehicleTypes{{{"Landstalker", 90},
@ -170,17 +175,17 @@ std::optional<Menu> DebugState::createVehicleMenu() {
{"Infernus", 101}}};
for (const auto& [name, id] : kVehicleTypes) {
menu->lambda(name, [this, id = id] { spawnVehicle(id); });
menu.lambda(name, [this, id = id] { spawnVehicle(id); });
}
menu->offset = kDebugMenuOffset;
return menu;
}
std::optional<Menu> DebugState::createAIMenu() {
auto menu =
Menu::create({{"Back", [=] { this->enterMenu(createDebugMenu()); }}},
kDebugFont, kDebugEntryHeight);
Menu DebugState::createAIMenu() {
Menu menu{{{"Back", [=] { setNextMenu(createDebugMenu()); }}},
kDebugMenuOffset,
kDebugFont,
kDebugEntryHeight};
static constexpr std::array<std::tuple<char const*, unsigned int>, 6>
kPedTypes{{
@ -193,10 +198,10 @@ std::optional<Menu> DebugState::createAIMenu() {
}};
for (const auto& [name, id] : kPedTypes) {
menu->lambda(name, [this, id = id] { spawnFollower(id); });
menu.lambda(name, [this, id = id] { spawnFollower(id); });
}
menu->lambda("Kill All Peds", [=] {
menu.lambda("Kill All Peds", [=] {
for (auto& [id, pedestrianPtr] :
game->getWorld()->pedestrianPool.objects) {
if (pedestrianPtr->getLifetime() == GameObject::PlayerLifetime) {
@ -208,44 +213,44 @@ std::optional<Menu> DebugState::createAIMenu() {
}
});
menu->offset = kDebugMenuOffset;
return menu;
}
std::optional<Menu> DebugState::createWeaponMenu() {
auto menu =
Menu::create({{"Back", [=] { this->enterMenu(createDebugMenu()); }}},
kDebugFont, kDebugEntryHeight);
Menu DebugState::createWeaponMenu() {
Menu menu{{{"Back", [=] { setNextMenu(createDebugMenu()); }}},
kDebugMenuOffset,
kDebugFont,
kDebugEntryHeight};
for (int i = 1; i < kMaxInventorySlots; ++i) {
auto& name = getWorld()->data->weaponData[i].name;
menu->lambda(name, [=] { giveItem(i); });
menu.lambda(name, [=] { giveItem(i); });
}
menu->offset = kDebugMenuOffset;
return menu;
}
std::optional<Menu> DebugState::createWeatherMenu() {
auto menu =
Menu::create({{"Back", [=] { this->enterMenu(createDebugMenu()); }}},
kDebugFont, kDebugEntryHeight);
Menu DebugState::createWeatherMenu() {
Menu menu{{{"Back", [=] { setNextMenu(createDebugMenu()); }}},
kDebugMenuOffset,
kDebugFont,
kDebugEntryHeight};
static constexpr std::array<char const*, 4> w{{"Sunny", "Cloudy", "Rainy", "Foggy"}};
for (std::size_t i = 0; i < w.size(); ++i) {
menu->lambda(w[i],
menu.lambda(w[i],
[=] { game->getWorld()->state->basic.nextWeather = static_cast<std::uint16_t>(i); });
}
menu->offset = kDebugMenuOffset;
return menu;
}
std::optional<Menu> DebugState::createMissionsMenu() {
auto menu =
Menu::create({{"Back", [=] { this->enterMenu(createDebugMenu()); }}},
kDebugFont, kDebugEntryHeightMissions);
Menu DebugState::createMissionsMenu() {
Menu menu{{{"Back", [=] { setNextMenu(createDebugMenu()); }}},
kDebugMenuOffset,
kDebugFont,
kDebugEntryHeightMissions};
static constexpr std::array<char const*, 80> w{{
"Intro Movie",
@ -331,7 +336,7 @@ std::optional<Menu> DebugState::createMissionsMenu() {
}};
for (std::size_t i = 0; i < w.size(); ++i) {
menu->lambda(w[i], [=] {
menu.lambda(w[i], [=] {
ScriptMachine* vm = game->getScriptVM();
if (vm) {
@ -356,13 +361,12 @@ std::optional<Menu> DebugState::createMissionsMenu() {
});
}
menu->offset = kDebugMenuOffset;
return menu;
}
DebugState::DebugState(RWGame* game, const glm::vec3& vp, const glm::quat& vd)
: State(game), _invertedY(game->getConfig().getInputInvertY()) {
this->enterMenu(createDebugMenu());
this->setNextMenu(createDebugMenu());
_debugCam.position = vp;
_debugCam.rotation = vd;

View File

@ -11,13 +11,13 @@ class DebugState final : public State {
bool _sonicMode = false;
bool _invertedY;
std::optional<Menu> createDebugMenu();
std::optional<Menu> createMapMenu();
std::optional<Menu> createVehicleMenu();
std::optional<Menu> createAIMenu();
std::optional<Menu> createWeaponMenu();
std::optional<Menu> createWeatherMenu();
std::optional<Menu> createMissionsMenu();
Menu createDebugMenu();
Menu createMapMenu();
Menu createVehicleMenu();
Menu createAIMenu();
Menu createWeaponMenu();
Menu createWeatherMenu();
Menu createMissionsMenu();
public:
DebugState(RWGame* game, const glm::vec3& vp = {},

View File

@ -13,7 +13,7 @@ MenuState::MenuState(RWGame* game) : State(game) {
void MenuState::enterMainMenu() {
auto& t = game->getGameData().texts;
auto menu = Menu::create(
Menu menu{
{{t.text(MenuDefaults::kStartGameId),
[=] { StateManager::get().enter<IngameState>(game); }},
{t.text(MenuDefaults::kLoadGameId), [=] { enterLoadMenu(); }},
@ -22,14 +22,14 @@ void MenuState::enterMainMenu() {
{t.text(MenuDefaults::kOptionsId),
[] { RW_UNIMPLEMENTED("Options Menu"); }},
{t.text(MenuDefaults::kQuitGameId),
[] { StateManager::get().clear(); }}});
menu->offset = glm::vec2(200.f, 200.f);
[] { StateManager::get().clear(); }}},
glm::vec2(200.f, 200.f)};
enterMenu(menu);
setNextMenu(std::move(menu));
}
void MenuState::enterLoadMenu() {
auto menu = Menu::create({{"BACK", [=] { enterMainMenu(); }}});
Menu menu{{{"BACK", [=] { enterMainMenu(); }}}, glm::vec2(20.f, 30.f)};
auto saves = SaveGame::getAllSaveGameInfo();
for (SaveGameInfo& save : saves) {
@ -46,14 +46,13 @@ void MenuState::enterLoadMenu() {
StateManager::get().enter<IngameState>(game, false);
game->loadGame(save.savePath);
};
menu->lambda(name, loadsave);
menu.lambda(name, loadsave);
} else {
menu->lambda("CORRUPT", [=] {});
menu.lambda("CORRUPT", [=] {});
}
}
menu->offset = glm::vec2(20.f, 30.f);
enterMenu(menu);
setNextMenu(std::move(menu));
}
void MenuState::enter() {

View File

@ -6,14 +6,13 @@
PauseState::PauseState(RWGame* game) : State(game) {
auto& t = game->getGameData().texts;
auto menu = Menu::create(
{{t.text(MenuDefaults::kResumeGameId), [&] { done(); }},
{t.text(MenuDefaults::kOptionsId),
[] { std::cout << "Options" << std::endl; }},
{t.text(MenuDefaults::kQuitGameId),
[] { StateManager::get().clear(); }}});
menu->offset = glm::vec2(200.f, 200.f);
enterMenu(menu);
Menu menu{{{t.text(MenuDefaults::kResumeGameId), [&] { done(); }},
{t.text(MenuDefaults::kOptionsId),
[] { std::cout << "Options" << std::endl; }},
{t.text(MenuDefaults::kQuitGameId),
[] { StateManager::get().clear(); }}},
glm::vec2(200.f, 200.f)};
setNextMenu(menu);
}
void PauseState::enter() {

View File

@ -4,79 +4,77 @@
BOOST_AUTO_TEST_SUITE(MenuTests)
BOOST_AUTO_TEST_CASE(menu_test_click) {
bool clickered = false;
Menu test({{"Test", [&] { clickered = true; }}});
bool clicked = false;
Menu test({{"Test", [&] { clicked = true; }}});
BOOST_CHECK(!clickered);
BOOST_CHECK(!clicked);
// Click underneath the menu item.
test.click(0.f, -1.f);
BOOST_CHECK(!clickered);
BOOST_CHECK(!clicked);
float h = 30.f;
test.click(0.f, h + 1.f);
BOOST_CHECK(!clickered);
BOOST_CHECK(!clicked);
test.click(0.f, h / 2.f);
BOOST_CHECK(clickered);
BOOST_CHECK(clicked);
}
BOOST_AUTO_TEST_CASE(menu_test_click_offset) {
bool clickered = false;
Menu test({{"Test", [&] { clickered = true; }}});
test.offset = glm::vec2(200.f, 200.f);
BOOST_CHECK(!clickered);
bool clicked = false;
Menu test({{"Test", [&] { clicked = true; }}}, glm::vec2(200.f, 200.f));
BOOST_CHECK(!clicked);
// Click underneath the menu item.
test.click(201.f, -1.f);
BOOST_CHECK(!clickered);
BOOST_CHECK(!clicked);
float h = 30.f;
test.click(201.f, 200.f + h + 1.f);
BOOST_CHECK(!clickered);
BOOST_CHECK(!clicked);
test.click(201.f, 200.f + h / 2.f);
BOOST_CHECK(clickered);
BOOST_CHECK(clicked);
}
BOOST_AUTO_TEST_CASE(menu_test_active_index) {
int clickindex = -1;
Menu test({{"Test1", [&] { clickindex = 0; }},
{"Test2", [&] { clickindex = 1; }}});
int clickIndex = -1;
Menu test({{"Test1", [&] { clickIndex = 0; }},
{"Test2", [&] { clickIndex = 1; }}});
test.activate();
BOOST_CHECK(clickindex == -1);
BOOST_CHECK(clickIndex == -1);
test.move(1);
test.activate();
BOOST_CHECK(clickindex == 0);
BOOST_CHECK(clickIndex == 0);
test.move(1);
test.activate();
BOOST_CHECK(clickindex == 1);
BOOST_CHECK(clickIndex == 1);
test.move(-1);
test.activate();
BOOST_CHECK(clickindex == 0);
BOOST_CHECK(clickIndex == 0);
}
BOOST_AUTO_TEST_CASE(menu_test_hover_index) {
int clickindex = -1;
Menu test({{"Test1", [&] { clickindex = 0; }},
{"Test2", [&] { clickindex = 1; }}});
int clickIndex = -1;
Menu test({{"Test1", [&] { clickIndex = 0; }},
{"Test2", [&] { clickIndex = 1; }}});
test.hover(0.f, 30.f - 0.1f);
BOOST_CHECK(test.activeEntry == 0);