diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4497d42..23d0b3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,11 +12,11 @@ jobs: strategy: matrix: include: - - os: windows - make_cmd: wrebuild - suffix: zip - zlib: wzlib - name: win + # - os: windows + # make_cmd: wrebuild + # suffix: zip + # zlib: wzlib + # name: win - os: macos make_cmd: xrebuild suffix: tgz @@ -88,9 +88,9 @@ jobs: strategy: matrix: include: - - os: win - artifact_name: EscapeTheFate.zip - asset_name: supergoon_win.zip + # - os: win + # artifact_name: EscapeTheFate.zip + # asset_name: supergoon_win.zip - os: mac artifact_name: EscapeTheFate.tgz asset_name: supergoon_mac.tgz diff --git a/README.md b/README.md index 3c456f9..3b4e606 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,12 @@ - Play the game here, or likely watch its "progress" [Play!](https://escapethefate.supergoon.com) - Project board [Board](https://github.com/users/kjblanchard/projects/11) - ![Build All Platforms]( https://github.com/kjblanchard/sgEngine/actions/workflows/build.yml/badge.svg) -- ![Status Picture](https://github.com/kjblanchard/sgEngine/blob/master/img/debug.gif?raw=true) + +- Current Status picture + - ![Status Picture](https://github.com/kjblanchard/sgEngine/blob/master/img/state.gif?raw=true) +- Uses imgui for development debugging. + - ![Status Picture](https://github.com/kjblanchard/sgEngine/blob/master/img/state.gif?raw=true) + ## Installation - Run any of the make commands for your platform, or run cmake directly, currently this bundles the demo game and engine together diff --git a/assets/sfx/typing.ogg b/assets/bgm/typing.ogg similarity index 100% rename from assets/sfx/typing.ogg rename to assets/bgm/typing.ogg diff --git a/assets/bgm/victory.ogg b/assets/bgm/victory.ogg new file mode 100644 index 0000000..b2ace39 Binary files /dev/null and b/assets/bgm/victory.ogg differ diff --git a/assets/config.json b/assets/config.json index 2da4e26..71e9189 100644 --- a/assets/config.json +++ b/assets/config.json @@ -8,7 +8,7 @@ "x": 512, "y": 288 }, - "skipLogos": false, - "mute": false + "skipLogos": true, + "mute": true } diff --git a/assets/img/interaction.png b/assets/img/interaction.png new file mode 100644 index 0000000..4cbeddb Binary files /dev/null and b/assets/img/interaction.png differ diff --git a/assets/tiled/goonRpg.tiled-session b/assets/tiled/goonRpg.tiled-session index 88bce41..e120257 100644 --- a/assets/tiled/goonRpg.tiled-session +++ b/assets/tiled/goonRpg.tiled-session @@ -1,5 +1,5 @@ { - "activeFile": "debugSouth.tmj", + "activeFile": "debugTown.tmj", "expandedProjectPaths": [ "." ], @@ -15,8 +15,8 @@ "scale": 2, "selectedLayer": 1, "viewCenter": { - "x": 396.5, - "y": 246.5 + "x": 393, + "y": 243 } }, "debugTown.tmj": { @@ -25,10 +25,10 @@ 3 ], "scale": 2, - "selectedLayer": 4, + "selectedLayer": 5, "viewCenter": { - "x": 300.25, - "y": 140.25 + "x": 300.5, + "y": 140.5 } }, "debugTownHome.tmj": { @@ -36,14 +36,14 @@ 4 ], "expandedObjectLayers": [ - 5, - 6 + 6, + 5 ], "scale": 3, "selectedLayer": 4, "viewCenter": { - "x": 125.5, - "y": 85.5 + "x": 125.66666666666669, + "y": 85.66666666666664 } }, "house.tsj": { @@ -75,12 +75,12 @@ ], "project": "goonRpg.tiled-project", "recentFiles": [ - "debugTown.tmj", - "debugTownHome.tmj", - "house.tsj", - "inside.tsj", - "terrain.tsj", + "debugSouth.tmj", "outside.tsj", - "debugSouth.tmj" + "terrain.tsj", + "inside.tsj", + "house.tsj", + "debugTownHome.tmj", + "debugTown.tmj" ] } diff --git a/img/debug.gif b/img/debug.gif index 5818b2f..a094038 100644 Binary files a/img/debug.gif and b/img/debug.gif differ diff --git a/img/state.gif b/img/state.gif new file mode 100644 index 0000000..70f1858 Binary files /dev/null and b/img/state.gif differ diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index be8412d..45a5826 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -11,6 +11,7 @@ set(SRC_FILES src/Tween/Tween.cpp src/Tween/Sequence.cpp src/Events.cpp + src/Filesystem.cpp src/Aseprite/AsepriteAnimation.cpp src/Content/AsepriteDocument.cpp src/Content/Font.cpp @@ -42,6 +43,7 @@ if(imgui) src/Widgets/UIWidget.cpp src/Widgets/Global.cpp src/Widgets/LevelWidget.cpp + src/Widgets/ConsoleWidget.cpp ) endif() diff --git a/src/engine/external/include/SupergoonEngine/log.h b/src/engine/external/include/SupergoonEngine/log.h index cfeb9b6..489e70a 100644 --- a/src/engine/external/include/SupergoonEngine/log.h +++ b/src/engine/external/include/SupergoonEngine/log.h @@ -10,10 +10,6 @@ * */ -/** - * @brief Wraps a function, or a line (so that you can return and define a variable) and measure it and display the time it took, identifier is a descriptive name for it - * - */ /** * @brief The level that we should show debug events at. * @@ -73,6 +69,8 @@ void sgLogError(const char *format, ...); */ void sgLogCritical(const char *fmt, ...); +void sgSetDebugFunction(void (*)(const char *, const char *, int)); + void sgSetLogLevel(int newLevel); #ifdef __cplusplus diff --git a/src/engine/external/src/log.c b/src/engine/external/src/log.c index eb8757c..a445e04 100644 --- a/src/engine/external/src/log.c +++ b/src/engine/external/src/log.c @@ -7,6 +7,8 @@ #define MAX_LOG_SIZE 400 +static void (*logFunc)(const char *, const char *, int) = NULL; + /** * @brief The file that will be written to when logs are put. * @@ -60,6 +62,9 @@ int sgInitializeDebugLogFile(void) { sgLogError("Could not open file for logging!"); return 0; } +void sgSetDebugFunction(void (*func)(const char *, const char *, int)) { + logFunc = func; +} int sgCloseDebugLogFile(void) { if (!openDebugFile) @@ -77,6 +82,9 @@ static void Log(sgLogLevel level, const char *thing_to_write) { strftime(buf, sizeof(buf), "%m-%d-%H:%M-%S", gm_time); FILE *outStream = level == Log_LError ? stderr : stdout; fprintf(outStream, "%s: %s end\n", buf, thing_to_write); + if (logFunc) { + logFunc(buf, thing_to_write, level); + } if (level == Log_LError && openDebugFile) { fprintf(openDebugFile, "%s: %s\n", buf, thing_to_write); } diff --git a/src/engine/include/Supergoon/Aseprite/AsepriteAnimation.hpp b/src/engine/include/Supergoon/Aseprite/AsepriteAnimation.hpp index a54f295..9433a00 100644 --- a/src/engine/include/Supergoon/Aseprite/AsepriteAnimation.hpp +++ b/src/engine/include/Supergoon/Aseprite/AsepriteAnimation.hpp @@ -8,7 +8,6 @@ class AsepriteDocument; class AsepriteAnimation { public: AsepriteAnimation(std::string n); - ~AsepriteAnimation(); std::string Filename(); void Load(); void UpdateAnimation(double d); diff --git a/src/engine/include/Supergoon/Content/ContentRegistry.hpp b/src/engine/include/Supergoon/Content/ContentRegistry.hpp index 85e1153..e676845 100644 --- a/src/engine/include/Supergoon/Content/ContentRegistry.hpp +++ b/src/engine/include/Supergoon/Content/ContentRegistry.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include @@ -13,7 +14,7 @@ class ContentRegistry { // Loads all content that isn't loaded static void LoadAllContent(); // Clear any content that isn't being used, useful to run between loading levels, if forced it will force destroy(usually only actually cleans when above 20 stale) - static void ClearStaleContent(bool force = false); + static void ClearStaleContent(); // Unloads all content and clears the loaded content list, even if shared ptrs still have references static void DestroyAllContent(); /** @@ -27,9 +28,12 @@ class ContentRegistry { static std::shared_ptr CreateContent(const std::string& key, Args&&... args) { auto it = _loadedContent.find(key); if (it != _loadedContent.end()) { - std::shared_ptr specificContent = std::dynamic_pointer_cast(it->second); - if (specificContent) { - return specificContent; + auto shared = it->second.lock(); + if (shared) { + auto sharedCast = std::dynamic_pointer_cast(shared); + if (sharedCast) { + return sharedCast; + } } } // If content doesn't exist or is expired, load it and store it in the map @@ -39,7 +43,7 @@ class ContentRegistry { } static bool ContentExists(const std::string& key) { auto it = _loadedContent.find(key); - if (it != _loadedContent.end()) { + if (it != _loadedContent.end() && !it->second.expired()) { return true; } return false; @@ -48,16 +52,23 @@ class ContentRegistry { static std::shared_ptr GetContent(const std::string& key) { auto it = _loadedContent.find(key); if (it != _loadedContent.end()) { - std::shared_ptr specificContent = std::dynamic_pointer_cast(it->second); - if (specificContent) { - return specificContent; + auto thing = it->second.lock(); + if (it->second.lock()) { + auto shared = std::dynamic_pointer_cast(it->second.lock()); + if (shared) { + return shared; + } } + // std::shared_ptr specificContent = std::dynamic_pointer_cast(it->second); + // if (specificContent) { + // return specificContent; + // } } return nullptr; } private: - static std::unordered_map> _loadedContent; + static std::unordered_map> _loadedContent; friend class ContentWidget; }; diff --git a/src/engine/include/Supergoon/Content/Image.hpp b/src/engine/include/Supergoon/Content/Image.hpp index 2f22a98..92d4ca2 100644 --- a/src/engine/include/Supergoon/Content/Image.hpp +++ b/src/engine/include/Supergoon/Content/Image.hpp @@ -33,10 +33,10 @@ class Image : public Content { void SetAlpha(int alpha); private: - SDL_Texture* _image; + SDL_Texture* _image = nullptr; ImageType _imageType; - float _width; - float _height; + float _width = 0; + float _height = 0; SDL_Surface* _surface = nullptr; Color _imageColor = {255, 255, 255, 255}; diff --git a/src/engine/include/Supergoon/Content/Text.hpp b/src/engine/include/Supergoon/Content/Text.hpp index a012fb9..90e29eb 100644 --- a/src/engine/include/Supergoon/Content/Text.hpp +++ b/src/engine/include/Supergoon/Content/Text.hpp @@ -1,12 +1,13 @@ #pragma once #include #include -#include +#include #include #include #include #include namespace Supergoon { +class Image; class Text : public Content { public: Text(std::string text, std::string fontName, int size); @@ -37,6 +38,7 @@ class Text : public Content { int _lettersToDraw; int _paddingL = 0, _paddingR = 0, _paddingT = 0; //,_paddingB = 0; int _alpha = 255; + std::vector> _letterImages; std::shared_ptr _font; std::shared_ptr _image; // We will keep the text within this space diff --git a/src/engine/include/Supergoon/ECS/Components/GameStateComponent.hpp b/src/engine/include/Supergoon/ECS/Components/GameStateComponent.hpp index 23eb3c8..8360f08 100644 --- a/src/engine/include/Supergoon/ECS/Components/GameStateComponent.hpp +++ b/src/engine/include/Supergoon/ECS/Components/GameStateComponent.hpp @@ -8,6 +8,7 @@ struct GameState { bool Loading; bool CameraFollowTarget; bool EnteringBattle; + bool Interacting; // Level* CurrentLevel; }; } // namespace Supergoon diff --git a/src/engine/include/Supergoon/Events.hpp b/src/engine/include/Supergoon/Events.hpp index 3225708..a10c458 100644 --- a/src/engine/include/Supergoon/Events.hpp +++ b/src/engine/include/Supergoon/Events.hpp @@ -12,6 +12,7 @@ struct BuiltinEventTypes { uint32_t LevelChangeEvent; uint32_t ResetGameEvent; uint32_t PlayBgmEvent; + uint32_t StopBgmEvent; uint32_t UiFadeInStart; uint32_t UiFadeInEnd; uint32_t UiFadeOutStart; diff --git a/src/engine/include/Supergoon/Filesystem.hpp b/src/engine/include/Supergoon/Filesystem.hpp new file mode 100644 index 0000000..0646352 --- /dev/null +++ b/src/engine/include/Supergoon/Filesystem.hpp @@ -0,0 +1,9 @@ +#pragma once +#include +#include +#include +namespace Supergoon { +// Loads a file that is needed for the game to run, if the file doesn't exist throws an error and exits. +std::ifstream SafeLoadFile(std::string& name); + +} // namespace Supergoon diff --git a/src/engine/include/Supergoon/Game.hpp b/src/engine/include/Supergoon/Game.hpp index 1c423ad..faa08c9 100644 --- a/src/engine/include/Supergoon/Game.hpp +++ b/src/engine/include/Supergoon/Game.hpp @@ -1,16 +1,16 @@ #pragma once -#include -#include #include -#include -#include -#include #include +typedef union SDL_Event SDL_Event; + namespace Supergoon { +class Sound; +class Graphics; +class Events; class Game; -} +} // namespace Supergoon #define REGISTER_GAME(DERIVED_GAME_CLASS) \ extern "C" Game* sgRegisterGame() { \ @@ -26,12 +26,8 @@ Supergoon::Game* sgRegisterGame(); #endif namespace Supergoon { -class Sound; -class Graphics; -class Events; class Game { public: - Game(); virtual ~Game(); // Happens once before game start void Initialize(); @@ -48,19 +44,16 @@ class Game { // Happens after update virtual void Draw() = 0; virtual void Reset() = 0; - // inline Sound& GetSound() { return *_sound; } - // static inline Game* Instance() { return _game; } static double DeltaTime(); static double DeltaTimeMS(); - // static inline void SetGameInstance(Game* game) { _game = game; }; + protected: private: void InitializeImGui(); bool _initialized = false; geClock _clock; - std::unique_ptr _sound = nullptr; - std::unique_ptr _graphics = nullptr; - std::unique_ptr _events = nullptr; - // static Game* _game; + std::shared_ptr _sound = nullptr; + std::shared_ptr _graphics = nullptr; + std::shared_ptr _events = nullptr; }; } // namespace Supergoon diff --git a/src/engine/include/Supergoon/Graphics/Graphics.hpp b/src/engine/include/Supergoon/Graphics/Graphics.hpp index b71bbdf..d795167 100644 --- a/src/engine/include/Supergoon/Graphics/Graphics.hpp +++ b/src/engine/include/Supergoon/Graphics/Graphics.hpp @@ -15,6 +15,7 @@ class Graphics { ~Graphics(); void CreateWindow(int width, int height, std::string name); void InitializeImGui(); + void CloseImGui(); void DrawStart(); void DrawEnd(); void DrawImGui(); diff --git a/src/engine/include/Supergoon/Primitives/Point.hpp b/src/engine/include/Supergoon/Primitives/Point.hpp index 71ae51f..d57353a 100644 --- a/src/engine/include/Supergoon/Primitives/Point.hpp +++ b/src/engine/include/Supergoon/Primitives/Point.hpp @@ -2,6 +2,8 @@ namespace Supergoon { struct Point { int X, Y; + inline Point(int x, int y) : X(x), Y(y) {} + inline Point() : X(0), Y(0) {} inline bool Zero() { return X && Y; } }; diff --git a/src/engine/include/Supergoon/Primitives/Rectangle.hpp b/src/engine/include/Supergoon/Primitives/Rectangle.hpp index b180bb5..fb64cea 100644 --- a/src/engine/include/Supergoon/Primitives/Rectangle.hpp +++ b/src/engine/include/Supergoon/Primitives/Rectangle.hpp @@ -4,10 +4,15 @@ namespace Supergoon { struct Rectangle { int X, Y, W, H; + inline Rectangle() : X(0), Y(0), W(0), H(0) {} + inline Rectangle(int x, int y, int w, int h) : X(x), Y(y), W(w), H(h) {} inline bool Zero() { return (X == 0 && Y == 0 && W == 0 && H == 0); } operator SDL_FRect() const { return SDL_FRect{(float)X, (float)Y, (float)W, (float)H}; } + operator SDL_Rect() const { + return SDL_Rect{X, Y, W, H}; + } inline bool IsOverlap(Rectangle* rhs) { return SDL_HasRectIntersection((SDL_Rect*)this, (SDL_Rect*)rhs); } @@ -19,11 +24,19 @@ struct Rectangle { SDL_GetRectIntersection((SDL_Rect*)this, (SDL_Rect*)rhs, (SDL_Rect*)&r); return r; } + inline bool operator==(const Rectangle& rhs) { + return X == rhs.X && Y == rhs.Y && W == rhs.W && H == rhs.H; + } }; struct RectangleF { float X, Y, W, H; + inline RectangleF() : X(0), Y(0), W(0), H(0) {} + inline RectangleF(float x, float y, float w, float h) : X(x), Y(y), W(w), H(h) {} inline bool Zero() { return (X == 0 && Y == 0 && W == 0 && H == 0); } + operator SDL_Rect() const { + return SDL_Rect{(int)X, (int)Y, (int)W, (int)H}; + } operator SDL_FRect() const { return SDL_FRect{X, Y, W, H}; } @@ -39,6 +52,9 @@ struct RectangleF { SDL_GetRectIntersectionFloat((SDL_FRect*)this, (SDL_FRect*)rhs, (SDL_FRect*)&r); return r; } + inline bool operator==(const RectangleF& rhs) { + return X == rhs.X && Y == rhs.Y && W == rhs.W && H == rhs.H; + } }; } // namespace Supergoon diff --git a/src/engine/include/Supergoon/Sound.hpp b/src/engine/include/Supergoon/Sound.hpp index 8445e57..b44300a 100644 --- a/src/engine/include/Supergoon/Sound.hpp +++ b/src/engine/include/Supergoon/Sound.hpp @@ -11,7 +11,9 @@ namespace Supergoon { class Tween; class Sound { public: - inline Sound() { _instance = this; } + // inline Sound() { _instance = this; } + Sound(); + ~Sound(); // Initializes the Supergoon Sound void InitializeSound(); /** @@ -22,16 +24,17 @@ class Sound { * @return true loaded successfully * @return false error */ - bool LoadBgm(std::string filename, float volume = 1.0, int loops = -1); + bool LoadBgm(std::string filename, float volume = 1.0, int loops = -1, int slot = 0); + // TODO this needs to be cleaned up // Plays a bgm loaded into the bgm slot - void PlayBgm(); + void PlayBgm(int slot = 0); // Pauses bgm in bgm slot - void PauseBgm(); + void PauseBgm(int slot = 0); // Pauses bgm in bgm slot - void StopBgm(); - void StopBgmFadeout(); + void StopBgm(int slot = 0); + void StopBgmFadeout(int slot = 0, float fadeTime = 1.0); // Sets current playing bgm volume, 0 - 1.0f - void SetPlayingBgmVolume(float volume); + void SetPlayingBgmVolume(float volume, int slot = 0); // Sets the global volume multiplier for bgm, 0 - 1.0f void SetGlobalBgmVolume(float volume); inline void SetGlobalSfxVolume(float volume) { _globalSfxVolume = volume; }; @@ -42,18 +45,22 @@ class Sound { // Updates all internal bgms void Update(); void CheckForStaleSfxStreams(); - void UpdatePlayingBgmVolume(); + void UpdatePlayingBgmVolume(int slot = 0); const size_t _totalSfxStreams = 4; float _globalBgmVolume = 1.0f; float _globalSfxVolume = 1.0f; - float _playingBgmVolume = 0; std::vector> _sfxStreams; std::vector _playingStreams; std::queue _usableSfxStreams; - float _bgmOriginalVolume = 0; bool _fadingOut = false; - sgBgm* _bgm = nullptr; - Tween* _bgmTween = nullptr; + // sgBgm* _bgm = nullptr; + // sgBgm* _bgmSecondary = nullptr; + const int _bgmSlots = 2; + std::vector _bgms; + std::vector _playingBgmVolume; + std::vector _bgmOriginalVolume; + std::vector _tweens; + // Tween* _bgmTween = nullptr; static Sound* _instance; friend class Game; friend class SoundWidget; diff --git a/src/engine/include/Supergoon/UI/UI.hpp b/src/engine/include/Supergoon/UI/UI.hpp index 2ad3681..043d275 100644 --- a/src/engine/include/Supergoon/UI/UI.hpp +++ b/src/engine/include/Supergoon/UI/UI.hpp @@ -11,12 +11,12 @@ class Panel; class UI { public: static Panel* Initialize(); - static void LoadUIFromFile(std::string filename, Panel* rootPanel = UIInstance); + static void LoadUIFromFile(std::string filename, Panel* rootPanel = UIInstance.get()); static void Draw(); static void Update(); - static void Reset(); static void RegisterUIEvents(); - static Panel* UIInstance; + inline static void Reset() { UIInstance.reset(); } + static std::unique_ptr UIInstance; // Set when the UI diff --git a/src/engine/include/Supergoon/UI/UIText.hpp b/src/engine/include/Supergoon/UI/UIText.hpp index fca7534..5b27e63 100644 --- a/src/engine/include/Supergoon/UI/UIText.hpp +++ b/src/engine/include/Supergoon/UI/UIText.hpp @@ -11,18 +11,34 @@ class UIText : public UIObject { virtual void Draw() override; virtual void OnDirty() override; inline void SetCenter(bool center) { - CenterText = center; + CenterTextX = center; Dirty = true; } + inline void SetWordWrap(bool wrap) { + WordWrap = wrap; + Dirty = true; + } + inline void SetCenterY(bool center) { + _centerTextY = center; + Dirty = true; + } + inline void SetCurrentLetters(int letters) { + _currentLetters = letters; + Dirty = true; + } + inline int* CurrentLettersRef() { return &_currentLetters; } void UpdateText(std::string text); std::shared_ptr TextPtr; - int currentLetters = 0; RectangleF TextDrawRect = RectangleF(); RectangleF TextSrcRect = RectangleF(); bool WordWrap; - bool CenterText = false; + bool CenterTextX = false; std::string DisplayText; + private: + bool _centerTextY = false; + int _currentLetters = 0; + private: friend class UIWidget; }; diff --git a/src/engine/include/Supergoon/Widgets/ConsoleWidget.hpp b/src/engine/include/Supergoon/Widgets/ConsoleWidget.hpp new file mode 100644 index 0000000..c79cb48 --- /dev/null +++ b/src/engine/include/Supergoon/Widgets/ConsoleWidget.hpp @@ -0,0 +1,9 @@ +#pragma once +namespace Supergoon { +class ConsoleWidget { + public: + static void InitializeConsoleWidget(); + static void ShowConsoleWidget(); +}; + +} // namespace Supergoon diff --git a/src/engine/include/Supergoon/Widgets/Widgets.hpp b/src/engine/include/Supergoon/Widgets/Widgets.hpp index 12f0167..218ceda 100644 --- a/src/engine/include/Supergoon/Widgets/Widgets.hpp +++ b/src/engine/include/Supergoon/Widgets/Widgets.hpp @@ -4,6 +4,7 @@ class Game; class Widgets { public: static void ShowWidgets(Game* game); + static void InitializeWidgets(); static int GetDefaultWindowFlags(); }; diff --git a/src/engine/include/Supergoon/World/Level.hpp b/src/engine/include/Supergoon/World/Level.hpp index e8fac07..a1e980d 100644 --- a/src/engine/include/Supergoon/World/Level.hpp +++ b/src/engine/include/Supergoon/World/Level.hpp @@ -51,7 +51,9 @@ class Level { GameObject *NewSolidObject(Rectangle r); private: + std::string GetBasePathForTiled(); std::string _name; + std::vector> _backgroundTilesetImages; std::shared_ptr _background; std::vector _gameObjects; std::unique_ptr _mapData; diff --git a/src/engine/src/Aseprite/AsepriteAnimation.cpp b/src/engine/src/Aseprite/AsepriteAnimation.cpp index 5312295..77a239c 100644 --- a/src/engine/src/Aseprite/AsepriteAnimation.cpp +++ b/src/engine/src/Aseprite/AsepriteAnimation.cpp @@ -7,19 +7,11 @@ using namespace Supergoon; -// #ifdef GN_PLATFORM_MACOS -// static const char *_animationPrefix = "../Resources/assets/img/"; -// #else -// static const char *_animationPrefix = "assets/img/"; -// #endif - AsepriteAnimation::AsepriteAnimation(std::string n) : _animNum(0), _frame(0), _nextFrame(0), _frameTime(0), _aseDocument(nullptr) { _filePath = n; Load(); } -AsepriteAnimation::~AsepriteAnimation() { -} std::string AsepriteAnimation::Filename() { return std::string(SDL_GetBasePath()) + "assets/img/" + _aseDocument->meta.image; } diff --git a/src/engine/src/Content/AsepriteDocument.cpp b/src/engine/src/Content/AsepriteDocument.cpp index 42c9747..ad80c82 100644 --- a/src/engine/src/Content/AsepriteDocument.cpp +++ b/src/engine/src/Content/AsepriteDocument.cpp @@ -9,6 +9,10 @@ using json = nlohmann::json; using namespace Supergoon; void AsepriteDocument::Load() { std::ifstream fileStream(_filePath); + if (!fileStream.is_open()) { + sgLogError("Could not open file to load aseprite document for %s", _filePath.c_str()); + return; + } auto j = json::parse(fileStream); for (const auto &frameJson : j["frames"]) { Frame frame; diff --git a/src/engine/src/Content/ContentRegistry.cpp b/src/engine/src/Content/ContentRegistry.cpp index 171be80..935173e 100644 --- a/src/engine/src/Content/ContentRegistry.cpp +++ b/src/engine/src/Content/ContentRegistry.cpp @@ -2,23 +2,17 @@ #include #include using namespace Supergoon; -std::unordered_map> ContentRegistry::_loadedContent = {}; +std::unordered_map> ContentRegistry::_loadedContent = {}; void ContentRegistry::LoadContent(Content& content) { content.LoadContent(); } -void ContentRegistry::ClearStaleContent(bool force) { - // If there is a lot of stale content, clear it all. - int count = std::count_if(_loadedContent.begin(), _loadedContent.end(), - [](const auto& pair) { return pair.second.use_count() <= 1; }); - if (!force && count < 20) { - return; - } - sgLogWarn("Clearing stale content as stale content count is %d", count); +void ContentRegistry::ClearStaleContent() { for (auto it = _loadedContent.begin(); it != _loadedContent.end();) { - if (it->second.use_count() == 1) { - it = _loadedContent.erase(it); // erase returns next iterator + auto ptr = it->second.lock(); + if (!ptr) { + it = _loadedContent.erase(it); } else { ++it; } @@ -27,13 +21,19 @@ void ContentRegistry::ClearStaleContent(bool force) { void ContentRegistry::LoadAllContent() { for (auto&& [key, value] : _loadedContent) { - value->LoadContent(); + auto shared = value.lock(); + if (shared) { + shared->LoadContent(); + } } } void ContentRegistry::DestroyAllContent() { for (auto&& [key, value] : _loadedContent) { - value->UnloadContent(); + auto shared = value.lock(); + if (shared) { + shared->UnloadContent(); + } } _loadedContent.clear(); } diff --git a/src/engine/src/Content/Image.cpp b/src/engine/src/Content/Image.cpp index b111cd9..13db810 100644 --- a/src/engine/src/Content/Image.cpp +++ b/src/engine/src/Content/Image.cpp @@ -11,6 +11,10 @@ SDL_Surface *loadPNG(const char *filename, void **dataToFree) { // Read data int width, height, bytesPerPixel; void *data = stbi_load(filename, &width, &height, &bytesPerPixel, 0); + if (!data) { + sgLogError("Couldn't open image file for loading, %s", filename); + return nullptr; + } // Calculate pitch int pitch; @@ -58,7 +62,6 @@ void Image::Load() { switch (_imageType) { case ImageType::Default: { void *pngData = nullptr; - sgLogError("%s", _filePath.c_str()); SDL_Surface *s = loadPNG(_filePath.c_str(), &pngData); if (!s) { sgLogError("Could not load PNG properly, content not fully loaded"); @@ -71,6 +74,7 @@ void Image::Load() { } case ImageType::Surface: { _image = graphics->CreateTextureFromSurface(_surface); + _surface = nullptr; break; } case ImageType::RenderTarget: { diff --git a/src/engine/src/Content/Sfx.cpp b/src/engine/src/Content/Sfx.cpp index a170f45..7f0329d 100644 --- a/src/engine/src/Content/Sfx.cpp +++ b/src/engine/src/Content/Sfx.cpp @@ -13,7 +13,8 @@ sgSfx* Sfx::SgSfx() { } Sfx::~Sfx() { - UnloadContent(); + Unload(); + // UnloadContent(); } void Sfx::Load() { sgSfxLoad(_sfx); diff --git a/src/engine/src/Content/Text.cpp b/src/engine/src/Content/Text.cpp index 2d691b2..b2fabcd 100644 --- a/src/engine/src/Content/Text.cpp +++ b/src/engine/src/Content/Text.cpp @@ -1,10 +1,11 @@ #include +#include #include #include #include using namespace Supergoon; -Text::Text(std::string text, std::string fontName, int size) : Content(text), _fontSize(size), _text(text) { +Text::Text(std::string text, std::string fontName, int size) : Content(text), _text(text), _fontSize(size) { _font = ContentRegistry::CreateContent(fontName, std::move(size)); _lettersToDraw = text.length(); } @@ -23,8 +24,6 @@ void Text::Unload() { Text::~Text() { } void Text::Draw(RectangleF& src, RectangleF& dst) { - // auto src = RectangleF(); - // auto realDst = RectangleF{dst.X, dst.Y, (float)_textSize.X, (float)_textSize.Y}; _image->Draw(src, dst); } @@ -118,8 +117,8 @@ bool Text::CheckShouldWrap(int x, int wordLength, int glyphWidth, int maxX) { void Text::AddWordToLetterPoints(FT_Face fontFace, int wordEndPos, int wordLength, int penX, int penY) { int x = penX, y = penY, wordStartPos = wordEndPos - wordLength; - for (size_t i = 0; i < wordLength; i++) { - int wordI = wordStartPos + i; + for (auto i = 0; i < wordLength; i++) { + size_t wordI = wordStartPos + i; if (wordI >= _text.length()) { sgLogWarn("How is this possible?"); return; @@ -135,7 +134,7 @@ void Text::AddWordToLetterPoints(FT_Face fontFace, int wordEndPos, int wordLengt } } int Text::GetKerning(FT_Face fontFace, int i) { - if (_text.length() <= i) { + if (_text.length() <= (size_t)i) { return 0; } if (!FT_HAS_KERNING(fontFace)) { @@ -169,7 +168,7 @@ void Text::DrawLettersToTextImage(int startLoc) { if (startLoc == 0) { _image->Clear(_backgroundColor); } - for (size_t i = startLoc; i < _lettersToDraw; i++) { + for (auto i = startLoc; i < _lettersToDraw; i++) { auto letter = _text[i]; if (letter == ' ' || letter == '\n') { continue; @@ -229,6 +228,7 @@ void Text::CreateSurfaceForLetter(std::string name, FT_Face fontFace, int r, int } auto content = ContentRegistry::CreateContent(name, std::move(surface)); content->LoadContent(); + _letterImages.push_back(content); } void Text::SetTextBounds(Point bounds) { @@ -241,10 +241,12 @@ void Text::SetTextBounds(Point bounds) { MeasureText(); auto imageName = std::string(_text.substr(0, 30)) + std::to_string(_fontSize) + std::to_string(_textSize.X) + std::to_string(_textSize.Y); // If we need a new image to draw on from the size changing, then we should create new content, otherwise we should clear the current Image before redrawing. - if (imageName != _image->ContentKey()) { - _image = ContentRegistry::CreateContent(imageName, std::move(_textSize.X), std::move(_textSize.Y)); + if (_isLoaded) { + if (imageName != _image->ContentKey()) { + _image = ContentRegistry::CreateContent(imageName, std::move(_textSize.X), std::move(_textSize.Y)); + } + DrawLettersToTextImage(); } - DrawLettersToTextImage(); } void Text::SetLetterCount(int letters) { if (letters == _lettersToDraw) { @@ -271,10 +273,12 @@ void Text::SetWordWrap(bool wordWrap) { MeasureText(); auto imageName = std::string(_text.substr(0, 30)) + std::to_string(_fontSize) + std::to_string(_textSize.X) + std::to_string(_textSize.Y); // If we need a new image to draw on from the size changing, then we should create new content, otherwise we should clear the current Image before redrawing. - if (imageName != _image->ContentKey()) { - _image = ContentRegistry::CreateContent(imageName, std::move(_textSize.X), std::move(_textSize.Y)); + if (_isLoaded) { + if (imageName != _image->ContentKey()) { + _image = ContentRegistry::CreateContent(imageName, std::move(_textSize.X), std::move(_textSize.Y)); + } + DrawLettersToTextImage(); } - DrawLettersToTextImage(); } void Text::SetAlpha(int alpha) { diff --git a/src/engine/src/ECS/GameObject.cpp b/src/engine/src/ECS/GameObject.cpp index 597c6b0..8306fb2 100644 --- a/src/engine/src/ECS/GameObject.cpp +++ b/src/engine/src/ECS/GameObject.cpp @@ -21,6 +21,7 @@ int GameObject::NumberGameObjects() { auto count = 0; auto view = _registry.view(); for (auto &&i : view) { + (void)i; count++; } return count; diff --git a/src/engine/src/Events.cpp b/src/engine/src/Events.cpp index 7b0ffd8..0d57444 100644 --- a/src/engine/src/Events.cpp +++ b/src/engine/src/Events.cpp @@ -19,6 +19,7 @@ Events::Events(Game* game) { BuiltinEvents.LevelChangeEvent = RegisterEvent(); BuiltinEvents.ResetGameEvent = RegisterEvent(); BuiltinEvents.PlayBgmEvent = RegisterEvent(); + BuiltinEvents.StopBgmEvent = RegisterEvent(); BuiltinEvents.UiFadeInStart = RegisterEvent(); BuiltinEvents.UiFadeInEnd = RegisterEvent(); BuiltinEvents.UiFadeOutStart = RegisterEvent(); diff --git a/src/engine/src/Filesystem.cpp b/src/engine/src/Filesystem.cpp new file mode 100644 index 0000000..4130ae7 --- /dev/null +++ b/src/engine/src/Filesystem.cpp @@ -0,0 +1,19 @@ +#include +#include +using namespace Supergoon; + +std::ifstream Supergoon::SafeLoadFile(std::string& name) { + std::ifstream fileStream(name); + fileStream.open(name, std::ios::in | std::ios::binary); + if (!fileStream.is_open()) { + sgLogCritical("Error: File does not exist or could not be opened: %s ", name.c_str()); + } + + fileStream.seekg(0, std::ios::end); + if (fileStream.tellg() == 0) { + sgLogCritical("File is empty: %s ", name.c_str()); + } + + fileStream.seekg(0, std::ios::beg); + return fileStream; +} diff --git a/src/engine/src/Game.cpp b/src/engine/src/Game.cpp index 0f3492f..5b9a434 100644 --- a/src/engine/src/Game.cpp +++ b/src/engine/src/Game.cpp @@ -9,10 +9,13 @@ #include #ifdef imgui #include + +#include #endif #include #include +#include #include #include #include @@ -34,6 +37,9 @@ SDL_AppResult SDL_AppInit(void **appState, int, char *[]) { return SDL_APP_FAILURE; } sgInitializeDebugLogFile(); +#ifdef imgui + Widgets::InitializeWidgets(); +#endif geInitializeKeyboard(); geInitializeJoysticks(); @@ -61,33 +67,23 @@ void SDL_AppQuit(void *appState, SDL_AppResult) { game->Reset(); } -Game::Game() { - // SDL_assert(!Game::Instance()); - // _game = this; - // _gameInternal = _game; -} Game::~Game() { sgCloseDebugLogFile(); -#ifdef imgui - ImGuiIO &io = ImGui::GetIO(); - SDL_free((void *)io.IniFilename); -#endif + _graphics->CloseImGui(); } void Game::Initialize() { - char *jsonPath = NULL; - SDL_asprintf(&jsonPath, "%sassets/config.json", SDL_GetBasePath()); - sgLogWarn("Going to read from file %s", jsonPath); - std::ifstream fileStream(jsonPath); - SDL_free(jsonPath); + std::string filename = SDL_GetBasePath() + std::string("assets/config.json"); + auto fileStream = SafeLoadFile(filename); configData = json::parse(fileStream); + int windowWidth = configData["window"]["x"]; int windowHeight = configData["window"]["y"]; int worldWidth = configData["world"]["x"]; int worldHeight = configData["world"]["y"]; std::string windowTitle = configData["window"]["title"]; - _events = std::make_unique(this); - _sound = std::make_unique(); - _graphics = std::make_unique(); + _events = std::make_shared(this); + _sound = std::make_shared(); + _graphics = std::make_shared(); _graphics->CreateWindow(windowWidth, windowHeight, windowTitle); _graphics->SetWindowScaling(worldWidth, worldHeight); geClockStart(&_clock); @@ -110,7 +106,6 @@ void Game::InitializeImGui() { io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // IF using Docking Branch static auto thing = std::string(SDL_GetPrefPath("Supergoon Games", "EscapeTheFate")) + "debug.ini"; io.IniFilename = thing.c_str(); - #endif } @@ -128,9 +123,6 @@ void Game::InternalDraw() { } void Game::InternalReset() { - if (_sound) { - _sound->StopBgm(); - } Reset(); UI::Reset(); GameObject::ClearGameObjects(); diff --git a/src/engine/src/Graphics/Graphics.cpp b/src/engine/src/Graphics/Graphics.cpp index 160900e..8adca5e 100644 --- a/src/engine/src/Graphics/Graphics.cpp +++ b/src/engine/src/Graphics/Graphics.cpp @@ -64,6 +64,16 @@ void Graphics::InitializeImGui() { #endif } +void Graphics::CloseImGui() { +#ifdef imgui + ImGuiIO& io = ImGui::GetIO(); + SDL_free((void*)io.IniFilename); + ImGui_ImplSDLRenderer3_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); +#endif +} + void Graphics::DrawStart() { SDL_RenderClear(_renderer); #ifdef imgui @@ -77,14 +87,11 @@ void Graphics::DrawStart() { } void Graphics::DrawImGui() { - // Draw imgui #ifdef imgui SDL_SetRenderTarget(_renderer, NULL); ImGui::ShowDemoWindow(); ImGui::Render(); ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), _renderer); - ImGui::Render(); - ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), _renderer); #endif } @@ -162,12 +169,10 @@ SDL_Texture* Graphics::CreateTextureFromSurface(SDL_Surface* surface) { SDL_Texture* t = SDL_CreateTextureFromSurface(_renderer, surface); SDL_SetTextureBlendMode(t, SDL_BLENDMODE_BLEND); SDL_SetTextureScaleMode(t, SDL_SCALEMODE_NEAREST); - // SDL_SetTextureScaleMode(t, SDL_SCALEMODE_LINEAR); if (t == NULL) { sgLogError("Could not create texture, Error: %s", SDL_GetError()); return NULL; } - // TODO why SDL_DestroySurface(surface); return t; } diff --git a/src/engine/src/Sound.cpp b/src/engine/src/Sound.cpp index d3a881e..01cceb2 100644 --- a/src/engine/src/Sound.cpp +++ b/src/engine/src/Sound.cpp @@ -8,25 +8,52 @@ #include #include #include +#include namespace Supergoon { Sound* Sound::_instance = nullptr; +Sound::Sound() { + _instance = this; +} +Sound::~Sound() { + for (auto bgm : _bgms) { + if (bgm) { + sgBgmDelete(bgm); + } + } +} void Sound::InitializeSound() { - Events::RegisterEventHandler(Events::BuiltinEvents.PlayBgmEvent, [this](int, void* name, void*) { + Events::RegisterEventHandler(Events::BuiltinEvents.PlayBgmEvent, [this](int slot, void* name, void*) { auto nameStr = std::string((const char*)name); - LoadBgm(nameStr); - PlayBgm(); + // TODO this doesn't pass in any thing correctly, probably pass in an actual object + LoadBgm(nameStr, 1, -1, slot); + PlayBgm(slot); SDL_free(name); }); + Events::RegisterEventHandler(Events::BuiltinEvents.StopBgmEvent, [this](int slot, void* shouldFade, void*) { + if (shouldFade) { + auto fade = (bool)shouldFade; + if (fade) { + StopBgmFadeout(slot, 0.25); + return; + } + } + StopBgm(slot); + }); for (size_t i = 0; i < _totalSfxStreams; i++) { auto stream = sgStreamNew(); _sfxStreams.push_back(std::unique_ptr(stream)); _usableSfxStreams.push(stream); } + _bgms.resize(_bgmSlots); + _tweens.resize(_bgmSlots); + _playingBgmVolume.resize(_bgmSlots); + _bgmOriginalVolume.resize(_bgmSlots); } -bool Sound::LoadBgm(std::string filename, float volume, int loops) { +bool Sound::LoadBgm(std::string filename, float volume, int loops, int slot) { char* fullPath = NULL; + auto& _bgm = _bgms[slot]; SDL_asprintf(&fullPath, "%sassets/bgm/%s%s", SDL_GetBasePath(), filename.c_str(), ".ogg"); if (_bgm && !strcmp(_bgm->Filename, fullPath)) { SDL_free(fullPath); @@ -46,68 +73,84 @@ bool Sound::LoadBgm(std::string filename, float volume, int loops) { bgm->Volume = volume * _globalBgmVolume; sgBgmLoad(bgm); _bgm = bgm; - _bgmOriginalVolume = _playingBgmVolume = volume; + _bgmOriginalVolume[slot] = volume; + _playingBgmVolume[slot] = bgm->Volume; return true; } -void Sound::PlayBgm() { +void Sound::PlayBgm(int slot) { + auto& _bgm = _bgms[slot]; if (!_bgm || !_bgm->CanPlay) { return; } - SetPlayingBgmVolume(_bgmOriginalVolume); + SetPlayingBgmVolume(_bgmOriginalVolume[slot], slot); sgBgmPlay(_bgm); } -void Sound::PauseBgm() { +void Sound::PauseBgm(int slot) { + auto& _bgm = _bgms[slot]; if (!_bgm || !_bgm->IsPlaying) { return; } sgBgmPause(_bgm); } -void Sound::StopBgm() { +void Sound::StopBgm(int slot) { + auto& _bgm = _bgms[slot]; if (!_bgm) { return; } sgBgmStop(_bgm); } -void Sound::StopBgmFadeout() { +void Sound::StopBgmFadeout(int slot, float fadeTime) { + auto& _bgm = _bgms[slot]; if (!_bgm || !_bgm->IsPlaying || _fadingOut) { return; } - _bgmTween = new Tween(1.0, 0, 1, &_playingBgmVolume, Supergoon::Easings::Linear); - _bgmTween->UpdateFunc = [this]() { + if (_tweens[slot]) { + delete _tweens[slot]; + _tweens[slot] = nullptr; + } + _tweens[slot] = new Tween(_playingBgmVolume[slot], 0, fadeTime, &_playingBgmVolume[slot], Supergoon::Easings::Linear); + _tweens[slot]->UpdateFunc = [this]() { UpdatePlayingBgmVolume(); }; _fadingOut = true; } void Sound::Update() { - if (_bgm) { - sgBgmUpdate(_bgm); + for (auto bgm : _bgms) { + if (bgm) { + sgBgmUpdate(bgm); + } } + if (_usableSfxStreams.empty()) { CheckForStaleSfxStreams(); } - if (!_bgmTween) return; - if (!_bgmTween->Complete()) { - _bgmTween->Update(); - // UpdatePlayingBgmVolume(); - // SetPlayingBgmVolume(_playingBgmVolume); - } else { - StopBgm(); - SDL_free(_bgmTween); - _bgmTween = nullptr; - _fadingOut = false; + for (size_t i = 0; i < _tweens.size(); i++) { + auto& tween = _tweens[i]; + if (!tween) return; + if (!tween->Complete()) { + tween->Update(); + // UpdatePlayingBgmVolume(); + // SetPlayingBgmVolume(_playingBgmVolume); + } else { + StopBgm(); + SDL_free(_tweens[i]); + tween = nullptr; + _fadingOut = false; + } } } -void Sound::UpdatePlayingBgmVolume() { +void Sound::UpdatePlayingBgmVolume(int slot) { + auto& _bgm = _bgms[slot]; if (!_bgm) { return; } - sgBgmUpdateVolume(_bgm, _globalBgmVolume * _playingBgmVolume); + sgBgmUpdateVolume(_bgm, _globalBgmVolume * _playingBgmVolume[slot]); } void Sound::SetGlobalBgmVolume(float volume) { @@ -118,12 +161,12 @@ void Sound::SetGlobalBgmVolume(float volume) { UpdatePlayingBgmVolume(); } -void Sound::SetPlayingBgmVolume(float volume) { +void Sound::SetPlayingBgmVolume(float volume, int slot) { if (volume < 0 || volume > 1.0) { return; } - _playingBgmVolume = volume; - UpdatePlayingBgmVolume(); + _playingBgmVolume[slot] = volume; + UpdatePlayingBgmVolume(slot); } void Sound::CheckForStaleSfxStreams() { diff --git a/src/engine/src/UI/UI.cpp b/src/engine/src/UI/UI.cpp index 9835bf4..4837115 100644 --- a/src/engine/src/UI/UI.cpp +++ b/src/engine/src/UI/UI.cpp @@ -10,13 +10,13 @@ #include using namespace Supergoon; using json = nlohmann::json; -Panel* UI::UIInstance = nullptr; +std::unique_ptr UI::UIInstance = nullptr; UIObjectAnimatorBase* UI::_fadeInAnimator = nullptr; UIObjectAnimatorBase* UI::_fadeOutAnimator = nullptr; // std::vector> UI::Animators; void UI::RegisterUIEvents() { Events::RegisterEventHandler(Events::BuiltinEvents.UiDestroyObject, [](int, void* name, void*) { - auto ui = UI::UIInstance; + auto ui = UI::UIInstance.get(); auto nameString = (const char*)name; assert(nameString); ui->Children.erase(nameString); @@ -25,14 +25,12 @@ void UI::RegisterUIEvents() { Panel* UI::Initialize() { if (UIInstance) { - return UIInstance; + UIInstance.reset(); } - auto rootPanel = new Panel(); + UIInstance = std::make_unique(); auto graphics = Graphics::Instance(); - rootPanel->Bounds = RectangleF{0, 0, (float)graphics->LogicalWidth(), (float)graphics->LogicalHeight()}; - UIInstance = rootPanel; - auto name = "fadeImage"; - auto fadeImage = new UIImage(rootPanel, "Fade Panel"); + UIInstance->Bounds = RectangleF{0, 0, (float)graphics->LogicalWidth(), (float)graphics->LogicalHeight()}; + auto fadeImage = new UIImage(UIInstance.get(), "Fade Panel"); fadeImage->SetAlpha(0); auto path = std::string(SDL_GetBasePath()) + "assets/img/null.png"; fadeImage->ImagePtr = ContentRegistry::CreateContent(path); @@ -52,7 +50,7 @@ Panel* UI::Initialize() { _fadeOutAnimator = fadeOutAnimator.get(); _fadeInAnimator = fadeInAnimator.get(); // rootPanel->Children[name] = fadePanel; - return rootPanel; + return UIInstance.get(); } void UI::Update() { @@ -66,12 +64,6 @@ void UI::Draw() { UIInstance->Draw(); } } -void UI::Reset() { - if (UIInstance) { - delete UIInstance; - UIInstance = nullptr; - } -} void UI::LoadUIFromFile(std::string filename, Panel* parentPanel) { std::string jsonPath = std::string(SDL_GetBasePath()) + "assets/ui/" + filename + ".json"; diff --git a/src/engine/src/UI/UIText.cpp b/src/engine/src/UI/UIText.cpp index b1deb2d..6644e86 100644 --- a/src/engine/src/UI/UIText.cpp +++ b/src/engine/src/UI/UIText.cpp @@ -2,6 +2,9 @@ #include using namespace Supergoon; +// TODO, not sure why this is needed +static const int _centerYOffsetCorrection = 2; + UIText::UIText(Panel* parent, std::string text, std::string uiName) : UIObject(parent), DisplayText(text) { auto uiname = uiName.empty() ? uiName : text; parent->Children[uiName] = std::shared_ptr(this); @@ -10,14 +13,12 @@ UIText::UIText(Panel* parent, std::string text, std::string uiName) : UIObject(p // Start the bounds to be the size of textptr, for ease of use. Bounds.W = TextPtr->Size().X; Bounds.H = TextPtr->Size().Y; - // TextBounds = TextPtr->TextBounds(); - currentLetters = TextPtr->_lettersToDraw; + // TextPtr->SetTextBounds({(int)Bounds.W, (int)Bounds.H}); + _currentLetters = TextPtr->_lettersToDraw; WordWrap = TextPtr->_wordWrap; } void UIText::Draw() { - // TODO this probably shouldn't be here. - TextPtr->LoadContent(); // We always want to draw the full text, if possible, otherwise we should cut off. // Src rect should either be the full text, or the size of the text that we decide. // Dst rect should always be the size of the src rect. @@ -25,21 +26,20 @@ void UIText::Draw() { } void UIText::OnDirty() { - TextPtr->LoadContent(); + // TextPtr->LoadContent(); auto parentBoundsX = Parent ? Parent->Bounds.X : 0; auto parentBoundsY = Parent ? Parent->Bounds.Y : 0; Bounds.X = Offset.X + parentBoundsX; Bounds.Y = Offset.Y + parentBoundsY; // Figure out the new src rect. // If we don't have text bounds, then we should just use the bounds x/y and size of text - TextPtr->SetAlpha(EffectiveAlpha()); + // if loaded do these things TextPtr->SetTextBounds({(int)Bounds.W, (int)Bounds.H}); - TextPtr->SetLetterCount(currentLetters); TextPtr->SetWordWrap(WordWrap); - // If we should center, adjust our X and Y accordingly. - // if (CenterText) { - // } - + // Load before getting the bounds if this is a new one + TextPtr->LoadContent(); + TextPtr->SetLetterCount(_currentLetters); + TextPtr->SetAlpha(EffectiveAlpha()); // If our bounds are set to 0, then we should use the full size. if (Bounds.W == 0 && Bounds.H == 0) { TextSrcRect = RectangleF{0, 0, (float)TextPtr->Size().X, (float)TextPtr->Size().Y}; @@ -49,11 +49,15 @@ void UIText::OnDirty() { auto height = Bounds.H ? std::min((int)Bounds.H, TextPtr->Size().Y) : TextPtr->Size().Y; TextSrcRect = RectangleF{0, 0, (float)width, (float)height}; } - auto x = Bounds.X; - if (CenterText) { - x = Bounds.X + ((Bounds.W / 2) - (TextPtr->Size().X / 2)); + auto x = (int)Bounds.X; + auto y = (int)Bounds.Y; + if (CenterTextX) { + x = (int)(Bounds.X + ((Bounds.W / 2) - (TextPtr->Size().X / 2))); + } + if (_centerTextY) { + y = (int)(Bounds.Y + ((Bounds.H / 2) - (TextPtr->Size().Y / 2) - _centerYOffsetCorrection)); } - TextDrawRect = RectangleF{x, Bounds.Y, TextSrcRect.W, TextSrcRect.H}; + TextDrawRect = RectangleF{(float)x, (float)y, TextSrcRect.W, TextSrcRect.H}; } void UIText::UpdateText(std::string text) { @@ -61,13 +65,6 @@ void UIText::UpdateText(std::string text) { return; } TextPtr = ContentRegistry::CreateContent(text, "commodore", 16); - TextPtr->LoadContent(); - // Bounds.W = TextPtr->Size().X; - // Bounds.H = TextPtr->Size().Y; - // Bounds.W = Bounds - // Bounds.H = TextPtr->Size().Y; - // create new text? - // TextBounds = TextPtr->TextBounds(); - currentLetters = TextPtr->_lettersToDraw; - WordWrap = TextPtr->_wordWrap; + _currentLetters = TextPtr->_lettersToDraw; + Dirty = true; } diff --git a/src/engine/src/Widgets/ConsoleWidget.cpp b/src/engine/src/Widgets/ConsoleWidget.cpp new file mode 100644 index 0000000..0fa8822 --- /dev/null +++ b/src/engine/src/Widgets/ConsoleWidget.cpp @@ -0,0 +1,57 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +using namespace Supergoon; + +static const int MAX_MESSAGES = 999; +static bool scrollToBottom = false; +std::deque> consoleMessages; + +static void engineLogFunc(const char* time, const char* message, int logLevel) { + if (consoleMessages.size() >= MAX_MESSAGES) { + consoleMessages.pop_front(); + } + consoleMessages.push_back({logLevel, time + std::string(" ") + std::string(message)}); + scrollToBottom = true; +} + +void ConsoleWidget::ShowConsoleWidget() { + auto window_flags = Widgets::GetDefaultWindowFlags(); + bool p_open; + if (!ImGui::Begin("Console", &p_open, window_flags)) { + // Early out if the window is collapsed, as an optimization. + ImGui::End(); + return; + } + for (auto message : consoleMessages) { + Color color = {255, 255, 255, 255}; + std::string typeText = "Info - "; + if (message.first == Log_LWarn) { + color.B = 0; + typeText = "Warning - "; + } else if (message.first == Log_LError) { + typeText = "Error - "; + color.G = 0; + color.B = 0; + } + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(color.R, color.G, color.B, 255)); + ImGui::Text("%s %s", typeText.c_str(), message.second.c_str()); + ImGui::PopStyleColor(); + } + if (scrollToBottom) { + ImGui::SetScrollHereY(1.0); + } + scrollToBottom = false; + + ImGui::End(); +} +void ConsoleWidget::InitializeConsoleWidget() { + sgSetDebugFunction(engineLogFunc); +} diff --git a/src/engine/src/Widgets/ContentWidget.cpp b/src/engine/src/Widgets/ContentWidget.cpp index fe33c91..4917da8 100644 --- a/src/engine/src/Widgets/ContentWidget.cpp +++ b/src/engine/src/Widgets/ContentWidget.cpp @@ -17,13 +17,16 @@ void ContentWidget::ShowContentDebugWindow() { return; } if (ImGui::Button("Clear StaleContent")) { - ContentRegistry::ClearStaleContent(true); + ContentRegistry::ClearStaleContent(); } ImGui::Text("Number of loaded content is %zu", ContentRegistry::_loadedContent.size()); // Create a temporary vector to hold all the content and sort it properly, for display purposes.. std::unordered_map>> typedContents; for (const auto& [key, value] : ContentRegistry::_loadedContent) { - typedContents[value->Type()].push_back(value); + auto content = value.lock(); + if (content) { + typedContents[content->Type()].push_back(value); + } } if (ImGui::TreeNode("Contents")) { for (const auto& [typeName, vectorOfValues] : typedContents) { diff --git a/src/engine/src/Widgets/Global.cpp b/src/engine/src/Widgets/Global.cpp index 36d9d25..6a717f6 100644 --- a/src/engine/src/Widgets/Global.cpp +++ b/src/engine/src/Widgets/Global.cpp @@ -17,10 +17,7 @@ void GlobalWidget::ShowGlobalDebugWindow() { return; } if (ImGui::Button("Reset##globalreset")) { - for (size_t i = 0; i < 15; i++) { - Events::PushEvent(Events::BuiltinEvents.ResetGameEvent, 0); - /* code */ - } + Events::PushEvent(Events::BuiltinEvents.ResetGameEvent, 0); } // TODO this is very ineffieicnt, yes. ImGui::Text("Total number of gameobjects %d", GameObject::NumberGameObjects()); diff --git a/src/engine/src/Widgets/SoundWidget.cpp b/src/engine/src/Widgets/SoundWidget.cpp index 66d1cc4..6367624 100644 --- a/src/engine/src/Widgets/SoundWidget.cpp +++ b/src/engine/src/Widgets/SoundWidget.cpp @@ -133,7 +133,7 @@ void SoundWidget::ShowSoundDebugWindow() { if (ImGui::Button("Stop Fadeout")) { sound->StopBgmFadeout(); } - if (ImGui::SliderFloat("Bgm Playing Volume", &Sound::Instance()->_playingBgmVolume, 0, 1.0)) { + if (ImGui::SliderFloat("Bgm Playing Volume", &Sound::Instance()->_playingBgmVolume[item_current], 0, 1.0)) { Sound::Instance()->UpdatePlayingBgmVolume(); } ImGui::SameLine(); diff --git a/src/engine/src/Widgets/UIWidget.cpp b/src/engine/src/Widgets/UIWidget.cpp index bbd7219..5eaf6eb 100644 --- a/src/engine/src/Widgets/UIWidget.cpp +++ b/src/engine/src/Widgets/UIWidget.cpp @@ -1,7 +1,7 @@ #include -#include #include +#include #include #include #include @@ -67,6 +67,7 @@ void UIWidget::DrawPanel(Panel *panel, std::string panelName) { std::string childYBounds = "TextBoundsY##" + key; std::string childWordWrapLabel = "WordWrap##" + key; std::string childCenterLabel = "Center##" + key; + std::string childCenterLabelY = "CenterY##" + key; std::string childLettersToDraw = "Letters To Draw##" + key; std::string childDebugBoxCheckbox = "DebugBox##" + key; if (ImGui::TreeNode((key + "- text").c_str())) { @@ -86,17 +87,20 @@ void UIWidget::DrawPanel(Panel *panel, std::string panelName) { value->Dirty = true; }; ImGui::SameLine(); - if (ImGui::Checkbox(childCenterLabel.c_str(), &textUIObject->CenterText)) { + if (ImGui::Checkbox(childCenterLabel.c_str(), &textUIObject->CenterTextX)) { + value->Dirty = true; + }; + ImGui::SameLine(); + if (ImGui::Checkbox(childCenterLabelY.c_str(), &textUIObject->_centerTextY)) { value->Dirty = true; }; ImGui::SameLine(); ImGui::Checkbox(childDebugBoxCheckbox.c_str(), &shouldDrawDebugBox); - if(shouldDrawDebugBox) { + if (shouldDrawDebugBox) { Graphics::Instance()->DrawRect(textUIObject->Bounds, Color(255, 0, 0, 255)); - } - if (ImGui::DragInt(childLettersToDraw.c_str(), &textUIObject->currentLetters, 1, 0, textUIObject->TextPtr->_text.length())) { + if (ImGui::DragInt(childLettersToDraw.c_str(), &textUIObject->_currentLetters, 1, 0, textUIObject->TextPtr->_text.length())) { value->Dirty = true; } ImGui::TreePop(); @@ -109,7 +113,7 @@ void UIWidget::DrawPanel(Panel *panel, std::string panelName) { } void UIWidget::ShowUiDebugWindow() { - auto rootPanel = UI::UIInstance; + auto rootPanel = UI::UIInstance.get(); if (!rootPanel) { return; } diff --git a/src/engine/src/Widgets/Widgets.cpp b/src/engine/src/Widgets/Widgets.cpp index 6036fe2..dc6cde6 100644 --- a/src/engine/src/Widgets/Widgets.cpp +++ b/src/engine/src/Widgets/Widgets.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -15,6 +16,7 @@ void Widgets::ShowWidgets(Game*) { UIWidget::ShowUiDebugWindow(); LevelWidget::ShowLevelWidget(); GlobalWidget::ShowGlobalDebugWindow(); + ConsoleWidget::ShowConsoleWidget(); } int Widgets::GetDefaultWindowFlags() { // static bool p_open = true; @@ -45,5 +47,8 @@ int Widgets::GetDefaultWindowFlags() { // if (no_close) p_open = NULL; // Don't pass our bool* to Begin return window_flags; } +void Widgets::InitializeWidgets() { + ConsoleWidget::InitializeConsoleWidget(); +} } // namespace Supergoon diff --git a/src/engine/src/World/Level.cpp b/src/engine/src/World/Level.cpp index 7138720..44af0c5 100644 --- a/src/engine/src/World/Level.cpp +++ b/src/engine/src/World/Level.cpp @@ -110,7 +110,7 @@ Level::~Level() { _gameObjects.clear(); } -static std::string getBasePathForTiled() { +std::string Level::GetBasePathForTiled() { return std::string(SDL_GetBasePath()) + "assets/tiled/"; } @@ -122,16 +122,12 @@ void Level::LoadSurfaces() { for (auto &tileset : _mapData->Tilesets) { if (tileset.Type == TilesetType::Image) { for (auto &tile : tileset.Tiles) { - char *fullPath = NULL; - SDL_asprintf(&fullPath, "%s%s", getBasePathForTiled().c_str(), tile.Image.c_str()); - ContentRegistry::CreateContent(fullPath); - SDL_free(fullPath); + auto fullPath = GetBasePathForTiled() + tile.Image; + _backgroundTilesetImages.push_back(ContentRegistry::CreateContent(fullPath)); } } else { - char *fullPath = NULL; - SDL_asprintf(&fullPath, "%s%s", getBasePathForTiled().c_str(), tileset.Image.c_str()); - auto i = ContentRegistry::CreateContent(fullPath); - SDL_free(fullPath); + auto fullPath = GetBasePathForTiled() + tileset.Image; + _backgroundTilesetImages.push_back(ContentRegistry::CreateContent(fullPath)); } } ContentRegistry::LoadAllContent(); @@ -141,11 +137,12 @@ Image *Level::GetSurfaceForGid(int gid, const TiledMap::Tileset *tileset) { if (tileset->Type == TilesetType::Image) { for (auto &tile : tileset->Tiles) { if (tile.Id + tileset->FirstGid == gid) { - return ContentRegistry::CreateContent(getBasePathForTiled() + tile.Image).get(); + return ContentRegistry::GetContent(GetBasePathForTiled() + tile.Image).get(); } } } else { - return ContentRegistry::CreateContent(getBasePathForTiled() + tileset->Image).get(); + auto name = GetBasePathForTiled() + tileset->Image; + return ContentRegistry::GetContent(GetBasePathForTiled() + tileset->Image).get(); } sgLogError("Could not find loaded surface for gid %ud\n", gid); return nullptr; @@ -156,10 +153,14 @@ void Level::LoadNewLevelFade(std::string level) { Events::PushEvent(Events::BuiltinEvents.LevelChangeEvent, false, (void *)strdup(level.c_str())); UI::FadeIn(); }); + static bool fadebool = true; + Events::PushEvent(Events::BuiltinEvents.StopBgmEvent, false, (void *)&fadebool); UI::FadeOut(); } void Level::LoadNewLevel(std::string level) { + // auto levelptr = new Level(level.c_str()); + // _currentLevel = std::unique_ptr(levelptr); _currentLevel = std::make_unique(level.c_str()); if (LoadFunc) { LoadFunc(); @@ -217,6 +218,7 @@ void Level::CreateBackgroundImage() { auto tiledMapTileset = _mapData->GetGidTiledMapTileset(tileGid); auto tileset = _mapData->GetTiledMapTilesetTileset(tiledMapTileset); auto tileSurface = GetSurfaceForGid(tileGid, tileset); + assert(tileSurface); auto sourceRect = _mapData->GetGidSourceRect(tileGid); auto dstX = x * _mapData->TileWidth; auto dstY = y * _mapData->TileHeight; diff --git a/src/game/BlackjackGame.cpp b/src/game/BlackjackGame.cpp index 9479abc..bf18f65 100644 --- a/src/game/BlackjackGame.cpp +++ b/src/game/BlackjackGame.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,8 @@ #include #include #include +#include +#include using json = nlohmann::json; extern json configData; @@ -22,23 +25,27 @@ std::unordered_map("display"); - auto ui = UI::UIInstance; - auto textPanel = std::dynamic_pointer_cast(UI::UIInstance->Children["textTesting"]); + // auto ui = UI::UIInstance.get(); + auto textPanel = std::dynamic_pointer_cast(UI::UIInstance->Children["textTestingscreen"]); assert(textPanel); if (display) { - auto textBox = (UIText *)textPanel->Children["textman"].get(); + auto textBox = (UIText *)textPanel->Children["textmanscreen"].get(); assert(textBox); textPanel->SetVisible(true); + textPanel->SetAlpha(255); textBox->UpdateText(*display); // TODO this should be different. for (auto &&animator : textPanel->Animators) { @@ -48,114 +55,22 @@ static void loadLevel() { } else { textPanel->SetVisible(false); } - ContentRegistry::LoadAllContent(); inGame = true; } class BlackjackGame : public Game { public: + ~BlackjackGame() = default; void Start() override; void Update() override; void Draw() override; void Reset() override; }; -// TODO this shouldn't be here -static void setupUINameChangeBox() { - auto ui = UI::UIInstance; - auto textPanel = new Panel(ui, "textTesting"); - textPanel->Offset = {145, 15}; - auto text = new UIText(textPanel, "Hello world!", "textman"); - text->Offset = {0, 13}; - // textPanel->Children["textman"] = text; - // ui->Children["textTesting"] = textPanel; - // Test creating the uitextbox - // First, lets load in the picture for uiimage so that we can draw from it to the new one - auto path = std::string(SDL_GetBasePath()) + "assets/img/uibase.png"; - auto uiImageFull = ContentRegistry::CreateContent(path); - uiImageFull->LoadContent(); - // Create ui text image of the right size as a render target - float fullSizeX = 200; - float fullSizeY = 48; - text->Bounds.W = fullSizeX; - text->Bounds.H = fullSizeY; - text->CenterText = true; - // text->SetCenter(true); - auto textBoxImage = ContentRegistry::CreateContent("uitextbox", (int)fullSizeX, (int)fullSizeY); - textBoxImage->LoadContent(); - // Set the background - textBoxImage->Clear({80, 0, 80, 220}); - float sizeX = 8; - float sizeY = 9; - textBoxImage->SetAlpha(200); - // Draw the corners - // tl - auto srcRect = RectangleF{0, 0, sizeX, sizeY}; - auto dstRect = RectangleF{0, 0, sizeX, sizeY}; - textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); - // tr - srcRect = RectangleF{uiImageFull->Width() - sizeX, 0, sizeX, sizeY}; - dstRect = RectangleF{fullSizeX - sizeX, 0, sizeX, sizeY}; - textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); - // bl - srcRect = RectangleF{0, uiImageFull->Height() - sizeY, sizeX, sizeY}; - dstRect = RectangleF{0, fullSizeY - sizeY, sizeX, sizeY}; - textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); - // br - srcRect = RectangleF{uiImageFull->Width() - sizeX, uiImageFull->Height() - sizeY, sizeX, sizeY}; - dstRect = RectangleF{fullSizeX - sizeX, fullSizeY - sizeY, sizeX, sizeY}; - textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); - // draw the bars - int length = fullSizeX - (sizeX); - int height = fullSizeY - (sizeY); - // top - srcRect = RectangleF{1 + sizeX, 0, 1, sizeY}; - for (size_t i = sizeX; i < length; i++) { - dstRect = RectangleF{(float)i, 0, 1, sizeY}; - textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); - } - // bottom - for (size_t i = sizeX; i < length; i++) { - dstRect = RectangleF{(float)i, fullSizeY - sizeY + 4, 1, sizeY}; - textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); - } - // left - srcRect = RectangleF({0, sizeY + 1, sizeX, 1}); - for (size_t i = sizeY; i < height; i++) { - dstRect = RectangleF{0, (float)i, sizeX, 1}; - textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); - } - // right - for (size_t i = sizeY; i < height; i++) { - dstRect = RectangleF{fullSizeX - sizeX + 3, (float)i, sizeX, 1}; - textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); - } - - // Add this image to the ui panel - auto textBoxUIImage = new UIImage(textPanel, "textBoxImage"); - textBoxUIImage->ImagePtr = textBoxImage; - textBoxUIImage->SetVisible(true); - textBoxUIImage->Bounds = RectangleF{0, 0, (float)textBoxImage->Width(), (float)textBoxImage->Height()}; - textBoxUIImage->Offset.X = 0; - textBoxUIImage->Offset.Y = 0; - // textPanel->Children["uitextbox"] = textBoxUIImage; - // textPanel->Visible = true; - // Setup the animators - auto animator = std::make_shared("levelDisplayAnimator"); - auto waitTween = new Tween(1.0f); - auto fadeOutTween = new Tween(255, 0, 0.5, textPanel->AlphaHandle(), Supergoon::Easings::Linear); - fadeOutTween->EndFunc = [textPanel]() { - textPanel->SetVisible(false); - textPanel->SetAlpha(255); - }; - animator->AddUIObjectTween(waitTween, textPanel); - animator->AddUIObjectTween(fadeOutTween, textPanel); - textPanel->Animators.push_back(animator); -} static void playLogos() { UI::LoadUIFromFile("logos"); - auto ui = UI::UIInstance; + auto ui = UI::UIInstance.get(); auto thing = (UIImage *)ui->Children["logoImage"].get(); auto thing2 = (UIImage *)ui->Children["logoImage2"].get(); auto animator = new UIObjectAnimatorBase("logo"); @@ -168,7 +83,9 @@ static void playLogos() { animator2->Play(); }; fadeOutTween2->EndFunc = []() { - setupUINameChangeBox(); + CreateUITextbox("screen", Point{145, 15}, Point{200, 48}, true); + InitializeTextInteractionUI(); + // setupUINameChangeBox(); Events::PushEvent(Events::BuiltinEvents.LevelChangeEvent, 0, (void *)strdup("debugTown")); Events::PushEvent(Events::BuiltinEvents.UiDestroyObject, 0, (void *)"logoImage"); Events::PushEvent(Events::BuiltinEvents.UiDestroyObject, 0, (void *)"logoImage2"); @@ -199,8 +116,8 @@ void BlackjackGame::Start() { if (!skipLogos) { playLogos(); } else { - // UI::LoadUIFromFile("logos"); - setupUINameChangeBox(); + CreateUITextbox("screen", Point{145, 15}, Point{200, 48}, true); + InitializeTextInteractionUI(); Events::PushEvent(Events::BuiltinEvents.LevelChangeEvent, 0, (void *)strdup("debugTown")); } } @@ -209,6 +126,7 @@ void BlackjackGame::Update() { if (inGame) { PlayerInput(); UpdateAnimationComponents(); + UpdateTextInteractions(); UpdateCamera(); } UI::Update(); @@ -219,6 +137,7 @@ void BlackjackGame::Draw() { Level::Draw(); DrawAnimationComponents(); DrawImages(); + DrawTextInteractionDisplay(); } UI::Draw(); #ifdef imgui @@ -234,6 +153,12 @@ void BlackjackGame::Draw() { if (PlayerWidget::ShowPlayerExitDebugBox) { DrawDebugBoxesPlayerExit(); } + if (PlayerWidget::ShowPlayerInteractionDebugBox) { + DrawDebugBoxesPlayerInteractionBox(); + } + if (PlayerWidget::ShowInteractionDebugBox) { + DrawDebugBoxesTextInteractionBox(); + } #endif } diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index e7ec96e..1cf6fdb 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -6,6 +6,7 @@ set(DEBUG_FILES) if(imgui) list(APPEND DEBUG_FILES Debug/PlayerCollider.cpp + Systems/DebugDrawSystem.cpp ) endif(imgui) @@ -23,11 +24,13 @@ add_executable(${EXECUTABLE_TARGET_NAME} BlackjackGame.cpp Entities/PlayerStart.cpp Entities/PlayerExit.cpp + Entities/TextInteraction.cpp Systems/PlayerSystem.cpp Systems/ImageSystem.cpp Systems/AsepriteSystem.cpp Systems/CameraSystem.cpp - Systems/DebugDrawSystem.cpp + Systems/TextInteractionSystem.cpp + Utilities/Utilities.cpp ${DEBUG_FILES} ${ICON_PATH} ) diff --git a/src/game/Components/PlayerComponent.hpp b/src/game/Components/PlayerComponent.hpp index b717a5b..c9bcdcd 100644 --- a/src/game/Components/PlayerComponent.hpp +++ b/src/game/Components/PlayerComponent.hpp @@ -8,4 +8,5 @@ struct PlayerComponent { RectangleF Body; Directions Direction; }; + } // namespace Supergoon diff --git a/src/game/Components/PlayerInteractionComponent.hpp b/src/game/Components/PlayerInteractionComponent.hpp new file mode 100644 index 0000000..b1663dd --- /dev/null +++ b/src/game/Components/PlayerInteractionComponent.hpp @@ -0,0 +1,11 @@ +#pragma once +#include +#include +#include +namespace Supergoon { +struct PlayerInteractionComponent { + RectangleF InteractionRect; + std::shared_ptr InteractionImage; + bool ImageShowing; +}; +} // namespace Supergoon diff --git a/src/game/Components/TextInteractionComponent.hpp b/src/game/Components/TextInteractionComponent.hpp new file mode 100644 index 0000000..deaa0fb --- /dev/null +++ b/src/game/Components/TextInteractionComponent.hpp @@ -0,0 +1,15 @@ +#pragma once +// #include +#include +#include +#include +namespace Supergoon { +class Text; +struct TextInteractionComponent { + RectangleF InteractionRect; + bool InteractionPressed; + std::string DisplayText; + std::shared_ptr TextPtr; +}; + +} // namespace Supergoon diff --git a/src/game/Debug/PlayerCollider.cpp b/src/game/Debug/PlayerCollider.cpp index 2c3b625..fdc43e6 100644 --- a/src/game/Debug/PlayerCollider.cpp +++ b/src/game/Debug/PlayerCollider.cpp @@ -1,10 +1,13 @@ #include +#include #include #include using namespace Supergoon; bool PlayerWidget::ShowPlayerColliderDebugBox = false; bool PlayerWidget::ShowPlayerExitDebugBox = false; +bool PlayerWidget::ShowPlayerInteractionDebugBox = false; +bool PlayerWidget::ShowInteractionDebugBox = false; void PlayerWidget::ShowPlayerColliderWindow() { ImGuiWindowFlags window_flags = Widgets::GetDefaultWindowFlags(); bool p_open; @@ -16,6 +19,8 @@ void PlayerWidget::ShowPlayerColliderWindow() { } ImGui::Checkbox("Show Player Debug Colliders", &ShowPlayerColliderDebugBox); ImGui::Checkbox("Show Player Exit Colliders", &ShowPlayerExitDebugBox); + ImGui::Checkbox("Show Player Interaction Colliders", &ShowPlayerInteractionDebugBox); + ImGui::Checkbox("Show Interaction Colliders", &ShowInteractionDebugBox); auto playerGo = GameObject::GetGameObjectWithComponents(); if (!playerGo.has_value()) { ImGui::End(); @@ -23,6 +28,9 @@ void PlayerWidget::ShowPlayerColliderWindow() { } auto& playerComponent = playerGo->GetComponent(); auto& locationComponent = playerGo->GetComponent(); + auto& playerInteractionComponent = playerGo->GetComponent(); + auto gamestateComponent = GameObject::FindComponent(); + assert(gamestateComponent); if (ImGui::CollapsingHeader("Player")) { ImGui::DragFloat("Location X", &locationComponent.Location.X, 0.1f); ImGui::DragFloat("Location Y", &locationComponent.Location.Y, 0.1f); @@ -30,6 +38,13 @@ void PlayerWidget::ShowPlayerColliderWindow() { ImGui::DragFloat("BoxOffset Y", &playerComponent.Body.Y, 0.1f); ImGui::DragFloat("BoxSize X", &playerComponent.Body.W, 0.1f); ImGui::DragFloat("BoxSize Y", &playerComponent.Body.H, 0.1f); + ImGui::DragFloat("Interaction X", &playerInteractionComponent.InteractionRect.X, 0.1f); + ImGui::DragFloat("Interaction Y", &playerInteractionComponent.InteractionRect.Y, 0.1f); + ImGui::DragFloat("Interaction W", &playerInteractionComponent.InteractionRect.W, 0.1f); + ImGui::DragFloat("Interaction H", &playerInteractionComponent.InteractionRect.H, 0.1f); + ImGui::BeginDisabled(true); + ImGui::Checkbox("Interacting", &gamestateComponent->Interacting); + ImGui::EndDisabled(); } ImGui::End(); } diff --git a/src/game/Debug/PlayerCollider.hpp b/src/game/Debug/PlayerCollider.hpp index e6dc90d..eac4dab 100644 --- a/src/game/Debug/PlayerCollider.hpp +++ b/src/game/Debug/PlayerCollider.hpp @@ -5,5 +5,7 @@ class PlayerWidget { static void ShowPlayerColliderWindow(); static bool ShowPlayerColliderDebugBox; static bool ShowPlayerExitDebugBox; + static bool ShowPlayerInteractionDebugBox; + static bool ShowInteractionDebugBox; }; } // namespace Supergoon diff --git a/src/game/Entities/TextInteraction.cpp b/src/game/Entities/TextInteraction.cpp new file mode 100644 index 0000000..b0c9922 --- /dev/null +++ b/src/game/Entities/TextInteraction.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include +using namespace Supergoon; +GameObject* Supergoon::NewTextInteraction(TiledMap::TiledObject& obj) { + auto go = new GameObject(); + auto textInteraction = TextInteractionComponent(); + textInteraction.InteractionRect.X = obj.X; + textInteraction.InteractionRect.Y = obj.Y; + textInteraction.InteractionRect.W = obj.Width; + textInteraction.InteractionRect.H = obj.Height; + // textInteraction.TextPtr = nullptr; + for (auto&& prop : obj.Properties) { + if (prop.Name == "text") { + textInteraction.DisplayText = std::get(prop.Value); + } + } + go->AddComponent(textInteraction); + return go; +} diff --git a/src/game/Entities/TextInteraction.hpp b/src/game/Entities/TextInteraction.hpp new file mode 100644 index 0000000..1a7d8e5 --- /dev/null +++ b/src/game/Entities/TextInteraction.hpp @@ -0,0 +1,8 @@ + +#pragma once +#include +namespace Supergoon { +class GameObject; +GameObject* NewTextInteraction(TiledMap::TiledObject&); + +} // namespace Supergoon diff --git a/src/game/Systems/DebugDrawSystem.cpp b/src/game/Systems/DebugDrawSystem.cpp index 68a3f39..76b0e18 100644 --- a/src/game/Systems/DebugDrawSystem.cpp +++ b/src/game/Systems/DebugDrawSystem.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include using namespace Supergoon; @@ -38,6 +40,27 @@ static void drawPlayerExitDebugBoxes(GameObject, PlayerExitComponent& pe) { auto graphics = Graphics::Instance(); graphics->DrawRect(dst, Color{0, 255, 0, 255}); } +static void drawPlayerInteractionDebugBoxes(GameObject, PlayerInteractionComponent& player) { + auto c = GameObject::GetGameObjectWithComponents(); + auto& cc = c->GetComponent(); + + float adjustedX = player.InteractionRect.X - cc.Box.X; + float adjustedY = player.InteractionRect.Y - cc.Box.Y; + auto dst = RectangleF{adjustedX, adjustedY, (float)player.InteractionRect.W, (float)player.InteractionRect.H}; + auto graphics = Graphics::Instance(); + graphics->DrawRect(dst, Color{0, 255, 0, 255}); +} + +static void drawTextInteractionDebugBoxes(GameObject, TextInteractionComponent& text) { + auto c = GameObject::GetGameObjectWithComponents(); + auto& cc = c->GetComponent(); + + float adjustedX = text.InteractionRect.X - cc.Box.X; + float adjustedY = text.InteractionRect.Y - cc.Box.Y; + auto dst = RectangleF{adjustedX, adjustedY, (float)text.InteractionRect.W, (float)text.InteractionRect.H}; + auto graphics = Graphics::Instance(); + graphics->DrawRect(dst, Color{0, 255, 255, 255}); +} void Supergoon::DrawDebugBoxesPlayer() { GameObject::ForEach(drawPlayerBodyDebugBoxes); @@ -48,3 +71,11 @@ void Supergoon::DrawDebugBoxesSolid() { void Supergoon::DrawDebugBoxesPlayerExit() { GameObject::ForEach(drawPlayerExitDebugBoxes); } + +void Supergoon::DrawDebugBoxesPlayerInteractionBox() { + GameObject::ForEach(drawPlayerInteractionDebugBoxes); +} + +void Supergoon::DrawDebugBoxesTextInteractionBox() { + GameObject::ForEach(drawTextInteractionDebugBoxes); +} diff --git a/src/game/Systems/DebugDrawSystem.hpp b/src/game/Systems/DebugDrawSystem.hpp index 7eee77c..41c10df 100644 --- a/src/game/Systems/DebugDrawSystem.hpp +++ b/src/game/Systems/DebugDrawSystem.hpp @@ -3,4 +3,6 @@ namespace Supergoon { void DrawDebugBoxesSolid(); void DrawDebugBoxesPlayer(); void DrawDebugBoxesPlayerExit(); +void DrawDebugBoxesPlayerInteractionBox(); +void DrawDebugBoxesTextInteractionBox(); } // namespace Supergoon diff --git a/src/game/Systems/PlayerSystem.cpp b/src/game/Systems/PlayerSystem.cpp index 3b9197f..460503d 100644 --- a/src/game/Systems/PlayerSystem.cpp +++ b/src/game/Systems/PlayerSystem.cpp @@ -1,15 +1,51 @@ +#include + #include #include +#include #include +#include #include #include using namespace Supergoon; +static void updateInteractionRect(PlayerComponent& player, PlayerInteractionComponent& playerInteraction, LocationComponent& location) { + auto ewWH = Point(26, 8); + auto nsWH = Point(8, 26); + switch (player.Direction) { + case Directions::East: + playerInteraction.InteractionRect.X = location.Location.X + player.Body.X + (player.Body.W / 2); + playerInteraction.InteractionRect.Y = location.Location.Y - (player.Body.Y / 2) + player.Body.H; + playerInteraction.InteractionRect.W = ewWH.X; + playerInteraction.InteractionRect.H = ewWH.Y; + break; + case Directions::West: + playerInteraction.InteractionRect.X = location.Location.X - 10; + playerInteraction.InteractionRect.Y = location.Location.Y - (player.Body.Y / 2) + player.Body.H; + playerInteraction.InteractionRect.W = ewWH.X; + playerInteraction.InteractionRect.H = ewWH.Y; + break; + case Directions::North: + playerInteraction.InteractionRect.X = location.Location.X + player.Body.X + (player.Body.W / 2) - (nsWH.X / 2); + playerInteraction.InteractionRect.Y = location.Location.Y - (player.Body.Y / 2); + playerInteraction.InteractionRect.W = nsWH.X; + playerInteraction.InteractionRect.H = nsWH.Y; + break; + case Directions::South: + playerInteraction.InteractionRect.X = location.Location.X + player.Body.X + (player.Body.W / 2) - (nsWH.X / 2); + playerInteraction.InteractionRect.Y = location.Location.Y + player.Body.H; + playerInteraction.InteractionRect.W = nsWH.X; + playerInteraction.InteractionRect.H = nsWH.Y; + break; + } +} + static void loadPlayer(GameObject, PlayerSpawnComponent& playerSpawn) { auto go = new GameObject(); auto playerLocation = LocationComponent(); auto playerComponent = PlayerComponent(); auto playerAnimation = AnimationComponent(); + auto playerInteraction = PlayerInteractionComponent(); playerAnimation.AnimationName = "player" + std::to_string(playerComponent.PlayerNum + 1); playerAnimation.Offset = Point{0, 0}; playerAnimation.AnimationSpeed = 1.0; @@ -18,9 +54,14 @@ static void loadPlayer(GameObject, PlayerSpawnComponent& playerSpawn) { playerComponent.Body = RectangleF{4, 9, 16, 22}; playerLocation.Location.X = playerSpawn.Location.X; playerLocation.Location.Y = playerSpawn.Location.Y; + updateInteractionRect(playerComponent, playerInteraction, playerLocation); + auto path = std::string(SDL_GetBasePath()) + "assets/img/interaction.png"; + playerInteraction.InteractionImage = ContentRegistry::CreateContent(path); + playerInteraction.ImageShowing = false; go->AddComponent(playerAnimation); go->AddComponent(playerLocation); go->AddComponent(playerComponent); + go->AddComponent(playerInteraction); Events::PushEvent(Events::BuiltinEvents.GameObjectAdd, true, (void*)go); } static void startPlayer(GameObject, PlayerComponent& playerComponent, AnimationComponent& animComponent) { @@ -28,6 +69,27 @@ static void startPlayer(GameObject, PlayerComponent& playerComponent, AnimationC animComponent.Animation->PlayAnimation("walk" + std::string(letter)); } +static bool interactionHandler(PlayerInteractionComponent& player, GameState& gamestate, bool interactionKeyPressed) { + bool interactionFound = false; + GameObject::ForEach([&player, interactionKeyPressed, &interactionFound](GameObject, TextInteractionComponent& text) { + if (interactionFound) { + return; + } + if (player.InteractionRect.IsOverlap(&text.InteractionRect)) { + interactionFound = true; + if (interactionKeyPressed) { + text.InteractionPressed = true; + // player.ImageShowing = false; + } else { + text.InteractionPressed = false; + // player.ImageShowing = true; + } + } + }); + player.ImageShowing = gamestate.Interacting ? false : interactionFound; + return true; +} + static void playerInput(GameObject go, PlayerComponent& player) { if (KeyDown(Supergoon::KeyboardKeys::Key_R)) { Events::PushEvent(Events::BuiltinEvents.ResetGameEvent, 0); @@ -37,6 +99,11 @@ static void playerInput(GameObject go, PlayerComponent& player) { auto& stateComponent = state->GetComponent(); assert(state.has_value()); if (stateComponent.Loading || stateComponent.EnteringBattle) { + if (KeyDown(KeyboardKeys::Key_W)) { + if (stateComponent.EnteringBattle) { + Events::PushEvent(Events::BuiltinEvents.PlayBgmEvent, 0, (void*)strdup("victory")); + } + } return; } auto vel = Vector2(); @@ -47,25 +114,27 @@ static void playerInput(GameObject go, PlayerComponent& player) { auto newDirection = player.Direction; // // Handle button presses - if (KeyDown(KeyboardKeys::Key_S)) { - vel.Y += speed; - moved = true; - newDirection = Directions::South; - } - if (KeyDown(KeyboardKeys::Key_D)) { - vel.X += speed; - moved = true; - newDirection = Directions::East; - } - if (KeyDown(KeyboardKeys::Key_W)) { - vel.Y -= speed; - moved = true; - newDirection = Directions::North; - } - if (KeyDown(KeyboardKeys::Key_A)) { - vel.X -= speed; - moved = true; - newDirection = Directions::West; + if (!stateComponent.Interacting) { + if (KeyDown(KeyboardKeys::Key_S)) { + vel.Y += speed; + moved = true; + newDirection = Directions::South; + } + if (KeyDown(KeyboardKeys::Key_D)) { + vel.X += speed; + moved = true; + newDirection = Directions::East; + } + if (KeyDown(KeyboardKeys::Key_W)) { + vel.Y -= speed; + moved = true; + newDirection = Directions::North; + } + if (KeyDown(KeyboardKeys::Key_A)) { + vel.X -= speed; + moved = true; + newDirection = Directions::West; + } } if (KeyDown(KeyboardKeys::Key_B)) { // Start battle transition. @@ -106,7 +175,6 @@ static void playerInput(GameObject go, PlayerComponent& player) { } } }); - // loc.Location += vel; if (moved) { loc.Location.X = std::round(desiredPosition.X); loc.Location.Y = std::round(desiredPosition.Y); @@ -117,6 +185,10 @@ static void playerInput(GameObject go, PlayerComponent& player) { anim.Animation->PlayAnimation("walk" + std::string(letter)); player.Direction = newDirection; } + auto& interaction = go.GetComponent(); + if (moved) { + updateInteractionRect(player, interaction, loc); + } // Did we exit? auto playerBodyRect = RectangleF{loc.Location.X + player.Body.X, loc.Location.Y + player.Body.Y, player.Body.W, player.Body.H}; @@ -140,6 +212,9 @@ static void playerInput(GameObject go, PlayerComponent& player) { if (exited) { return; } + // Check for interactions if we pressed space. + auto interactionKeyPressed = KeyJustPressed(KeyboardKeys::Key_SPACE); + interactionHandler(interaction, stateComponent, interactionKeyPressed); } static void loadPlayerEach(GameObject go, PlayerSpawnComponent& ps) { diff --git a/src/game/Systems/TextInteractionSystem.cpp b/src/game/Systems/TextInteractionSystem.cpp new file mode 100644 index 0000000..dfd0914 --- /dev/null +++ b/src/game/Systems/TextInteractionSystem.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +using namespace Supergoon; + +static GameState* gameStateComponent; +static TextInteractionComponent* currentInteractingText; +static Tween typingTween = Tween(0); +static bool isTyping = false; + +void updateTextInteractionComponents(GameObject, TextInteractionComponent& textInteractionComponent) { + // This is set when something interacts with it, ie player hits space, if it isn't being interacted with quick return; + // Also quick return if we are interacting, but the interacting text is not the one who is interacting. + // if (textInteractionComponent.InteractionPressed) { + // return; + // } + if ((currentInteractingText && currentInteractingText != &textInteractionComponent)) { + return; + } + // If we are already engaged in a interaction, then progress it. + if (gameStateComponent->Interacting) { + // If we still have some to type + if (isTyping) { + typingTween.Update(); + auto ui = UI::UIInstance.get(); + auto panelName = "textTesting" + std::string("regular"); + auto thing = (Panel*)ui->Children[panelName].get(); + thing->Dirty = true; + + // If we are complete and click + if (typingTween.Complete()) { + // If we click, we should end + isTyping = false; + Events::PushEvent(Events::BuiltinEvents.StopBgmEvent, 1); + // If we click and it is not complete, finish typing the text. + } else if (textInteractionComponent.InteractionPressed) { + auto textName = "textman" + std::string("regular"); + auto text = (UIText*)thing->Children[textName].get(); + text->SetCurrentLetters(textInteractionComponent.DisplayText.length()); + isTyping = false; + Events::PushEvent(Events::BuiltinEvents.StopBgmEvent, 1); + } + // if we are finished typing and press a button, then we should exit. + } else { + if (textInteractionComponent.InteractionPressed) { + textInteractionComponent.InteractionPressed = false; + auto ui = UI::UIInstance.get(); + auto panelName = "textTesting" + std::string("regular"); + auto thing = (Panel*)ui->Children[panelName].get(); + thing->SetVisible(false); + gameStateComponent->Interacting = false; + currentInteractingText = nullptr; + isTyping = false; + } + } + // If we aren't interacting, then we should start to + } else if (textInteractionComponent.InteractionPressed) { + textInteractionComponent.InteractionPressed = false; + currentInteractingText = &textInteractionComponent; + gameStateComponent->Interacting = true; + // update the text interaction box to say what this is. + auto ui = UI::UIInstance.get(); + auto panelName = "textTesting" + std::string("regular"); + auto thing = (Panel*)ui->Children[panelName].get(); + assert(thing); + auto textName = "textman" + std::string("regular"); + auto text = (UIText*)thing->Children[textName].get(); + // auto text = std::dynamic_pointer_cast(thing->Children[textName]); + assert(text); + // text->TextPtr = textInteractionComponent.TextPtr; + text->UpdateText(textInteractionComponent.DisplayText); + text->SetCurrentLetters(0); + isTyping = true; + typingTween = Tween(0, textInteractionComponent.DisplayText.length(), textInteractionComponent.DisplayText.length() * 0.05, text->CurrentLettersRef(), Supergoon::Easings::Linear); + Events::PushEvent(Events::BuiltinEvents.PlayBgmEvent, 1, (void*)strdup("typing")); + thing->SetVisible(true); + } +} + +void drawTextInteractionComponents(GameObject, LocationComponent& location, PlayerInteractionComponent& playerInteraction) { + if (!playerInteraction.ImageShowing) { + return; + } + auto c = GameObject::GetGameObjectWithComponents(); + auto& cc = c->GetComponent(); + + float adjustedX = location.Location.X + 14 - cc.Box.X; + float adjustedY = location.Location.Y - 4 - cc.Box.Y; + auto dst = RectangleF{adjustedX, adjustedY, (float)playerInteraction.InteractionImage->Width(), (float)playerInteraction.InteractionImage->Height()}; + auto src = RectangleF{0, 0, 0, 0}; + playerInteraction.InteractionImage->Draw(src, dst); +} + +void loadTextInteractionComponents(GameObject, TextInteractionComponent& textInteraction) { + // textInteraction.TextPtr = ContentRegistry::CreateContent(textInteraction.DisplayText, "commodore", 16); + auto ui = UI::UIInstance.get(); + auto panelName = "textTesting" + std::string("regular"); + auto thing = (Panel*)ui->Children[panelName].get(); + assert(thing); + auto textName = "textman" + std::string("regular"); + auto text = (UIText*)thing->Children[textName].get(); + textInteraction.TextPtr = text->TextPtr; + text->UpdateText(textInteraction.DisplayText); + text->OnDirty(); +} +void Supergoon::InitializeTextInteractionUI() { + auto textPanel = CreateUITextbox("regular", Point{145, 200}, Point{236, 80}, false); + textPanel->SetVisible(false); +} + +void Supergoon::UpdateTextInteractions() { + assert(GameObject::FindComponent()); + gameStateComponent = GameObject::FindComponent(); + GameObject::ForEach(updateTextInteractionComponents); +} + +void Supergoon::DrawTextInteractionDisplay() { + GameObject::ForEach(drawTextInteractionComponents); +} +void Supergoon::LoadTextInteractions() { + GameObject::ForEach(loadTextInteractionComponents); +} diff --git a/src/game/Systems/TextInteractionSystem.hpp b/src/game/Systems/TextInteractionSystem.hpp new file mode 100644 index 0000000..ca4504c --- /dev/null +++ b/src/game/Systems/TextInteractionSystem.hpp @@ -0,0 +1,7 @@ +#pragma once +namespace Supergoon { +void InitializeTextInteractionUI(); +void LoadTextInteractions(); +void UpdateTextInteractions(); +void DrawTextInteractionDisplay(); +} // namespace Supergoon diff --git a/src/game/Utilities/Utilities.cpp b/src/game/Utilities/Utilities.cpp new file mode 100644 index 0000000..f168f77 --- /dev/null +++ b/src/game/Utilities/Utilities.cpp @@ -0,0 +1,104 @@ +#include + +#include +#include +#include + +using namespace Supergoon; +// 200/70 for textbox +Panel* Supergoon::CreateUITextbox(std::string name, Point screenLoc, Point size, bool screen) { + auto ui = UI::UIInstance.get(); + auto textPanel = new Panel(ui, "textTesting" + name); + textPanel->Offset = {(float)screenLoc.X, (float)screenLoc.Y}; + auto text = new UIText(textPanel, "Hello world!", "textman" + name); + text->Offset = {8, 8}; + // Test creating the uitextbox + // First, lets load in the picture for uiimage so that we can draw from it to the new one + auto path = std::string(SDL_GetBasePath()) + "assets/img/uibase.png"; + auto uiImageFull = ContentRegistry::CreateContent(path); + uiImageFull->LoadContent(); + // Create ui text image of the right size as a render target + // float fullSizeX = 200; + // float fullSizeY = 48; + float fullSizeX = size.X; + float fullSizeY = size.Y; + text->Bounds.W = fullSizeX - (text->Offset.X * 2); + text->Bounds.H = fullSizeY - (text->Offset.Y * 2); + // text->CenterText = true; + // text->WordWrap = true; + text->SetCenter(true); + text->SetCenterY(true); + text->SetWordWrap(true); + // text->SetCenter(true); + auto textBoxImage = ContentRegistry::CreateContent("uitextbox" + name, (int)fullSizeX, (int)fullSizeY); + textBoxImage->LoadContent(); + // Set the background + textBoxImage->Clear({80, 0, 80, 220}); + float sizeX = 8; + float sizeY = 9; + textBoxImage->SetAlpha(200); + // Draw the corners + // tl + auto srcRect = RectangleF{0, 0, sizeX, sizeY}; + auto dstRect = RectangleF{0, 0, sizeX, sizeY}; + textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); + // tr + srcRect = RectangleF{uiImageFull->Width() - sizeX, 0, sizeX, sizeY}; + dstRect = RectangleF{fullSizeX - sizeX, 0, sizeX, sizeY}; + textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); + // bl + srcRect = RectangleF{0, uiImageFull->Height() - sizeY, sizeX, sizeY}; + dstRect = RectangleF{0, fullSizeY - sizeY, sizeX, sizeY}; + textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); + // br + srcRect = RectangleF{uiImageFull->Width() - sizeX, uiImageFull->Height() - sizeY, sizeX, sizeY}; + dstRect = RectangleF{fullSizeX - sizeX, fullSizeY - sizeY, sizeX, sizeY}; + textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); + // draw the bars + int length = fullSizeX - (sizeX); + int height = fullSizeY - (sizeY); + // top + srcRect = RectangleF{1 + sizeX, 0, 1, sizeY}; + for (auto i = sizeX; i < length; i++) { + dstRect = RectangleF{(float)i, 0, 1, sizeY}; + textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); + } + // bottom + for (auto i = sizeX; i < length; i++) { + dstRect = RectangleF{(float)i, fullSizeY - sizeY + 4, 1, sizeY}; + textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); + } + // left + srcRect = RectangleF({0, sizeY + 1, sizeX, 1}); + for (auto i = sizeY; i < height; i++) { + dstRect = RectangleF{0, (float)i, sizeX, 1}; + textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); + } + // right + for (auto i = sizeY; i < height; i++) { + dstRect = RectangleF{fullSizeX - sizeX + 3, (float)i, sizeX, 1}; + textBoxImage->DrawImageToImage(*uiImageFull, srcRect, dstRect); + } + + // Add this image to the ui panel + auto textBoxUIImage = new UIImage(textPanel, "textBoxImage"); + textBoxUIImage->ImagePtr = textBoxImage; + textBoxUIImage->SetVisible(true); + textBoxUIImage->Bounds = RectangleF{0, 0, (float)textBoxImage->Width(), (float)textBoxImage->Height()}; + textBoxUIImage->Offset.X = 0; + textBoxUIImage->Offset.Y = 0; + // Setup the animators + if (screen) { + auto animator = std::make_shared("levelDisplayAnimator"); + auto waitTween = new Tween(1.0f); + auto fadeOutTween = new Tween(255, 0, 0.5, textPanel->AlphaHandle(), Supergoon::Easings::Linear); + fadeOutTween->EndFunc = [textPanel]() { + textPanel->SetVisible(false); + textPanel->SetAlpha(255); + }; + animator->AddUIObjectTween(waitTween, textPanel); + animator->AddUIObjectTween(fadeOutTween, textPanel); + textPanel->Animators.push_back(animator); + } + return textPanel; +} diff --git a/src/game/Utilities/Utilities.hpp b/src/game/Utilities/Utilities.hpp new file mode 100644 index 0000000..3183353 --- /dev/null +++ b/src/game/Utilities/Utilities.hpp @@ -0,0 +1,9 @@ +#pragma once +#include +namespace Supergoon { +class Panel; + +Panel* CreateUITextbox(std::string name, Point screenLoc, Point size, bool screen); + + +} // namespace Supergoon diff --git a/what.txt b/what.txt deleted file mode 100644 index 1f397b1..0000000 --- a/what.txt +++ /dev/null @@ -1,44 +0,0 @@ -==24872== HEAP SUMMARY: -==24872== in use at exit: 827,449 bytes in 4,067 blocks -==24872== total heap usage: 182,534 allocs, 171,014 frees, 308,161,384 bytes allocated -==24872== -==24872== 4 bytes in 1 blocks are definitely lost in loss record 2 of 211 -==24872== at 0x4E05833: operator new(unsigned long) (vg_replace_malloc.c:483) -==24872== by 0x1F501F: Supergoon::Level::Level(char const*) (Level.cpp:71) -==24872== by 0x1F96BF: std::__detail::_MakeUniq::__single_object std::make_unique(char const*&&) (unique_ptr.h:1070) -==24872== by 0x1F5C2C: Supergoon::Level::LoadNewLevel(std::__cxx11::basic_string, std::allocator >) (Level.cpp:159) -==24872== by 0x1F4B35: Supergoon::Level::AddLevelEventHandlers()::{lambda(int, void*, void*)#2}::operator()(int, void*, void*) const (Level.cpp:38) -==24872== by 0x1F7D27: void std::__invoke_impl(std::__invoke_other, Supergoon::Level::AddLevelEventHandlers()::{lambda(int, void*, void*)#2}&, int&&, void*&&, void*&&) (invoke.h:61) -==24872== by 0x1F75F2: std::enable_if, void>::type std::__invoke_r(Supergoon::Level::AddLevelEventHandlers()::{lambda(int, void*, void*)#2}&, int&&, void*&&, void*&&) (invoke.h:111) -==24872== by 0x1F7064: std::_Function_handler::_M_invoke(std::_Any_data const&, int&&, void*&&, void*&&) (std_function.h:290) -==24872== by 0x1CD874: std::function::operator()(int, void*, void*) const (std_function.h:591) -==24872== by 0x1CC40D: Supergoon::Events::HandleCustomEventHandlers(SDL_Event*) (Events.cpp:68) -==24872== by 0x1CC2EF: Supergoon::Events::HandleEvent(SDL_Event*) (Events.cpp:54) -==24872== by 0x1ADF75: Supergoon::Game::HandleEvent(SDL_Event*) (Game.cpp:97) -==24872== -==24872== 49 (48 direct, 1 indirect) bytes in 1 blocks are definitely lost in loss record 63 of 211 -==24872== at 0x4E05833: operator new(unsigned long) (vg_replace_malloc.c:483) -==24872== by 0x14563F: sgRegisterGame (BlackjackGame.cpp:225) -==24872== by 0x1AD8D9: SDL_AppInit (Game.cpp:40) -==24872== by 0x5532E4: SDL_InitMainCallbacks (SDL_main_callbacks.c:104) -==24872== by 0x528979: SDL_EnterAppMainCallbacks_REAL (SDL_sysmain_callbacks.c:42) -==24872== by 0x38BEE7: SDL_EnterAppMainCallbacks (SDL_dynapi_procs.h:207) -==24872== by 0x1AD843: SDL_main (SDL_main_impl.h:59) -==24872== by 0x3CC09C: SDL_RunApp_REAL (SDL_runapp.c:40) -==24872== by 0x3844DE: SDL_RunApp_DEFAULT (SDL_dynapi_procs.h:804) -==24872== by 0x3918A4: SDL_RunApp (SDL_dynapi_procs.h:804) -==24872== by 0x1AD872: main (SDL_main_impl.h:149) -==24872== -==24872== 2,347 (2,243 direct, 104 indirect) bytes in 8 blocks are definitely lost in loss record 176 of 211 -==24872== at 0x4E050C5: malloc (vg_replace_malloc.c:442) -==24872== by 0x40D2E1: real_malloc (SDL_malloc.c:6321) -==24872== by 0x40D568: SDL_malloc_REAL (SDL_malloc.c:6431) -==24872== by 0x50492E: SDL_SYS_GetPrefPath (SDL_sysfilesystem.c:301) -==24872== by 0x3AE76B: SDL_GetPrefPath_REAL (SDL_filesystem.c:498) -==24872== by 0x38E43F: SDL_GetPrefPath (SDL_dynapi_procs.h:476) -==24872== by 0x1AE03B: Supergoon::Game::InitializeImGui() (Game.cpp:107) -==24872== by 0x1ADE43: Supergoon::Game::Initialize() (Game.cpp:90) -==24872== by 0x1AE278: Supergoon::Game::InternalReset() (Game.cpp:135) -==24872== by 0x1AD8F4: SDL_AppInit (Game.cpp:42) -==24872== by 0x5532E4: SDL_InitMainCallbacks (SDL_main_callbacks.c:104) -==24872== by 0x528979: SDL_EnterAppMainCallbacks_REAL (SDL_sysmain_callbacks.c:42) \ No newline at end of file