Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/screenComponents/hackingDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ GuiHackingDialog::GuiHackingDialog(GuiContainer* owner, string id)
{
game->reset();
});
reset_button->setSize(200, 50);
reset_button->setSize(150.0f, 50.0f);
reset_button->setPosition(25, -25, sp::Alignment::BottomLeft);
close_button = new GuiButton(minigame_box, "", tr("button", "Close"), [this]()
{
hide();
});
close_button->setSize(200, 50);
close_button->setSize(150.0f, 50.0f);
close_button->setPosition(-25, -25, sp::Alignment::BottomRight);

progress_bar = new GuiProgressbar(minigame_box, "", 0, 1, 0.0);
Expand Down
154 changes: 130 additions & 24 deletions src/screenComponents/mineSweeper.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "random.h"
#include "mineSweeper.h"
#include "i18n.h"
#include "random.h"
#include "miniGame.h"
#include "hackingDialog.h"
#include "gui/gui2_togglebutton.h"
Expand All @@ -8,22 +9,63 @@
#include "gui/gui2_panel.h"

MineSweeper::MineSweeper(GuiPanel* owner, GuiHackingDialog* parent, int difficulty)
: MiniGame(owner, parent, difficulty) {
: MiniGame(owner, parent, difficulty)
{
field_size = difficulty * 2 + 6;
bomb_count = difficulty * 2 + 6;

// Create attempts remaining label
attempts_label = new GuiLabel(owner, "MINESWEEPER_ATTEMPTS_COUNT", "", 25.0f);
attempts_label
->setAlignment(sp::Alignment::CenterRight)
->setPosition(-185.0f, -25.0f, sp::Alignment::BottomRight)
->setSize(150.0f, 50.0f);

// Create flag mode toggle button for touch/no right click
flag_mode_toggle = new GuiToggleButton(owner, "MINESWEEPER_FLAG_MODE", "",
[this](bool value)
{
flag_mode = value;
}
);
flag_mode_toggle
->setIcon("waypoint.png", sp::Alignment::Center)
->setPosition(185.0f, -25.0f, sp::Alignment::BottomLeft)
->setSize(50.0f, 50.0f);

for(int x=0; x<field_size; x++)
{
for(int y=0; y<field_size; y++)
{
FieldItem* item = new FieldItem(owner, "", "", [this, x, y](bool value) { getFieldItem(x, y)->setValue(!value); onFieldClick(x, y); });
item->setSize(50, 50);
item->setPosition(x * 50 - field_size * 25, 25 + y * 50 - field_size * 25, sp::Alignment::Center);
FieldItem* item = new FieldItem(
owner, "", "",
[this, x, y](bool value)
{
if (flag_mode) onFieldRightClick(x, y);
else onFieldClick(x, y);
},
[this, x, y](bool value)
{
onFieldRightClick(x, y);
}
);

item
->setSize(50, 50)
->setPosition(x * 50 - field_size * 25, 25 + y * 50 - field_size * 25, sp::Alignment::Center);
board.emplace_back(item);
}
}
reset();
}

MineSweeper::~MineSweeper()
{
// Explicitly destroy GUI controls
if (attempts_label) attempts_label->destroy();
if (flag_mode_toggle) flag_mode_toggle->destroy();
}

void MineSweeper::disable()
{
MiniGame::disable();
Expand All @@ -32,11 +74,15 @@ void MineSweeper::disable()
for(int y=0; y < field_size; y++)
{
FieldItem* item = getFieldItem(x, y);
item->setText("");
item->setValue(false);
item->disable();
item
->setValue(false)
->setText("")
->setIcon("")
->disable();
}
}
attempts_label->hide();
flag_mode_toggle->hide();
}

void MineSweeper::reset()
Expand All @@ -47,9 +93,11 @@ void MineSweeper::reset()
for(int y=0; y < field_size; y++)
{
FieldItem* item = getFieldItem(x, y);
item->setText("");
item->setValue(false);
item->enable();
item
->setValue(false)
->setText("")
->setIcon("")
->enable();
item->bomb = false;
}
}
Expand All @@ -67,6 +115,11 @@ void MineSweeper::reset()
}
error_count = 0;
correct_count = 0;
flag_mode = false;
flag_mode_toggle->setValue(false);
attempts_label->show();
flag_mode_toggle->show();
updateAttemptsLabel();
}

