diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fc20d9..c8cf222 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,5 +26,6 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") src/InputMonitor.cpp include/InputMonitor.h src/Settings.cpp - include/Settings.h) + include/Settings.h + include/displayUtils.h) endif() diff --git a/README.md b/README.md index 1a70c4f..bb84d89 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ 在游戏运行时,你可以输入以下按键: | 按键 | 功能 | -| --- | -------------- | +| --- |----------------| | `a` | 点击,增加点数 | | `u` | 打开升级菜单 | | `b` | 打开建筑商店 | diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..c47b340 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,55 @@ +# FunnyNumber Idle Game + +A small idle game you can run in the background while coding or working over SSH, giving you a little sense of achievement right from the command line. + +--- + +## ✨ Features + +* **Idle gameplay**: Points increase automatically over time +* **Active boost**: Press `a` to manually gain points +* **Upgrades & Buildings**: + + * **Upgrades** – make your clicks stronger + * **Buildings** – increase automatic growth rate +* **Funny big number system**: Uses an *“INT\_MAX carry-over”* mechanic — because it’s funnier this way + +--- + +## 🕹️ Controls + +While the game is running, you can press the following keys: + +| Key | Action | +| --- | ----------------------- | +| `a` | Click to gain points | +| `u` | Open upgrade menu | +| `b` | Open building shop | +| `s` | Open settings menu | +| `y` | Confirm action in menus | +| `m` | Return to main menu | + +--- + +## 📖 Game Mechanics + +* Initial click value: **1 point per click** +* Initial auto-growth: **1 point per second** +* Auto-growth increases based on the number of buildings you own +* Buying upgrades and buildings consumes points +* Each purchase generates a new, more expensive option + +--- + +## ⚙️ Settings (WIP) + +* Work in progress + +--- + +## 🚀 Possible Future Improvements + +* Save/load system +* More diverse upgrade/building trees +* Leaderboards +* Optional ASCII art UI diff --git a/include/DisplayUtils.h b/include/DisplayUtils.h new file mode 100644 index 0000000..9240e97 --- /dev/null +++ b/include/DisplayUtils.h @@ -0,0 +1,26 @@ +// +// Created by Calcite on 2025/9/15. +// +#ifndef DISPLAYUTILS_H +#define DISPLAYUTILS_H + +#pragma once +#include + +#define nl std::endl +#define RESET_COLOR "\033[0m" +#define RED "\033[31m" +#define GREEN "\033[32m" +#define YELLOW "\033[33m" +#define BLUE "\033[34m" +#define MAGENTA "\033[35m" +#define CYAN "\033[36m" +#define WHITE "\033[37m" + +const std::string title = +"\033[31m __ __ _ ____ ____ ___ ____ ____\n" +"\033[33m ( )( ( \\(_ _)( __)/ __)( __)( _ \\\n" +"\033[32m )( / / )( ) _)( (_ \\ ) _) ) /\n" +"\033[34m (__)\\_)__) (__) (____)\\___/(____)(__\\_)\n"; + +#endif //DISPLAYUTILS_H diff --git a/include/Game.h b/include/Game.h index a0254d9..f239a1e 100644 --- a/include/Game.h +++ b/include/Game.h @@ -1,33 +1,32 @@ #ifndef GAME_H #define GAME_H -#define nl std::endl -#define RESET_COLOR "\033[0m" -#define RED "\033[31m" -#define GREEN "\033[32m" -#define YELLOW "\033[33m" -#define BLUE "\033[34m" -#define MAGENTA "\033[35m" -#define CYAN "\033[36m" -#define WHITE "\033[37m" + #include #include #include + +#ifdef _WIN64 +#include +#else +#include +#include +#endif + +#include "DisplayUtils.h" #include "Buyables.h" #include "InputMonitor.h" -#include "Settings.h" +class Settings; class Game { public: - const std::string title = - "\033[31m __ __ _ ____ ____ ___ ____ ____\n" -"\033[33m ( )( ( \\(_ _)( __)/ __)( __)( _ \\\n" -"\033[32m )( / / )( ) _)( (_ \\ ) _) ) /\n" -"\033[34m (__)\\_)__) (__) (____)\\___/(____)(__\\_)\n"; + std::string username; std::string statMessage; std::atomic bIsRunning; + std::atomic bInputMode; + std::atomic bIsDisplayOnHalt; const int FRAMERATE = 40; const int AUTO_INCREMENT_RATE = 40; const int CLICK_COOLDOWN = 10; @@ -36,6 +35,7 @@ class Game { int iClickIncrement; int iAutoIncrement; int iOptionIdx; + int LINE_HEIGHT = 0; bool bIsInputThreadRunning; void (Game::*buyConfirm)(int idx); void (Settings::*settingConfirm)(int idx); @@ -51,14 +51,14 @@ class Game { Game(); ~Game(); void gameRun(); - + void initWindow(); + void setHalt(bool active); void display(); void displayNumber(); void displayMenu(); - static void displaySettings(); - static void displayMainMenu(); void displayUpgrade(); void displayShop(); + static void displayMainMenu(); void handleKey(int ch); static void clear_screen(); @@ -66,7 +66,7 @@ class Game { std::vector increment(int carry); std::vector increment(const std::vector& carry); std::vector decrement(const std::vector& cost); - bool isSufficient(const std::vector& cost); + bool isSufficient(const std::vector& cost) const; void click(); void buyUpgrade(int idx); diff --git a/include/InputMonitor.h b/include/InputMonitor.h index d3fed5d..3a6433b 100644 --- a/include/InputMonitor.h +++ b/include/InputMonitor.h @@ -26,10 +26,13 @@ class InputMonitor { public: using Callback = std::function; - InputMonitor(): bRunning(false){} + InputMonitor(): paused(false), bRunning(false){} ~InputMonitor(); + std::atomic paused; void start(const Callback& callback); void stop(); + void pause(); + void resume(); private: std::atomic bRunning; diff --git a/include/Settings.h b/include/Settings.h index 2b79b32..087fd4b 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -4,16 +4,38 @@ #ifndef SETTINGS_H #define SETTINGS_H +#include "InputMonitor.h" +#include - - +class Game; class Settings { public: - Settings(); + int thisSettingPage; + Game* game = nullptr; + enum settingState{menu, username, exportSave, loadSave}; + Settings(Game* g); ~Settings(); - void load_settings(); - void save_settings(); - void confirm(int idx); + void loadSettings(); + void saveSettings(); + void displaySettings(); + + void displaySettingsMainMenu(); + + + void displaySettingsExportSave(); // $username.ids + + void displaySettingsLoadSave(); + + void setUsername(); + + void confirm(int option); + + struct saveData { + std::vector theFunnyNumber; + int uLevel; + int bLevel; + }; + }; diff --git a/src/Game.cpp b/src/Game.cpp index 4613a07..6295b2a 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -5,29 +5,15 @@ #include "Game.h" #include "Settings.h" -#ifdef __linux__ -#include -#include -#include -#endif - -#ifdef __APPLE__ -#include -#include -#include -#endif - -#ifdef _WIN64 -#include -#include -#endif - Game::Game() { + initWindow(); currentState = MAIN; monitor = new InputMonitor; - settings = new Settings; + settings = new Settings(this); statMessage = ""; + bInputMode.store(false); + bIsDisplayOnHalt.store(false); clear_screen(); upgrades.emplace_back(upgrade::initPrice, upgrade::initBoost); buildings.emplace_back(building::initPrice, building::initBoost); @@ -52,12 +38,39 @@ Game::~Game() { theFunnyNumber.clear(); bIsRunning.store(false); } +void Game::initWindow() { +#ifdef _WIN64 + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(hConsole, &csbi); + LINE_HEIGHT = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; +#else + struct winsize win; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &win); + LINE_HEIGHT = win.ws_row; + int LINE_WIDTH = win.ws_col; +#endif + while (LINE_HEIGHT < 18 || LINE_WIDTH < 40) { + std::cout<displaySettings(); break; default: displayMainMenu(); @@ -126,10 +141,13 @@ void Game::displayMainMenu() { std::cout<<"[e] Export save"< 6)std::cout << GREEN <<"..."< LINE_HEIGHT - 13 && upgrades.size() - i > LINE_HEIGHT - 13) { + continue; + } std::cout << GREEN << "Upgrade " << i + 1 << ": " << upgrades[i].vBoost[0] << " boost " << " - Cost: " << upgrades[i].vPrice[0] << nl; @@ -138,10 +156,13 @@ void Game::displayUpgrade() { std::cout << "Press 'y' to buy, 'm' to return to main menu" << nl; } -void Game::displayShop() { +void Game::displayShop(){ std::cout << YELLOW << "Shop Menu" << RESET_COLOR << nl << nl; - + if (buildings.size() > 8)std::cout << GREEN <<"..."; for (int i = 0; i < buildings.size(); ++i) { + if (upgrades.size() > LINE_HEIGHT - 13 && upgrades.size() - i > LINE_HEIGHT - 13) { + continue; + } std::cout << GREEN << "Building " << i + 1 << ": " << buildings[i].vBoost[0] << " boost " << " - Cost: " << buildings[i].vPrice[0] << nl; @@ -151,24 +172,20 @@ void Game::displayShop() { std::cout << "Press 'y' to buy, 'm' to return to main menu" << nl; } -void Game::displaySettings() { - std::cout << YELLOW << "Settings Menu" << RESET_COLOR << nl << nl; - std::cout << GREEN << "1. Toggle Sound" << RESET_COLOR << nl; - std::cout << GREEN << "2. Toggle Fullscreen" << RESET_COLOR << nl; - std::cout << GREEN << "3. Change Language" << RESET_COLOR << nl; - std::cout << GREEN << "4. Back to Main Menu" << RESET_COLOR << nl; - - std::cout << nl; -} void Game::handleKey(int ch) { + if (bInputMode.load()){return;} std::lock_guard lk(stateMutex); switch (ch) { case 'a': increment(iClickIncrement); break; case 'u': + if (currentState==SETTINGS) { + settings->thisSettingPage = Settings::username; + break; + } currentState = UPGRADE; buyConfirm = &Game::buyUpgrade; break; @@ -185,6 +202,12 @@ void Game::handleKey(int ch) { break; case 'm': backToMain(); + break; + case 'l': + if (currentState == SETTINGS)settings->thisSettingPage = Settings::loadSave; + break; + case 'e': + if (currentState == SETTINGS)settings->thisSettingPage = Settings::exportSave; default: break; } } @@ -251,9 +274,9 @@ std::vector Game::decrement(const std::vector& cost) { return theFunnyNumber; } -bool Game::isSufficient(const std::vector& cost) { +bool Game::isSufficient(const std::vector& cost) const { if (theFunnyNumber.size() < cost.size()) return false; - for (int i = cost.size() - 1; i >= 0; --i) { + for (int i = static_cast(cost.size()) - 1; i >= 0; --i) { if (theFunnyNumber[i] < cost[i]) return false; if (theFunnyNumber[i] > cost[i]) return true; } @@ -271,7 +294,7 @@ void Game::click() { void Game::buyUpgrade(int idx) { if (idx < 0 || idx >= upgrades.size()) return; - upgrade& u = upgrades[idx]; + upgrade& u = upgrades[upgrades.size() - 1]; if (!isSufficient(u.vPrice)) { statMessage = "too poor"; return; @@ -290,7 +313,7 @@ void Game::buyUpgrade(int idx) { void Game::buyBuilding(int idx) { if (idx < 0 || idx >= buildings.size()) return; - building& b = buildings[idx]; + building& b = buildings[buildings.size() - 1]; if (!isSufficient(b.vPrice)) { statMessage = "too poor"; return; @@ -303,4 +326,9 @@ void Game::buyBuilding(int idx) { buildings.push_back(building::next_buyable(b)); statMessage = "success!"; +} + +void Game::setHalt(bool active) { + bInputMode.store(active); + bIsDisplayOnHalt.store(active); } \ No newline at end of file diff --git a/src/InputMonitor.cpp b/src/InputMonitor.cpp index 55e6a7c..af3d27b 100644 --- a/src/InputMonitor.cpp +++ b/src/InputMonitor.cpp @@ -54,6 +54,8 @@ int InputMonitor::pollInput() { } return -1; } +void InputMonitor::pause() { paused.store(true); resetInput(); } +void InputMonitor::resume() { initInput(); paused.store(false); } #else void InputMonitor::initInput() { struct termios newt; @@ -76,4 +78,7 @@ int InputMonitor::pollInput() { if (ch != EOF) return ch; return -1; } +void InputMonitor::pause() { paused.store(true); resetInput(); } +void InputMonitor::resume() { initInput(); paused.store(false); } + #endif \ No newline at end of file diff --git a/src/Settings.cpp b/src/Settings.cpp index 0958922..bfc00dc 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -1,16 +1,73 @@ #include "Settings.h" -Settings::Settings() { +#include "Game.h" +#include "DisplayUtils.h" +#include +Settings::Settings(Game* g) { + thisSettingPage = menu; + game = g; } -Settings::~Settings() { +Settings::~Settings() = default; +void Settings::loadSettings() { + +} +void Settings::saveSettings() { + +} +void Settings::displaySettings() { + switch (thisSettingPage) { + case menu: + displaySettingsMainMenu(); + break; + case username: + setUsername(); + break; + case exportSave: + displaySettingsExportSave(); + break; + case loadSave: + displaySettingsLoadSave(); + break; + default: + thisSettingPage = menu; + displaySettingsMainMenu(); + break; + } +} +void Settings::displaySettingsMainMenu() { + std::cout << YELLOW << "Settings Menu" << RESET_COLOR << nl << nl; + + std::cout << YELLOW<< "[u] set user name" << RESET_COLOR << nl; + std::cout << YELLOW<< "[e] export save" << RESET_COLOR << nl; + std::cout << YELLOW<< "[l] load save" << RESET_COLOR << nl; + + + std::cout << nl; } -void Settings::load_settings() { + +void Settings::displaySettingsExportSave() { } -void Settings::save_settings() { +void Settings::displaySettingsLoadSave() { } -void Settings::confirm(int idx) { + +void Settings::setUsername() { + // halt display and input monitor to let user type something + game->setHalt(true); + game->monitor->pause(); + std::cout << "\033[H\033[J"; + std::cout << title << nl; + std::string userName; + std::cout<<"what's your name?"<< nl; + std::getline(std::cin, userName); + game->monitor->resume(); + game->setHalt(false); + thisSettingPage = menu; + game->username = userName; + } + +void Settings::confirm(int idx){}