/* +------------------------------------------------------+ ____/ \____ /| - Open source game framework licensed to freely use, | \ / / | copy, modify and sell without restriction | +--\ ^__^ /--+ | | | ~/ \~ | | - created for | | ~~~~~~~~~~~~ | +------------------------------------------------------+ | SPACE ~~~~~ | / | ~~~~~~~ BOX |/ +-------------*/ #pragma once #include #include #include #include #include #include #include #define SDL_MAIN_HANDLED #include "SDL.h" #include "SDL_mixer.h" #include "SDL_ttf.h" #define GL_GLEXT_PROTOTYPES #if defined(__EMSCRIPTEN__) #include #include #include #elif defined(__ANDROID__) || defined(ANDROID) #include #include #else #include "glew/glew.h" #endif #if defined(__ANDROID__) || defined(ANDROID) #include #endif #include "Node.hpp" #include "Input.hpp" #include "Display.hpp" #include "Configuration.hpp" #include "Delegate.hpp" #include "Recorder.hpp" #include "Audio.hpp" #include "Log.hpp" #include "filesystem.hpp" #include "extension.hpp" class Game : public Node { protected: /* Construct sb::Delegate first, so it is the last object to be destroyed, allowing the other objects to successfully call its * unsubscribe method in their destructors. */ sb::Delegate _delegate {this}; private: SDL_Window* _window; std::shared_ptr _font; inline static const std::string user_config_path = "config.json"; inline static const std::string android_config_path = "config_android.json"; inline static const std::string wasm_config_path = "config_wasm.json"; /*! * Overrides SDL's default log function to log a message to stdout/stderr and, if log is enabled in the * global configuration, to a file. Debug level statements may be suppressed, printed to stdout, or printed to * both stdout and file, depending on the global configuration. This shouldn't be called directly. Use * `sb::Log::log` instead. * * @see sb::Log::log(const std::string&) * * @param userdata must be a pointer to Game * @param category SDL log category. It is not used by SPACEBOX, so it should be sb::Log::DEFAULT_CATEGORY * @param priority SDL log priority, which is equivalent to the values in sb::Log::Level * @param message message as a C-style string */ static void sdl_log_override(void* userdata, int category, SDL_LogPriority priority, const char* message); protected: Configuration _configuration {this}; public: /* two-state enum equivalent to a boolean that can improve readability depending on the context */ enum class Flip { OFF, ON }; /* Prevent an instance of this class from being copied or moved */ Game(const Game&) = delete; Game& operator=(const Game&) = delete; Game(Game&&) = delete; Game& operator=(Game&&) = delete; SDL_Renderer* renderer = nullptr; SDL_GLContext glcontext = nullptr; int frame_count_this_second = 0, last_frame_length, current_frames_per_second = 0; float frame_time_overflow = 0, last_frame_timestamp, last_frame_count_timestamp; bool done = false, show_framerate = true, is_gl_context = true; sb::Display display {this}; // Recorder recorder {this}; Input input {this}; std::vector frame_length_history; Game(); virtual void reset() { activate(); } void print_frame_length_history(); void load_sdl_context(); void load_gl_context(); GLuint load_shader(const fs::path&, GLenum) const; bool link_shader(GLuint program) const; void log_renderer_info(SDL_RendererInfo&); /*! * Write resolution, monitor refresh rate, and pixel format to the log. Taken from SDL_GetCurrentDisplayMode.html * on the SDL wiki. */ void log_display_mode() const; /*! * Log properties of the GL context. Taken from `sdl_source/tests/testgles2.c` */ void log_gl_properties() const; void log_surface_format(SDL_Surface*, std::string = "surface"); const Configuration& configuration() const; Configuration& configuration(); const sb::Delegate& delegate() const; sb::Delegate& delegate(); const SDL_Window* window() const; SDL_Window* window(); const SDL_Renderer* get_renderer() const; SDL_Renderer* get_renderer(); const Input& get_input() const; Input& get_input(); /*! * @return shared pointer to the default font pre-loaded at construction */ std::shared_ptr font() const; /*! * Get a new font object with the given font size. If the font cannot be loaded, the default font will be returned. If there was an error, * the shared pointer will point to `nullptr`. * * @return shared pointer to the font object created from the TTF font file at the given path */ std::shared_ptr font(const fs::path& path, int size) const; void run(); void frame(float); void flag_to_end(); virtual void update(float timestamp) = 0; void set_framerate(int); void handle_quit_event(SDL_Event&); void quit(); virtual std::string class_name() const { return "Game"; } ~Game(); /*! * Applies delta timing to a value: returns the value as weighted by the amount of time passed since the * last frame update, allowing for values to change the same amount over time independent of the frame rate. * The amount is how much the value should change per second. * * @param amount any scalar value to be weighted * @return weighted value */ template T weight(T amount) const { return (last_frame_length / 1000.0f) * amount; } }; /* Add Game class to the sb namespace. This should be the default location, but Game is left in the global namespace * for backward compatibility. */ namespace sb { using ::Game; } #if defined(__EMSCRIPTEN__) void loop(void*); #endif