float MineSweeper::getProgress()
Expand All @@ -89,18 +142,25 @@ glm::vec2 MineSweeper::getBoardSize()
void MineSweeper::onFieldClick(int x, int y)
{
FieldItem* item = getFieldItem(x, y);
if (item->getValue() || item->getText() == "X" || error_count > 1 || correct_count == (field_size * field_size - bomb_count))

if (item->getValue() || item->getText() == "X" || item->getIcon() == "waypoint.png" || error_count > 1 || correct_count == (field_size * field_size - bomb_count))
{
//Unpressing an already pressed button.
//Unpressing an already pressed button, or flagged tile, or game over.
return;
}

item->setValue(true);

if (item->bomb)
{
item->setText("X");
item->setValue(false);
item
->setValue(false)
->setText("X");
error_count++;
}else{
updateAttemptsLabel();
}
else
{
correct_count++;
int proximity = 0;
if (x > 0 && y > 0 && getFieldItem(x - 1, y - 1)->bomb) proximity++;
Expand All @@ -123,15 +183,15 @@ void MineSweeper::onFieldClick(int x, int y)
{
//if no bombs found in proximity, auto click on all surrounding tiles
if (x > 0 && y > 0) onFieldClick(x - 1, y - 1);
if (x > 0) onFieldClick(x - 1, y);
if (x > 0) onFieldClick(x - 1, y);
if (x > 0 && y < field_size - 1) onFieldClick(x - 1, y + 1);

if (y > 0) onFieldClick(x, y - 1);
if (y < field_size - 1) onFieldClick(x, y + 1);
if (y > 0) onFieldClick(x, y - 1);
if (y < field_size - 1) onFieldClick(x, y + 1);

if (x < field_size - 1 && y > 0) onFieldClick(x + 1, y - 1);
if (x < field_size - 1) onFieldClick(x + 1, y);
if (x < field_size - 1 && y < field_size - 1) onFieldClick(x + 1, y + 1);
if (x < field_size - 1 && y > 0) onFieldClick(x + 1, y - 1);
if (x < field_size - 1) onFieldClick(x + 1, y);
if (x < field_size - 1 && y < field_size - 1) onFieldClick(x + 1, y + 1);
}
}

Expand All @@ -141,12 +201,58 @@ void MineSweeper::onFieldClick(int x, int y)
}
}

void MineSweeper::onFieldRightClick(int x, int y)
{
FieldItem* item = getFieldItem(x, y);

// Don't allow flagging already revealed tiles or revealed bombs
if (item->getValue() || item->getText() == "X") return;

// Don't allow flagging after game is over
if (error_count > 1 || correct_count == (field_size * field_size - bomb_count)) return;

// Toggle flag
if (item->getIcon() == "waypoint.png") item->setIcon("");
else item->setIcon("waypoint.png", sp::Alignment::Center);
}

void MineSweeper::updateAttemptsLabel()
{
int attempts_remaining = MAX_ATTEMPTS - error_count;
string attempts_text = static_cast<string>("{remaining}/{max}").format({{"remaining", attempts_remaining}, {"max", MAX_ATTEMPTS}});
if (difficulty > 1) attempts_text = tr("minesweeper", "Attempts: ") + attempts_text;
else attempts_text = "X: " + attempts_text;
attempts_label->setText(attempts_text);
}

MineSweeper::FieldItem* MineSweeper::getFieldItem(int x, int y)
{
return dynamic_cast<MineSweeper::FieldItem*>(board[x * field_size + y]);
}

MineSweeper::FieldItem::FieldItem(GuiContainer* owner, string id, string text, func_t func)
: GuiToggleButton(owner, id, text, func), bomb(false)
MineSweeper::FieldItem::FieldItem(GuiContainer* owner, string id, string text, func_t left_func, func_t right_func)
: GuiToggleButton(owner, id, text, nullptr), bomb(false), left_click_func(left_func), right_click_func(right_func), last_button(sp::io::Pointer::Button::Unknown)
{
}

bool MineSweeper::FieldItem::onMouseDown(sp::io::Pointer::Button button, glm::vec2 position, sp::io::Pointer::ID id)
{
last_button = button;
return true;
}

void MineSweeper::FieldItem::onMouseUp(glm::vec2 position, sp::io::Pointer::ID id)
{
if (!rect.contains(position)) return;

if (last_button == sp::io::Pointer::Button::Left && left_click_func)
{
func_t f = left_click_func;
f(getValue());
}
else if (last_button == sp::io::Pointer::Button::Right && right_click_func)
{
func_t f = right_click_func;
f(getValue());
}
}
41 changes: 29 additions & 12 deletions src/screenComponents/mineSweeper.h
Original file line number Diff line number Diff line change
@@ -1,37 +1,54 @@
/** An implementation of mineSweeper for use as a hacking minigame.
* Original implementation by https://github.com/daid
*/

#ifndef MINESWEEPER_H
#define MINESWEEPER_H
#pragma once

#include "miniGame.h"
#include "gui/gui2_togglebutton.h"

class GuiLabel;
class GuiToggleButton;

/** An implementation of mineSweeper for use as a hacking minigame.
* Original implementation by https://github.com/daid
*/
class MineSweeper : public MiniGame {
public:
MineSweeper(GuiPanel* owner, GuiHackingDialog* parent, int difficulty);
virtual ~MineSweeper();
virtual void reset() override;
virtual void disable() override;
virtual float getProgress() override;
virtual glm::vec2 getBoardSize() override;
protected:
virtual void gameComplete() override;
private:
static constexpr int MAX_ATTEMPTS = 2;

void onFieldClick(int x, int y);
int error_count;
int correct_count;
void onFieldRightClick(int x, int y);
void updateAttemptsLabel();

int error_count = 0;
int correct_count = 0;
int field_size;
int bomb_count;
bool flag_mode = false;

GuiLabel* attempts_label;
GuiToggleButton* flag_mode_toggle;

class FieldItem : public GuiToggleButton
{
public:
FieldItem(GuiContainer* owner, string id, string text, func_t func);
FieldItem(GuiContainer* owner, string id, string text, func_t left_func, func_t right_func);

bool bomb;
virtual bool onMouseDown(sp::io::Pointer::Button button, glm::vec2 position, sp::io::Pointer::ID id) override;
virtual void onMouseUp(glm::vec2 position, sp::io::Pointer::ID id) override;

bool bomb = false;

private:
func_t left_click_func;
func_t right_click_func;
sp::io::Pointer::Button last_button;
};
FieldItem* getFieldItem(int x, int y);
};

#endif//MINESWEEPER_H
Loading