mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 12:23:15 +00:00
Games: Add MasterWord
A simple wordle clone.
This commit is contained in:
parent
d56544aca0
commit
011b6df63a
4
Base/res/apps/MasterWrod.af
Normal file
4
Base/res/apps/MasterWrod.af
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[App]
|
||||||
|
Name=MasterWord
|
||||||
|
Executable=/bin/MasterWord
|
||||||
|
Category=Games
|
BIN
Base/res/icons/16x16/app-masterword.png
Normal file
BIN
Base/res/icons/16x16/app-masterword.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 198 B |
BIN
Base/res/icons/32x32/app-masterword.png
Normal file
BIN
Base/res/icons/32x32/app-masterword.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 582 B |
2091
Base/res/words.txt
Normal file
2091
Base/res/words.txt
Normal file
File diff suppressed because it is too large
Load diff
15
Base/usr/share/man/man6/MasterWord.md
Normal file
15
Base/usr/share/man/man6/MasterWord.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
## Name
|
||||||
|
|
||||||
|
![Icon](/res/icons/16x16/app-masterword.png) MasterWord
|
||||||
|
|
||||||
|
[Open](file:///bin/MasterWord)
|
||||||
|
|
||||||
|
## Synopsis
|
||||||
|
|
||||||
|
```**sh
|
||||||
|
$ MasterWord
|
||||||
|
```
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Try to guess a secret word. As you guess, letters that occur but are in the wrong spot will turn yellow and letters that are in the correct spot will turn green.
|
|
@ -4,6 +4,7 @@ add_subdirectory(Chess)
|
||||||
add_subdirectory(FlappyBug)
|
add_subdirectory(FlappyBug)
|
||||||
add_subdirectory(GameOfLife)
|
add_subdirectory(GameOfLife)
|
||||||
add_subdirectory(Hearts)
|
add_subdirectory(Hearts)
|
||||||
|
add_subdirectory(MasterWord)
|
||||||
add_subdirectory(Minesweeper)
|
add_subdirectory(Minesweeper)
|
||||||
add_subdirectory(Pong)
|
add_subdirectory(Pong)
|
||||||
add_subdirectory(Snake)
|
add_subdirectory(Snake)
|
||||||
|
|
13
Userland/Games/MasterWord/CMakeLists.txt
Normal file
13
Userland/Games/MasterWord/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
serenity_component(
|
||||||
|
MasterWord
|
||||||
|
RECOMMENDED
|
||||||
|
TARGETS MasterWord
|
||||||
|
)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
main.cpp
|
||||||
|
WordGame.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
serenity_app(MasterWord ICON app-masterword)
|
||||||
|
target_link_libraries(MasterWord LibGUI LibConfig LibMain LibDesktop)
|
259
Userland/Games/MasterWord/WordGame.cpp
Normal file
259
Userland/Games/MasterWord/WordGame.cpp
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Joe Petrus <joe@petrus.io>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "WordGame.h"
|
||||||
|
#include <AK/QuickSort.h>
|
||||||
|
#include <AK/Random.h>
|
||||||
|
#include <LibConfig/Client.h>
|
||||||
|
#include <LibCore/File.h>
|
||||||
|
#include <LibGUI/Application.h>
|
||||||
|
#include <LibGUI/MessageBox.h>
|
||||||
|
#include <LibGUI/Painter.h>
|
||||||
|
#include <LibGfx/Font.h>
|
||||||
|
#include <LibGfx/FontDatabase.h>
|
||||||
|
#include <LibGfx/Palette.h>
|
||||||
|
|
||||||
|
// TODO: Add stats
|
||||||
|
|
||||||
|
WordGame::WordGame()
|
||||||
|
{
|
||||||
|
read_words();
|
||||||
|
m_num_letters = Config::read_i32("MasterWord", "", "word_length", 5);
|
||||||
|
m_max_guesses = Config::read_i32("MasterWord", "", "max_guesses", 6);
|
||||||
|
reset();
|
||||||
|
pick_font();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::reset()
|
||||||
|
{
|
||||||
|
m_current_guess = {};
|
||||||
|
m_guesses.clear();
|
||||||
|
auto maybe_word = WordGame::random_word(m_num_letters);
|
||||||
|
if (maybe_word.has_value())
|
||||||
|
m_current_word = maybe_word.value();
|
||||||
|
else {
|
||||||
|
GUI::MessageBox::show(window(), String::formatted("Could not get a random {} letter word. Defaulting to 5.", m_num_letters), "MasterWord");
|
||||||
|
if (m_num_letters != 5) {
|
||||||
|
m_num_letters = 5;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::pick_font()
|
||||||
|
{
|
||||||
|
String best_font_name;
|
||||||
|
auto best_font_size = -1;
|
||||||
|
auto& font_database = Gfx::FontDatabase::the();
|
||||||
|
font_database.for_each_font([&](Gfx::Font const& font) {
|
||||||
|
if (font.family() != "Liza" || font.weight() != 700)
|
||||||
|
return;
|
||||||
|
auto size = font.glyph_height();
|
||||||
|
if (size * 2 <= m_letter_height && size > best_font_size) {
|
||||||
|
best_font_name = font.qualified_name();
|
||||||
|
best_font_size = size;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto font = font_database.get_by_name(best_font_name);
|
||||||
|
set_font(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::resize_event(GUI::ResizeEvent&)
|
||||||
|
{
|
||||||
|
pick_font();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::keydown_event(GUI::KeyEvent& event)
|
||||||
|
{
|
||||||
|
// If we can still add a letter and the key was alpha
|
||||||
|
if (m_current_guess.length() < m_num_letters && is_ascii_alpha(event.code_point())) {
|
||||||
|
m_current_guess = String::formatted("{}{}", m_current_guess, event.text().to_uppercase());
|
||||||
|
}
|
||||||
|
// If backspace pressed and already have some letters entered
|
||||||
|
else if (event.key() == KeyCode::Key_Backspace && m_current_guess.length() > 0) {
|
||||||
|
m_current_guess = m_current_guess.substring(0, m_current_guess.length() - 1);
|
||||||
|
}
|
||||||
|
// If enough letters and return pressed
|
||||||
|
else if (m_current_guess.length() == m_num_letters && event.key() == KeyCode::Key_Return) {
|
||||||
|
add_guess(m_current_guess);
|
||||||
|
auto won = m_current_guess == m_current_word;
|
||||||
|
m_current_guess = {};
|
||||||
|
if (won) {
|
||||||
|
GUI::MessageBox::show(window(), "You win!", "MasterWord");
|
||||||
|
reset();
|
||||||
|
} else if (m_guesses.size() == m_max_guesses) {
|
||||||
|
GUI::MessageBox::show(window(), String::formatted("You lose!\nThe word was {}", m_current_word), "MasterWord");
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::paint_event(GUI::PaintEvent& event)
|
||||||
|
{
|
||||||
|
GUI::Frame::paint_event(event);
|
||||||
|
GUI::Painter painter(*this);
|
||||||
|
painter.add_clip_rect(frame_inner_rect());
|
||||||
|
painter.add_clip_rect(event.rect());
|
||||||
|
painter.fill_rect(event.rect(), m_background_color);
|
||||||
|
|
||||||
|
for (size_t guess_index = 0; guess_index < m_max_guesses; ++guess_index) {
|
||||||
|
for (size_t letter_index = 0; letter_index < m_num_letters; ++letter_index) {
|
||||||
|
auto this_rect = letter_rect(guess_index, letter_index);
|
||||||
|
|
||||||
|
if (guess_index < m_guesses.size()) {
|
||||||
|
|
||||||
|
switch (m_guesses[guess_index].letter_states.at(letter_index)) {
|
||||||
|
case Correct:
|
||||||
|
painter.fill_rect(this_rect, m_right_letter_right_spot_color);
|
||||||
|
break;
|
||||||
|
case WrongSpot:
|
||||||
|
painter.fill_rect(this_rect, m_right_letter_wrong_spot_color);
|
||||||
|
break;
|
||||||
|
case Incorrect:
|
||||||
|
painter.fill_rect(this_rect, m_wrong_letter_color);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.draw_text(this_rect, m_guesses[guess_index].text.substring_view(letter_index, 1), font(), Gfx::TextAlignment::Center, m_text_color);
|
||||||
|
} else if (guess_index == m_guesses.size()) {
|
||||||
|
if (letter_index < m_current_guess.length())
|
||||||
|
painter.draw_text(this_rect, m_current_guess.substring_view(letter_index, 1), font(), Gfx::TextAlignment::Center, m_text_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.draw_rect(this_rect, m_border_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Gfx::IntRect WordGame::letter_rect(size_t guess_number, size_t letter_number) const
|
||||||
|
{
|
||||||
|
auto letter_left = m_outer_margin + letter_number * m_letter_width + m_letter_spacing * letter_number;
|
||||||
|
auto letter_top = m_outer_margin + guess_number * m_letter_height + m_letter_spacing * guess_number;
|
||||||
|
return Gfx::IntRect(int(letter_left), int(letter_top), m_letter_width, m_letter_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::read_words()
|
||||||
|
{
|
||||||
|
m_words.clear();
|
||||||
|
auto response = Core::File::open("/res/words.txt", Core::OpenMode::ReadOnly);
|
||||||
|
if (response.is_error()) {
|
||||||
|
GUI::MessageBox::show(nullptr, "Could not read /res/words.txt.\nPlease ensure this file exists and restart MasterWord.", "MasterWord");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
auto words_file = response.value();
|
||||||
|
|
||||||
|
while (!words_file->eof()) {
|
||||||
|
auto current_word = words_file->read_line();
|
||||||
|
if (!current_word.starts_with('#') and current_word.length() > 0)
|
||||||
|
m_words.ensure(current_word.length()).append(current_word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<String> WordGame::random_word(size_t length)
|
||||||
|
{
|
||||||
|
auto words_for_length = m_words.get(length);
|
||||||
|
if (words_for_length.has_value()) {
|
||||||
|
auto i = get_random_uniform(words_for_length->size());
|
||||||
|
return words_for_length->at(i).to_uppercase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t WordGame::shortest_word()
|
||||||
|
{
|
||||||
|
auto available_lengths = m_words.keys();
|
||||||
|
AK::quick_sort(available_lengths);
|
||||||
|
return available_lengths.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t WordGame::longest_word()
|
||||||
|
{
|
||||||
|
auto available_lengths = m_words.keys();
|
||||||
|
AK::quick_sort(available_lengths);
|
||||||
|
return available_lengths.last();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::set_use_system_theme(bool b)
|
||||||
|
{
|
||||||
|
if (b) {
|
||||||
|
auto theme = palette();
|
||||||
|
m_right_letter_wrong_spot_color = Color::from_rgb(0xb59f3b);
|
||||||
|
m_right_letter_right_spot_color = Color::from_rgb(0x538d4e);
|
||||||
|
m_border_color = Color::Black;
|
||||||
|
m_wrong_letter_color = theme.window();
|
||||||
|
m_background_color = theme.window();
|
||||||
|
m_text_color = theme.accent();
|
||||||
|
} else {
|
||||||
|
m_right_letter_wrong_spot_color = Color::from_rgb(0xb59f3b);
|
||||||
|
m_right_letter_right_spot_color = Color::from_rgb(0x538d4e);
|
||||||
|
m_border_color = Color::from_rgb(0x3a3a3c);
|
||||||
|
m_wrong_letter_color = m_border_color;
|
||||||
|
m_background_color = Color::from_rgb(0x121213);
|
||||||
|
m_text_color = Color::White;
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::set_word_length(size_t length)
|
||||||
|
{
|
||||||
|
m_num_letters = length;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::set_max_guesses(size_t max_guesses)
|
||||||
|
{
|
||||||
|
m_max_guesses = max_guesses;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
Gfx::IntSize WordGame::game_size() const
|
||||||
|
{
|
||||||
|
auto w = 2 * m_outer_margin + m_num_letters * m_letter_width + (m_num_letters - 1) * m_letter_spacing;
|
||||||
|
auto h = 2 * m_outer_margin + m_max_guesses * m_letter_height + (m_max_guesses - 1) * m_letter_spacing;
|
||||||
|
return Gfx::IntSize(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::add_guess(AK::StringView guess)
|
||||||
|
{
|
||||||
|
AK::Vector<LetterState> letter_states;
|
||||||
|
|
||||||
|
auto number_correct_for_letter = [this, &guess](StringView letter) -> size_t {
|
||||||
|
VERIFY(m_current_word.length() == guess.length());
|
||||||
|
auto correct_count = 0;
|
||||||
|
for (size_t i = 0; i < m_current_word.length(); ++i) {
|
||||||
|
if (m_current_word.substring_view(i, 1) == letter && guess.substring_view(i, 1) == letter)
|
||||||
|
++correct_count;
|
||||||
|
}
|
||||||
|
return correct_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t letter_index = 0; letter_index < m_num_letters; ++letter_index) {
|
||||||
|
auto guess_letter = guess.substring_view(letter_index, 1);
|
||||||
|
|
||||||
|
if (m_current_word[letter_index] == guess_letter[0])
|
||||||
|
letter_states.append(Correct);
|
||||||
|
else if (m_current_word.contains(guess_letter)) {
|
||||||
|
auto occurrences_in_word = m_current_word.count(guess_letter);
|
||||||
|
auto occurrences_in_guess_already_counted = guess.substring_view(0, letter_index).count(guess_letter);
|
||||||
|
auto correct = number_correct_for_letter(guess_letter);
|
||||||
|
if (occurrences_in_word > correct && occurrences_in_guess_already_counted < occurrences_in_word)
|
||||||
|
letter_states.append(WrongSpot);
|
||||||
|
else
|
||||||
|
letter_states.append(Incorrect);
|
||||||
|
} else
|
||||||
|
letter_states.append(Incorrect);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_guesses.append({ guess, letter_states });
|
||||||
|
update();
|
||||||
|
}
|
73
Userland/Games/MasterWord/WordGame.h
Normal file
73
Userland/Games/MasterWord/WordGame.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Joe Petrus <joe@petrus.io>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
#include <LibGUI/Frame.h>
|
||||||
|
#include <LibGfx/Rect.h>
|
||||||
|
|
||||||
|
class WordGame : public GUI::Frame {
|
||||||
|
C_OBJECT(WordGame);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~WordGame() override = default;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void set_use_system_theme(bool b);
|
||||||
|
void set_word_length(size_t length);
|
||||||
|
void set_max_guesses(size_t max_guesses);
|
||||||
|
Gfx::IntSize game_size() const;
|
||||||
|
|
||||||
|
Optional<String> random_word(size_t length);
|
||||||
|
size_t shortest_word();
|
||||||
|
size_t longest_word();
|
||||||
|
|
||||||
|
void add_guess(AK::StringView guess);
|
||||||
|
|
||||||
|
private:
|
||||||
|
WordGame();
|
||||||
|
void read_words();
|
||||||
|
|
||||||
|
virtual void paint_event(GUI::PaintEvent&) override;
|
||||||
|
virtual void keydown_event(GUI::KeyEvent&) override;
|
||||||
|
virtual void resize_event(GUI::ResizeEvent&) override;
|
||||||
|
void pick_font();
|
||||||
|
|
||||||
|
Gfx::IntRect letter_rect(size_t guess_number, size_t letter_number) const;
|
||||||
|
|
||||||
|
size_t m_max_guesses { 6 };
|
||||||
|
size_t m_num_letters { 5 };
|
||||||
|
static constexpr int m_letter_width { 40 };
|
||||||
|
static constexpr int m_letter_spacing { 5 };
|
||||||
|
static constexpr int m_outer_margin { 20 };
|
||||||
|
static constexpr int m_letter_height { 60 };
|
||||||
|
|
||||||
|
Color m_right_letter_wrong_spot_color { Color::from_rgb(0xb59f3b) };
|
||||||
|
Color m_right_letter_right_spot_color { Color::from_rgb(0x538d4e) };
|
||||||
|
Color m_border_color { Color::from_rgb(0x3a3a3c) };
|
||||||
|
Color m_wrong_letter_color { m_border_color };
|
||||||
|
Color m_background_color { Color::from_rgb(0x121213) };
|
||||||
|
Color m_text_color { Color::White };
|
||||||
|
|
||||||
|
enum LetterState {
|
||||||
|
Correct,
|
||||||
|
WrongSpot,
|
||||||
|
Incorrect
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Guess {
|
||||||
|
AK::String text;
|
||||||
|
AK::Vector<LetterState> letter_states;
|
||||||
|
};
|
||||||
|
|
||||||
|
AK::Vector<Guess> m_guesses;
|
||||||
|
AK::String m_current_guess;
|
||||||
|
AK::String m_current_word;
|
||||||
|
|
||||||
|
HashMap<size_t, AK::Vector<String>> m_words;
|
||||||
|
};
|
134
Userland/Games/MasterWord/main.cpp
Normal file
134
Userland/Games/MasterWord/main.cpp
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Joe Petrus <joe@petrus.io>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "WordGame.h"
|
||||||
|
#include <AK/URL.h>
|
||||||
|
#include <LibConfig/Client.h>
|
||||||
|
#include <LibCore/System.h>
|
||||||
|
#include <LibDesktop/Launcher.h>
|
||||||
|
#include <LibGUI/Action.h>
|
||||||
|
#include <LibGUI/ActionGroup.h>
|
||||||
|
#include <LibGUI/Application.h>
|
||||||
|
#include <LibGUI/Icon.h>
|
||||||
|
#include <LibGUI/InputBox.h>
|
||||||
|
#include <LibGUI/Menubar.h>
|
||||||
|
#include <LibGUI/MessageBox.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
#include <LibMain/Main.h>
|
||||||
|
|
||||||
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
{
|
||||||
|
TRY(Core::System::pledge("stdio rpath recvfd sendfd unix"));
|
||||||
|
|
||||||
|
auto app = TRY(GUI::Application::try_create(arguments));
|
||||||
|
|
||||||
|
Config::pledge_domain("MasterWord");
|
||||||
|
|
||||||
|
TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_protocol("/usr/share/man/man6/MasterWord.md") }));
|
||||||
|
TRY(Desktop::Launcher::seal_allowlist());
|
||||||
|
|
||||||
|
TRY(Core::System::pledge("stdio rpath recvfd sendfd"));
|
||||||
|
|
||||||
|
TRY(Core::System::unveil("/res", "r"));
|
||||||
|
TRY(Core::System::unveil("/tmp/portal/launch", "rw"));
|
||||||
|
TRY(Core::System::unveil(nullptr, nullptr));
|
||||||
|
|
||||||
|
auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-masterword"));
|
||||||
|
|
||||||
|
auto window = TRY(GUI::Window::try_create());
|
||||||
|
|
||||||
|
window->set_double_buffering_enabled(false);
|
||||||
|
window->set_title("MasterWord");
|
||||||
|
window->set_resizable(false);
|
||||||
|
|
||||||
|
auto game = TRY(window->try_set_main_widget<WordGame>());
|
||||||
|
|
||||||
|
auto use_system_theme = Config::read_bool("MasterWord", "", "use_system_theme", false);
|
||||||
|
game->set_use_system_theme(use_system_theme);
|
||||||
|
|
||||||
|
auto shortest_word = game->shortest_word();
|
||||||
|
auto longest_word = game->longest_word();
|
||||||
|
|
||||||
|
window->resize(game->game_size());
|
||||||
|
|
||||||
|
auto game_menu = TRY(window->try_add_menu("&Game"));
|
||||||
|
|
||||||
|
TRY(game_menu->try_add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, [&](auto&) {
|
||||||
|
game->reset();
|
||||||
|
})));
|
||||||
|
TRY(game_menu->try_add_action(GUI::Action::create("Set &Word Length", [&](auto&) {
|
||||||
|
auto word_length = Config::read_i32("MasterWord", "", "word_length", 5);
|
||||||
|
auto word_length_string = String::number(word_length);
|
||||||
|
if (GUI::InputBox::show(window, word_length_string, "Word length:", "MasterWord") == GUI::InputBox::ExecOK && !word_length_string.is_empty()) {
|
||||||
|
auto maybe_word_length = word_length_string.template to_uint();
|
||||||
|
if (!maybe_word_length.has_value() || maybe_word_length.value() < shortest_word || maybe_word_length.value() > longest_word) {
|
||||||
|
GUI::MessageBox::show(window, String::formatted("Please enter a number between {} and {}.", shortest_word, longest_word), "MasterWord");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
word_length = maybe_word_length.value();
|
||||||
|
Config::write_i32("MasterWord", "", "word_length", word_length);
|
||||||
|
game->set_word_length(word_length);
|
||||||
|
window->resize(game->game_size());
|
||||||
|
}
|
||||||
|
})));
|
||||||
|
TRY(game_menu->try_add_action(GUI::Action::create("Set &Number Of Guesses", [&](auto&) {
|
||||||
|
auto max_guesses = Config::read_i32("MasterWord", "", "max_guesses", 5);
|
||||||
|
auto max_guesses_string = String::number(max_guesses);
|
||||||
|
if (GUI::InputBox::show(window, max_guesses_string, "Maximum number of guesses:", "MasterWord") == GUI::InputBox::ExecOK && !max_guesses_string.is_empty()) {
|
||||||
|
auto maybe_max_guesses = max_guesses_string.template to_uint();
|
||||||
|
if (!maybe_max_guesses.has_value() || maybe_max_guesses.value() < 1 || maybe_max_guesses.value() > 20) {
|
||||||
|
GUI::MessageBox::show(window, "Please enter a number between 1 and 20.", "MasterWord");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_guesses = maybe_max_guesses.value();
|
||||||
|
Config::write_i32("MasterWord", "", "max_guesses", max_guesses);
|
||||||
|
game->set_max_guesses(max_guesses);
|
||||||
|
window->resize(game->game_size());
|
||||||
|
}
|
||||||
|
})));
|
||||||
|
|
||||||
|
TRY(game_menu->try_add_separator());
|
||||||
|
TRY(game_menu->try_add_action(GUI::CommonActions::make_quit_action([](auto&) {
|
||||||
|
GUI::Application::the()->quit();
|
||||||
|
})));
|
||||||
|
|
||||||
|
auto theme_menu = TRY(window->try_add_menu("&Theme"));
|
||||||
|
auto system_theme_action = GUI::Action::create("&System", [&](auto&) {
|
||||||
|
game->set_use_system_theme(true);
|
||||||
|
Config::write_bool("MasterWord", "", "use_system_theme", true);
|
||||||
|
});
|
||||||
|
system_theme_action->set_checkable(true);
|
||||||
|
system_theme_action->set_checked(use_system_theme);
|
||||||
|
TRY(theme_menu->try_add_action(system_theme_action));
|
||||||
|
|
||||||
|
auto wordle_theme_action = GUI::Action::create("&Wordle", [&](auto&) {
|
||||||
|
game->set_use_system_theme(false);
|
||||||
|
Config::write_bool("MasterWord", "", "use_system_theme", false);
|
||||||
|
});
|
||||||
|
wordle_theme_action->set_checkable(true);
|
||||||
|
wordle_theme_action->set_checked(!use_system_theme);
|
||||||
|
TRY(theme_menu->try_add_action(wordle_theme_action));
|
||||||
|
|
||||||
|
GUI::ActionGroup theme_actions;
|
||||||
|
theme_actions.set_exclusive(true);
|
||||||
|
theme_actions.set_unchecking_allowed(false);
|
||||||
|
theme_actions.add_action(system_theme_action);
|
||||||
|
theme_actions.add_action(wordle_theme_action);
|
||||||
|
|
||||||
|
auto help_menu = TRY(window->try_add_menu("&Help"));
|
||||||
|
TRY(help_menu->try_add_action(GUI::CommonActions::make_help_action([](auto&) {
|
||||||
|
Desktop::Launcher::open(URL::create_with_file_protocol("/usr/share/man/man6/MasterWord.md"), "/bin/Help");
|
||||||
|
})));
|
||||||
|
TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("MasterWord", app_icon, window)));
|
||||||
|
|
||||||
|
window->show();
|
||||||
|
|
||||||
|
window->set_icon(app_icon.bitmap_for_size(16));
|
||||||
|
|
||||||
|
return app->exec();
|
||||||
|
}
|
Loading…
Reference in a new issue