Compare commits

..

1 Commits
v1.28 ... main

Author SHA1 Message Date
theredbluecube2@gmail.com
e9814dea3e add controller support 2025-08-07 17:48:03 +03:00
4 changed files with 230 additions and 163 deletions

1
.gitignore vendored
View File

@ -6,4 +6,3 @@ lib
.vscode .vscode
bin bin
build build
Makefile

View File

@ -9,7 +9,7 @@ DBG_CXXFLAGS := $(CXXFLAGS) -g
RLS_CXXFLAGS := $(CXXFLAGS) -O2 RLS_CXXFLAGS := $(CXXFLAGS) -O2
# linker stuff # linker stuff
LDFLAGS := -lncursesw LDFLAGS := -lncursesw -lSDL2
DBG_LDFLAGS := $(LDFLAGS) DBG_LDFLAGS := $(LDFLAGS)
RLS_LDFLAGS := $(LDFLAGS) -s RLS_LDFLAGS := $(LDFLAGS) -s

View File

@ -1,72 +0,0 @@
# SPDX-FileCopyrightText: 2025 thorium1256
#
# SPDX-License-Identifier: GPL-3.0-or-later
# compiler stuff
CXX := c++
CXXFLAGS := -I include -std=c++11 -Wall -Wextra
DBG_CXXFLAGS := $(CXXFLAGS) -g
RLS_CXXFLAGS := $(CXXFLAGS) -O2
# linker stuff
LDFLAGS := -L C:\msys64\mingw64\bin -lncursesw6
DBG_LDFLAGS := $(LDFLAGS)
RLS_STRP_LDFLAGS := $(LDFLAGS) -s
# directories
BUILD_DIR := build
SRC_DIR := src
BIN_DIR := bin
DBG_DIR := $(BIN_DIR)\debug
RLS_DIR := $(BIN_DIR)\release
LIB_DIR := lib
DBG_EXEC := $(DBG_DIR)\debug.exe
RLS_STRIPPED_EXEC := $(RLS_DIR)\tuimine.exe
# sources and objects
LIBS := $(wildcard $(LIB_DIR)/*.dll)
LIBS_DBG := $(patsubst $(LIB_DIR)/%.dll, $(DBG_DIR)\\%.dll, $(LIBS))
LIBS_RLS := $(patsubst $(LIB_DIR)/%.dll, $(RLS_DIR)\\%.dll, $(LIBS))
SRCS := $(wildcard $(SRC_DIR)/*.cpp)
DBG_OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/debug_%.o,$(SRCS))
RLS_OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/release_%.o,$(SRCS))
$(shell if not exist $(BIN_DIR) mkdir $(BIN_DIR))
$(shell if not exist $(DBG_DIR) mkdir $(DBG_DIR))
$(shell if not exist $(RLS_DIR) mkdir $(RLS_DIR))
$(shell if not exist $(BUILD_DIR) mkdir $(BUILD_DIR))
# phony rules
.PHONY = all debug release clean libraries_debug libraries_release both
all: release
debug: $(DBG_EXEC) libraries_debug
release: $(RLS_STRIPPED_EXEC) libraries_release
both: debug release
libraries_debug: $(LIBS_DBG)
libraries_release: $(LIBS_RLS)
# linking
$(DBG_EXEC): $(DBG_OBJS)
$(CXX) -o $@ $^ $(DBG_LDFLAGS)
$(RLS_STRIPPED_EXEC): $(RLS_OBJS)
$(CXX) -o $@ $^ $(RLS_STRP_LDFLAGS)
# compiling
$(BUILD_DIR)/debug_%.o: $(SRC_DIR)/%.cpp
$(CXX) -c -o $@ $< $(DBG_CXXFLAGS)
$(BUILD_DIR)/release_%.o: $(SRC_DIR)/%.cpp
$(CXX) -c -o $@ $< $(RLS_CXXFLAGS)
$(DBG_DIR)\\%.dll: $(LIB_DIR)\\%.dll
xcopy $< $(DBG_DIR)\ >nul
$(RLS_DIR)\\%.dll: $(LIB_DIR)\\%.dll
xcopy $< $(RLS_DIR)\ >nul
clean:
rmdir /s /q $(BUILD_DIR) $(DBG_DIR) $(RLS_DIR)

View File

@ -4,9 +4,10 @@
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <ncursesw/ncurses.h> #include <ncurses.h>
#include <ctime> #include <ctime>
#include <unistd.h> #include <unistd.h>
#include <SDL2/SDL.h>
#include "Board.h" #include "Board.h"
#ifndef _WIN32 #ifndef _WIN32
#include <signal.h> #include <signal.h>
@ -20,6 +21,8 @@ void handleSIGWINCH(int sig)
#define MAX_TIME 60 #define MAX_TIME 60
SDL_GameController *controller;
void startGame(Board &board) void startGame(Board &board)
{ {
int minesLeft = board.getMineCount(); int minesLeft = board.getMineCount();
@ -161,99 +164,207 @@ void startGame(Board &board)
c = getch(); c = getch();
if (c == ERR) if (c == ERR)
continue;
switch (c)
{ {
case KEY_UP: if (controller != nullptr)
cursorY = (cursorY > 0) ? cursorY - 1 : boardSize.y - 1;
break;
case KEY_DOWN:
cursorY = (cursorY < boardSize.y - 1) ? cursorY + 1 : 0;
break;
case KEY_LEFT:
cursorX = (cursorX > 0) ? cursorX - 1 : boardSize.x - 1;
break;
case KEY_RIGHT:
cursorX = (cursorX < boardSize.x - 1) ? cursorX + 1 : 0;
break;
case 'q':
echo();
cbreak();
endwin();
exit(0);
break;
case 'z':
if (!somethingHasBeenDone)
{ {
startTime = time(nullptr); SDL_Event event;
somethingHasBeenDone = true; while (SDL_PollEvent(&event))
{
if (event.type == SDL_CONTROLLERBUTTONDOWN)
{
switch (event.cbutton.button)
{
case SDL_CONTROLLER_BUTTON_DPAD_UP:
cursorY = (cursorY > 0) ? cursorY - 1 : boardSize.y - 1;
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
cursorY = (cursorY < boardSize.y - 1) ? cursorY + 1 : 0;
break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
cursorX = (cursorX > 0) ? cursorX - 1 : boardSize.x - 1;
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
cursorX = (cursorX < boardSize.x - 1) ? cursorX + 1 : 0;
break;
case SDL_CONTROLLER_BUTTON_START:
if (isPaused)
{
pauseTime = difftime(time(NULL), startPauseTime);
totalpausetime += pauseTime;
}
else
{
startPauseTime = time(NULL);
}
isPaused = !isPaused;
break;
case SDL_CONTROLLER_BUTTON_A:
if (!somethingHasBeenDone)
{
startTime = time(nullptr);
somethingHasBeenDone = true;
}
if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Revealed)
{
if (!somethingHasBeenDone)
{
startTime = time(nullptr);
somethingHasBeenDone = true;
}
auto neighbors = board.getNeighborsOf(cursorX, cursorY);
int flagCount = 0;
for (const Cell &neighbor : neighbors)
{
if (neighbor.getState() == Cell::State::Flagged)
flagCount++;
}
if (static_cast<int>(board.revealCellAt(cursorX, cursorY).getContent()) > flagCount)
break;
for (const Cell &neighbor : neighbors)
{
if (neighbor.getState() == Cell::State::Flagged)
continue;
board.revealCellAt(neighbor.getPosition().x, neighbor.getPosition().y);
}
break;
}
board.revealCellAt(cursorX, cursorY);
break;
case SDL_CONTROLLER_BUTTON_B:
if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Revealed)
break;
if (!somethingHasBeenDone)
{
startTime = time(nullptr);
somethingHasBeenDone = true;
}
if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Flagged)
{
minesLeft++;
}
else
{
minesLeft--;
}
board.flagCellAt(cursorX, cursorY);
break;
case SDL_CONTROLLER_BUTTON_MISC1:
startTime = time(NULL);
minesLeft = board.getMineCount();
board.regenerateBoard();
somethingHasBeenDone = false;
elapsedTime = 0;
break;
case SDL_CONTROLLER_BUTTON_BACK:
echo();
cbreak();
endwin();
exit(0);
break;
}
}
else
{
break;
}
}
} }
board.revealCellAt(cursorX, cursorY); }
break;
case 'x': if (!(c == ERR))
if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Revealed) {
switch (c)
{
case KEY_UP:
cursorY = (cursorY > 0) ? cursorY - 1 : boardSize.y - 1;
break; break;
if (!somethingHasBeenDone) case KEY_DOWN:
{ cursorY = (cursorY < boardSize.y - 1) ? cursorY + 1 : 0;
startTime = time(nullptr); break;
somethingHasBeenDone = true; case KEY_LEFT:
} cursorX = (cursorX > 0) ? cursorX - 1 : boardSize.x - 1;
if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Flagged) break;
{ case KEY_RIGHT:
minesLeft++; cursorX = (cursorX < boardSize.x - 1) ? cursorX + 1 : 0;
} break;
else case 'q':
{ echo();
minesLeft--; cbreak();
} endwin();
board.flagCellAt(cursorX, cursorY); exit(0);
break; break;
case 'r': case 'z':
startTime = time(NULL); if (!somethingHasBeenDone)
minesLeft = board.getMineCount(); {
board.regenerateBoard(); startTime = time(nullptr);
somethingHasBeenDone = false; somethingHasBeenDone = true;
elapsedTime = 0; }
break; board.revealCellAt(cursorX, cursorY);
case 'p': break;
if (isPaused) case 'x':
{ if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Revealed)
pauseTime = difftime(time(NULL), startPauseTime); break;
totalpausetime += pauseTime; if (!somethingHasBeenDone)
} {
else startTime = time(nullptr);
{ somethingHasBeenDone = true;
startPauseTime = time(NULL); }
} if (board.getCellStateAt(cursorX, cursorY) == Cell::State::Flagged)
isPaused = !isPaused; {
break; minesLeft++;
case 'c': }
if (board.getCellStateAt(cursorX, cursorY) != Cell::State::Revealed) else
{ {
minesLeft--;
}
board.flagCellAt(cursorX, cursorY);
break;
case 'r':
startTime = time(NULL);
minesLeft = board.getMineCount();
board.regenerateBoard();
somethingHasBeenDone = false;
elapsedTime = 0;
break;
case 'p':
if (isPaused)
{
pauseTime = difftime(time(NULL), startPauseTime);
totalpausetime += pauseTime;
}
else
{
startPauseTime = time(NULL);
}
isPaused = !isPaused;
break;
case 'c':
if (board.getCellStateAt(cursorX, cursorY) != Cell::State::Revealed)
{
break;
}
if (!somethingHasBeenDone)
{
startTime = time(nullptr);
somethingHasBeenDone = true;
}
auto neighbors = board.getNeighborsOf(cursorX, cursorY);
int flagCount = 0;
for (const Cell &neighbor : neighbors)
{
if (neighbor.getState() == Cell::State::Flagged)
flagCount++;
}
if (static_cast<int>(board.revealCellAt(cursorX, cursorY).getContent()) > flagCount)
break;
for (const Cell &neighbor : neighbors)
{
if (neighbor.getState() == Cell::State::Flagged)
continue;
board.revealCellAt(neighbor.getPosition().x, neighbor.getPosition().y);
}
break; break;
} }
if (!somethingHasBeenDone)
{
startTime = time(nullptr);
somethingHasBeenDone = true;
}
auto neighbors = board.getNeighborsOf(cursorX, cursorY);
int flagCount = 0;
for (const Cell &neighbor : neighbors)
{
if (neighbor.getState() == Cell::State::Flagged)
flagCount++;
}
if (static_cast<int>(board.revealCellAt(cursorX, cursorY).getContent()) > flagCount)
break;
for (const Cell &neighbor : neighbors)
{
if (neighbor.getState() == Cell::State::Flagged)
continue;
board.revealCellAt(neighbor.getPosition().x, neighbor.getPosition().y);
}
break;
} }
if (isPaused) if (isPaused)
@ -327,6 +438,19 @@ void startGame(Board &board)
} }
} }
SDL_GameController *findController()
{
for (int i = 0; i < SDL_NumJoysticks(); i++)
{
if (SDL_IsGameController(i))
{
return SDL_GameControllerOpen(i);
}
}
return nullptr;
}
int main() int main()
{ {
#ifndef _WIN32 #ifndef _WIN32
@ -342,6 +466,22 @@ int main()
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
#endif #endif
bool controllerSupport;
if (SDL_Init(SDL_INIT_GAMECONTROLLER) < 0)
{
std::cerr << "SDL failed to initialize GameController API! Controller support will not be available!" << std::endl;
controllerSupport = false;
}
else
{
controllerSupport = true;
}
controller = findController();
if (controller != nullptr)
controllerSupport = false; // no controller for you
start_color(); start_color();
init_pair(1, COLOR_BLUE, COLOR_BLACK); init_pair(1, COLOR_BLUE, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK);