mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:47:45 +00:00
Breakout: Add player lives game mechanic and pause functionality
This commit is contained in:
parent
afba614d68
commit
0cc970bd07
3 changed files with 73 additions and 5 deletions
|
@ -29,6 +29,7 @@
|
||||||
#include <LibGUI/Application.h>
|
#include <LibGUI/Application.h>
|
||||||
#include <LibGUI/MessageBox.h>
|
#include <LibGUI/MessageBox.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
|
#include <LibGfx/Font.h>
|
||||||
#include <LibGfx/StandardCursor.h>
|
#include <LibGfx/StandardCursor.h>
|
||||||
|
|
||||||
namespace Breakout {
|
namespace Breakout {
|
||||||
|
@ -39,6 +40,7 @@ Game::Game()
|
||||||
auto level_dialog = LevelSelectDialog::show(m_board, window());
|
auto level_dialog = LevelSelectDialog::show(m_board, window());
|
||||||
if (level_dialog != GUI::Dialog::ExecOK)
|
if (level_dialog != GUI::Dialog::ExecOK)
|
||||||
m_board = -1;
|
m_board = -1;
|
||||||
|
set_paused(false);
|
||||||
start_timer(16);
|
start_timer(16);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
@ -56,6 +58,9 @@ void Game::reset_paddle()
|
||||||
|
|
||||||
void Game::reset()
|
void Game::reset()
|
||||||
{
|
{
|
||||||
|
m_lives = 3;
|
||||||
|
m_pause_count = 0;
|
||||||
|
m_cheater = false;
|
||||||
reset_ball();
|
reset_ball();
|
||||||
reset_paddle();
|
reset_paddle();
|
||||||
generate_bricks();
|
generate_bricks();
|
||||||
|
@ -63,6 +68,8 @@ void Game::reset()
|
||||||
|
|
||||||
void Game::generate_bricks()
|
void Game::generate_bricks()
|
||||||
{
|
{
|
||||||
|
m_bricks = {};
|
||||||
|
|
||||||
Gfx::Color colors[] = {
|
Gfx::Color colors[] = {
|
||||||
Gfx::Color::Red,
|
Gfx::Color::Red,
|
||||||
Gfx::Color::Green,
|
Gfx::Color::Green,
|
||||||
|
@ -105,8 +112,24 @@ void Game::generate_bricks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Game::set_paused(bool paused)
|
||||||
|
{
|
||||||
|
m_paused = paused;
|
||||||
|
|
||||||
|
if (m_paused) {
|
||||||
|
set_override_cursor(Gfx::StandardCursor::None);
|
||||||
|
m_pause_count++;
|
||||||
|
} else {
|
||||||
|
set_override_cursor(Gfx::StandardCursor::Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void Game::timer_event(Core::TimerEvent&)
|
void Game::timer_event(Core::TimerEvent&)
|
||||||
{
|
{
|
||||||
|
if (m_paused)
|
||||||
|
return;
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,10 +148,23 @@ void Game::paint_event(GUI::PaintEvent& event)
|
||||||
if (!brick.dead)
|
if (!brick.dead)
|
||||||
painter.fill_rect(enclosing_int_rect(brick.rect), brick.color);
|
painter.fill_rect(enclosing_int_rect(brick.rect), brick.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int msg_width = font().width(String::formatted("Lives: {}", m_lives));
|
||||||
|
int msg_height = font().glyph_height();
|
||||||
|
painter.draw_text({ (game_width - msg_width - 2), 2, msg_width, msg_height }, String::formatted("Lives: {}", m_lives), Gfx::TextAlignment::Center, Color::White);
|
||||||
|
|
||||||
|
if (m_paused) {
|
||||||
|
const char* msg = m_cheater ? "C H E A T E R" : "P A U S E D";
|
||||||
|
int msg_width = font().width(msg);
|
||||||
|
int msg_height = font().glyph_height();
|
||||||
|
painter.draw_text({ (game_width / 2) - (msg_width / 2), (game_height / 2) - (msg_height / 2), msg_width, msg_height }, msg, Gfx::TextAlignment::Center, Color::White);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::keyup_event(GUI::KeyEvent& event)
|
void Game::keyup_event(GUI::KeyEvent& event)
|
||||||
{
|
{
|
||||||
|
if (m_paused)
|
||||||
|
return;
|
||||||
switch (event.key()) {
|
switch (event.key()) {
|
||||||
case Key_Left:
|
case Key_Left:
|
||||||
m_paddle.moving_left = false;
|
m_paddle.moving_left = false;
|
||||||
|
@ -143,6 +179,8 @@ void Game::keyup_event(GUI::KeyEvent& event)
|
||||||
|
|
||||||
void Game::keydown_event(GUI::KeyEvent& event)
|
void Game::keydown_event(GUI::KeyEvent& event)
|
||||||
{
|
{
|
||||||
|
if (m_paused)
|
||||||
|
return;
|
||||||
switch (event.key()) {
|
switch (event.key()) {
|
||||||
case Key_Escape:
|
case Key_Escape:
|
||||||
GUI::Application::the()->quit();
|
GUI::Application::the()->quit();
|
||||||
|
@ -160,6 +198,8 @@ void Game::keydown_event(GUI::KeyEvent& event)
|
||||||
|
|
||||||
void Game::mousemove_event(GUI::MouseEvent& event)
|
void Game::mousemove_event(GUI::MouseEvent& event)
|
||||||
{
|
{
|
||||||
|
if (m_paused)
|
||||||
|
return;
|
||||||
float new_paddle_x = event.x() - m_paddle.rect.width() / 2;
|
float new_paddle_x = event.x() - m_paddle.rect.width() / 2;
|
||||||
new_paddle_x = max(0.0f, new_paddle_x);
|
new_paddle_x = max(0.0f, new_paddle_x);
|
||||||
new_paddle_x = min(game_width - m_paddle.rect.width(), new_paddle_x);
|
new_paddle_x = min(game_width - m_paddle.rect.width(), new_paddle_x);
|
||||||
|
@ -185,7 +225,13 @@ void Game::reset_ball()
|
||||||
void Game::hurt()
|
void Game::hurt()
|
||||||
{
|
{
|
||||||
stop_timer();
|
stop_timer();
|
||||||
GUI::MessageBox::show(window(), "Ouch!", "Breakout", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::OK);
|
m_lives--;
|
||||||
|
if (m_lives <= 0) {
|
||||||
|
update();
|
||||||
|
GUI::MessageBox::show(window(), "You lose!", "Breakout", GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::OK);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
sleep(1);
|
||||||
reset_ball();
|
reset_ball();
|
||||||
reset_paddle();
|
reset_paddle();
|
||||||
start_timer(16);
|
start_timer(16);
|
||||||
|
@ -194,7 +240,12 @@ void Game::hurt()
|
||||||
void Game::win()
|
void Game::win()
|
||||||
{
|
{
|
||||||
stop_timer();
|
stop_timer();
|
||||||
|
update();
|
||||||
|
if (m_cheater) {
|
||||||
|
GUI::MessageBox::show(window(), "You cheated not only the game, but yourself.", "Breakout", GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::OK);
|
||||||
|
} else {
|
||||||
GUI::MessageBox::show(window(), "You win!", "Breakout", GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::OK);
|
GUI::MessageBox::show(window(), "You win!", "Breakout", GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::OK);
|
||||||
|
}
|
||||||
reset();
|
reset();
|
||||||
start_timer(16);
|
start_timer(16);
|
||||||
}
|
}
|
||||||
|
@ -268,6 +319,9 @@ void Game::tick()
|
||||||
|
|
||||||
m_ball = new_ball;
|
m_ball = new_ball;
|
||||||
|
|
||||||
|
if (m_pause_count > 50)
|
||||||
|
m_cheater = true;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
|
|
||||||
virtual ~Game() override;
|
virtual ~Game() override;
|
||||||
|
|
||||||
|
void set_paused(bool paused);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Game();
|
Game();
|
||||||
|
|
||||||
|
@ -94,7 +96,11 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool m_paused;
|
||||||
|
int m_lives;
|
||||||
int m_board;
|
int m_board;
|
||||||
|
long m_pause_count;
|
||||||
|
bool m_cheater;
|
||||||
Ball m_ball;
|
Ball m_ball;
|
||||||
Paddle m_paddle;
|
Paddle m_paddle;
|
||||||
Vector<Brick> m_bricks;
|
Vector<Brick> m_bricks;
|
||||||
|
|
|
@ -59,16 +59,24 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
auto window = GUI::Window::construct();
|
auto window = GUI::Window::construct();
|
||||||
window->resize(Breakout::Game::game_width, Breakout::Game::game_height);
|
window->resize(Breakout::Game::game_width, Breakout::Game::game_height);
|
||||||
|
window->set_resizable(false);
|
||||||
|
window->set_double_buffering_enabled(false);
|
||||||
|
window->set_title("Breakout");
|
||||||
auto app_icon = GUI::Icon::default_icon("app-breakout");
|
auto app_icon = GUI::Icon::default_icon("app-breakout");
|
||||||
window->set_icon(app_icon.bitmap_for_size(16));
|
window->set_icon(app_icon.bitmap_for_size(16));
|
||||||
window->set_title("Breakout");
|
auto& game = window->set_main_widget<Breakout::Game>();
|
||||||
window->set_double_buffering_enabled(false);
|
|
||||||
window->set_main_widget<Breakout::Game>();
|
|
||||||
window->show();
|
window->show();
|
||||||
|
|
||||||
auto menubar = GUI::MenuBar::construct();
|
auto menubar = GUI::MenuBar::construct();
|
||||||
|
|
||||||
auto& app_menu = menubar->add_menu("Breakout");
|
auto& app_menu = menubar->add_menu("Breakout");
|
||||||
|
app_menu.add_action(GUI::Action::create_checkable("Pause", { {}, Key_P }, [&](auto& action) {
|
||||||
|
game.set_paused(action.is_checked());
|
||||||
|
return;
|
||||||
|
}));
|
||||||
|
|
||||||
|
app_menu.add_separator();
|
||||||
|
|
||||||
app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) {
|
app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) {
|
||||||
GUI::Application::the()->quit();
|
GUI::Application::the()->quit();
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue