- add Switch and time_it classes

- finish adding #pragma once to headers
- move configuration initialization into Game class
- print debug to stdout by default
- explicitly pass file to refresh to config auto refresh
- add delegate to sb namespace and deprecate get_delegate() in favor of delegate()
- set custom log function before loading config
- raise exception when no model textures are available
This commit is contained in:
ohsqueezy 2023-05-23 15:28:53 -04:00
parent 8c086ba161
commit 3ba3be4496
30 changed files with 546 additions and 238 deletions

View File

@ -1,5 +1,14 @@
#ifndef Animation_h_
#define Animation_h_
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#pragma once
#include <vector>
#include <functional>
@ -58,5 +67,3 @@ public:
};
#include "Node.hpp"
#endif

View File

@ -84,8 +84,7 @@
* { 5.5 6.6 7.7 8.8 }
*/
#ifndef SB_ATTRIBUTES_H_
#define SB_ATTRIBUTES_H_
#pragma once
/* include Open GL */
#if defined(__EMSCRIPTEN__)
@ -289,5 +288,3 @@ namespace sb
};
}
#endif

View File

@ -1,5 +1,14 @@
#ifndef Audio_h_
#define Audio_h_
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#pragma once
#include <regex>
#include <map>
@ -89,5 +98,3 @@ struct Audio : Node
#include "Box.hpp"
#include "Configuration.hpp"
#include "Display.hpp"
#endif

View File

@ -8,8 +8,7 @@
| ~~~~~~~ BOX |/
+-------------*/
#ifndef CAROUSEL_H_
#define CAROUSEL_H_
#pragma once
#include <cstdint>
#include <iterator>
@ -86,5 +85,3 @@ namespace sb
};
}
#endif

View File

@ -1,5 +1,4 @@
#ifndef Color_h_
#define Color_h_
#pragma once
#include <cstdint>
#include <cstdlib>
@ -50,5 +49,3 @@ namespace std
{
std::ostream& operator<<(std::ostream&, const Color&);
}
#endif

View File

@ -13,12 +13,6 @@
Configuration::Configuration(Node* parent) : Node(parent)
{
set_defaults();
merge(USER_CONFIG_PATH);
#ifdef __ANDROID__
merge(ANDROID_CONFIG_PATH);
#endif
auto_refresher.set_frame_length(config["configuration"]["auto-refresh-interval"].get<float>());
auto_refresh(config["configuration"]["auto-refresh"]);
}
void Configuration::set_defaults()
@ -93,7 +87,7 @@ void Configuration::set_defaults()
};
config["log"] = {
{"enabled", false},
{"debug-to-stdout", false},
{"debug-to-stdout", true},
{"debug-to-file", false},
{"output-directory", "."},
{"info-file-name", "space_box_log.txt"},
@ -123,6 +117,7 @@ const nlohmann::json& Configuration::operator()() const
void Configuration::merge(const nlohmann::json& incoming)
{
sb::Log::log("Merging into configuration", sb::Log::DEBUG);
if (!incoming.empty())
{
/* loop over first level key/value pairs */
@ -186,19 +181,35 @@ void Configuration::merge(const char* path)
merge(fs::path(path));
}
void Configuration::auto_refresh(bool on)
void Configuration::enable_auto_refresh(const fs::path& file_to_refresh, float interval)
{
on ? auto_refresher.play() : auto_refresher.pause();
#ifndef __ANDROID__
/* Warn user if the file does not exist */
if (!fs::exists(file_to_refresh))
{
std::ostringstream message;
message << "File to auto-refresh does not exist: " << file_to_refresh;
sb::Log::log(file_to_refresh, sb::Log::WARN);
}
#endif
this->file_to_refresh = file_to_refresh;
auto_refresher.set_frame_length(interval);
auto_refresher.play();
}
void Configuration::disable_auto_refresh()
{
auto_refresher.pause();
}
void Configuration::refresh()
{
if (fs::exists(USER_CONFIG_PATH) && fs::last_write_time(USER_CONFIG_PATH) > config_file_modification_time)
if (fs::exists(file_to_refresh) && fs::last_write_time(file_to_refresh) > config_file_modification_time)
{
std::ostringstream message;
message << "config file modified, reloading " << USER_CONFIG_PATH;
message << "config file modified, reloading " << file_to_refresh;
sb::Log::log(message, sb::Log::DEBUG);
merge(USER_CONFIG_PATH);
merge(file_to_refresh);
}
}

View File

@ -25,12 +25,10 @@ class Configuration : public Node
private:
inline static const std::string USER_CONFIG_PATH = "config.json";
inline static const std::string ANDROID_CONFIG_PATH = "config_android.json";
Animation auto_refresher = Animation(&Configuration::refresh, this);
fs::file_time_type config_file_modification_time;
nlohmann::json config;
fs::path file_to_refresh;
/*!
* Fill the config JSON with default values set by the framework.
@ -103,12 +101,18 @@ public:
void merge(const char* path);
/*!
* Set auto refresh to on or off. Auto refresh watches the file at Configuration::user_path (set in the contructor) for changes
* and loads them automatically at the interval set at Configuration::config["configuration"]["auto-refresh-interval"].
* Enable auto refresh. Auto refresh watches the file at the given path for changes and loads them automatically every interval given
* in milliseconds.
*
* @param bool true to refresh automatically, false to turn off automatic refresh
* @param file_to_refresh path to a configuration JSON
* @param interval amount of milliseconds between each refresh
*/
void auto_refresh(bool);
void enable_auto_refresh(const fs::path& file_to_refresh, float interval);
/*!
* Disable auto refresh. The file previously set with Configuration::enable_auto_refresh will no longer be watched for changes.
*/
void disable_auto_refresh();
/*!
* Check if the user config file was modified and merge if so.

View File

@ -8,12 +8,14 @@
* | ~~~~~~~ BOX |/
* +--------------+
*
* Connection
* ==========
* Connection objects contain a binary state of either on (connected) or off (not connected). When their state
* is changed, an optional user supplied function corresponding to the state change is run automatically.
*
* A connection is an object containing a binary state of either on (connected) or off (not connected)
* and user supplied functions that run automatically on each state change. The functions each have the
* same return type, number of arguments, and argument types determined by the template arguments.
* The functions each have the same return type, number of arguments, and argument types determined by the
* template arguments.
*
* By default, the state must change in order for a callback to be triggered. To allow repeat calls to
* trigger a callback, the
*
* Original test code:
*
@ -34,6 +36,8 @@
* std::cout << result << std::endl;
*/
#pragma once
#include <functional>
namespace sb
@ -58,9 +62,14 @@ namespace sb
public:
/* Without any arguments, the connection object will be in the disconnected state with empty functions. Otherwise,
/*!
* Without any arguments, the connection object will be in the disconnected state with empty functions. Otherwise,
* the supplied functions will be added to the connection object. The first function argument will be run on a
* connection, and the second function argument will be run on a disconnection. */
* connection, and the second function argument will be run on a disconnection.
*
* @param on_connect_callback function to run on connection
* @param on_disconnect_callback function to run on disconnection
*/
Connection(callback on_connect_callback = callback(), callback on_disconnect_callback = callback())
{
if (on_connect_callback)

View File

@ -1,15 +1,17 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#include "Delegate.hpp"
using namespace sb;
Delegate::Delegate(Node* parent) : Node(parent) {}
void Delegate::add_subscriber(Subscriber s, std::uint32_t type)

View File

@ -1,15 +1,14 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#ifndef SB_DELEGATE_H_
#define SB_DELEGATE_H_
#pragma once
#include <string>
#include <map>
@ -20,63 +19,64 @@
#include "SDL.h"
#include "Node.hpp"
struct Subscriber
namespace sb
{
std::function<void(SDL_Event&)> f;
Node* o;
};
class Delegate : public Node
{
private:
std::map<std::uint32_t, std::vector<Subscriber>> subscribers;
bool cancelling_propagation = false;
public:
inline static std::uint32_t command_event_type = SDL_RegisterEvents(1);
Delegate(Node*);
void add_subscriber(Subscriber, std::uint32_t);
void dispatch();
static bool compare(SDL_Event&, const std::vector<std::string>&, bool = false, bool = false);
static bool compare(SDL_Event&, const std::string& = "", bool = false, bool = false);
static bool compare_cancel(SDL_Event&, const std::string& = "");
static bool compare_neutral(SDL_Event&, const std::string& = "");
void cancel_propagation();
static const std::string& get_event_command(SDL_Event&);
static bool get_event_cancel_state(SDL_Event&);
template<typename T>
void subscribe(void(T::*f)(SDL_Event&), T* o, std::uint32_t type = command_event_type)
struct Subscriber
{
add_subscriber({std::bind(f, o, std::placeholders::_1), static_cast<Node*>(o)}, type);
}
std::function<void(SDL_Event&)> f;
Node* o;
};
template<typename T>
void unsubscribe(T* p)
class Delegate : public Node
{
for (auto t = subscribers.begin(); t != subscribers.end(); t++)
private:
std::map<std::uint32_t, std::vector<Subscriber>> subscribers;
bool cancelling_propagation = false;
public:
inline static std::uint32_t command_event_type = SDL_RegisterEvents(1);
Delegate(Node*);
void add_subscriber(Subscriber, std::uint32_t);
void dispatch();
static bool compare(SDL_Event&, const std::vector<std::string>&, bool = false, bool = false);
static bool compare(SDL_Event&, const std::string& = "", bool = false, bool = false);
static bool compare_cancel(SDL_Event&, const std::string& = "");
static bool compare_neutral(SDL_Event&, const std::string& = "");
void cancel_propagation();
static const std::string& get_event_command(SDL_Event&);
static bool get_event_cancel_state(SDL_Event&);
template<typename T>
void subscribe(void(T::*f)(SDL_Event&), T* o, std::uint32_t type = command_event_type)
{
for (auto s = t->second.begin(); s != t->second.end();)
add_subscriber({std::bind(f, o, std::placeholders::_1), static_cast<Node*>(o)}, type);
}
template<typename T>
void unsubscribe(T* p)
{
for (auto t = subscribers.begin(); t != subscribers.end(); t++)
{
if (p == s->o)
for (auto s = t->second.begin(); s != t->second.end();)
{
s = t->second.erase(s);
}
else
{
s++;
if (p == s->o)
{
s = t->second.erase(s);
}
else
{
s++;
}
}
}
}
}
};
};
}
#include "Input.hpp"
#include "Game.hpp"
#endif

View File

@ -1,15 +1,14 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, and modify without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#ifndef SB_DISPLAY_H_
#define SB_DISPLAY_H_
#pragma once
#include <sstream>
#include "glm/vec2.hpp"
@ -47,5 +46,3 @@ namespace sb
}
#include "Game.hpp"
#endif

View File

@ -26,13 +26,13 @@
*/
#ifndef SB_GLOBJECT_H_
#define SB_GLOBJECT_H_
#pragma once
#include <iostream>
#include <sstream>
#include <memory>
#include <functional>
#include <stdexcept>
/* include Open GL */
#if defined(__EMSCRIPTEN__)
@ -122,5 +122,3 @@ namespace sb
void buffer_deleter(GLuint*);
}
#endif

View File

@ -12,21 +12,27 @@
Game::Game()
{
/* Set the appropriate priority level for the default log category so either info level messages
* and higher are enabled or debug level messages are enabled, depending on the global configuration */
SDL_LogPriority default_log_category_priority;
if (configuration()["log"]["debug-to-file"] || configuration()["log"]["debug-to-stdout"])
{
default_log_category_priority = SDL_LOG_PRIORITY_DEBUG;
}
else
{
default_log_category_priority = SDL_LOG_PRIORITY_INFO;
}
SDL_LogSetPriority(sb::Log::DEFAULT_CATEGORY, default_log_category_priority);
/* Set custom log function that prints to stdout/stderr and to file if enabled */
/* Set custom log function that prints to stdout/stderr and to file if enabled. Temporarily set to DEBUG priority before loading
* user setting. */
SDL_LogSetOutputFunction(&Game::sdl_log_override, this);
SDL_LogSetPriority(sb::Log::DEFAULT_CATEGORY, SDL_LOG_PRIORITY_DEBUG);
/* Merge user configuration into the existing configuration and turn on auto refresh if it is enabled by the configuration. */
_configuration.merge(USER_CONFIG_PATH);
#ifdef __ANDROID__
_configuration.merge(ANDROID_CONFIG_PATH);
#endif
if (configuration()["configuration"]["auto-refresh"])
{
_configuration.enable_auto_refresh(USER_CONFIG_PATH, configuration()["configuration"]["auto-refresh-interval"].get<float>());
}
/* Set the appropriate priority level for the default log category, changing it to INFO if the user does not want debug statements,
* or leaving it at DEBUG otherwise. */
if (!configuration()["log"]["debug-to-file"] && !configuration()["log"]["debug-to-stdout"])
{
SDL_LogSetPriority(sb::Log::DEFAULT_CATEGORY, SDL_LOG_PRIORITY_INFO);
}
/* Log the current working directory as seen by std::filesystem */
std::ostringstream log_message;
@ -56,7 +62,7 @@ Game::Game()
set_framerate(configuration()["display"]["framerate"]);
/* Subscribe to SDL's quit event */
delegate.subscribe(&Game::handle_quit_event, this, SDL_QUIT);
_delegate.subscribe(&Game::handle_quit_event, this, SDL_QUIT);
/* Needed for displaying fullscreen correctly on Linux (?) Also might need SDL_VIDEO_CENTERED (?) */
std::string fullscreen_env_assigment = "SDL_VIDEO_X11_LEGACY_FULLSCREEN=0";
@ -538,7 +544,7 @@ void Game::frame(float ticks)
if (last_frame_length < 1000)
{
recorder.update();
delegate.dispatch();
_delegate.dispatch();
audio.update();
input.unsuppress_animation.update();
update();
@ -636,18 +642,18 @@ void Game::quit()
Game::~Game()
{
get_delegate().unsubscribe(this);
_delegate.unsubscribe(this);
}
FramerateIndicator::FramerateIndicator(Node* parent) : Sprite(parent)
{
get_delegate().subscribe(&FramerateIndicator::respond, this);
delegate().subscribe(&FramerateIndicator::respond, this);
hide();
}
void FramerateIndicator::respond(SDL_Event& event)
{
if (get_delegate().compare(event, "toggle-framerate"))
if (delegate().compare(event, "toggle-framerate"))
{
toggle_hidden();
}

View File

@ -73,6 +73,9 @@ private:
float frame_length = 1000.0 / 60.0;
SDL_Window* _window;
inline static const std::string USER_CONFIG_PATH = "config.json";
inline static const std::string ANDROID_CONFIG_PATH = "config_android.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
@ -108,7 +111,7 @@ public:
float frame_time_overflow = 0, last_frame_timestamp, last_frame_count_timestamp;
bool done = false, show_framerate = true, is_gl_context = true;
Configuration _configuration {this};
Delegate delegate {this};
sb::Delegate _delegate {this};
sb::Display display {this};
Recorder recorder {this};
Input input {this};

View File

@ -1,12 +1,12 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#include "Input.hpp"
@ -121,7 +121,7 @@ void Input::post_command(std::string& name, const bool& cancel)
{
SDL_Event relay;
bool* cancel_pointer = new bool(cancel);
relay.type = Delegate::command_event_type;
relay.type = sb::Delegate::command_event_type;
relay.user.data1 = &name;
relay.user.data2 = cancel_pointer;
SDL_PushEvent(&relay);

View File

@ -1,15 +1,14 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#ifndef SB_INPUT_H_
#define SB_INPUT_H_
#pragma once
#include <string>
#include <map>
@ -79,5 +78,3 @@ public:
#include "extension.hpp"
#include "Configuration.hpp"
#include "Delegate.hpp"
#endif

View File

@ -115,7 +115,20 @@ std::map<std::string, sb::Texture>& sb::Model::textures()
* anything else a Texture object can be used for with direct calls to GL functions. */
sb::Texture& sb::Model::texture(const std::string& name)
{
return textures().at(name);
if (textures().empty())
{
throw std::out_of_range("There are no textures attached to this model.");
}
else if (textures().find(name) == textures().end())
{
std::ostringstream message;
message << "No texture named " << name << " found attached to this model.";
throw std::out_of_range(message.str());
}
else
{
return textures().at(name);
}
}
/* Get the default texture. The default texture must have previously been set with the default key as

View File

@ -8,8 +8,7 @@
| ~~~~~~~ BOX |/
+-------------*/
#ifndef MODEL_H_
#define MODEL_H_
#pragma once
/* GL functions */
#if defined(__EMSCRIPTEN__)
@ -26,6 +25,7 @@
#include <map>
#include <memory>
#include <iterator>
#include <stdexcept>
#include "glm/glm.hpp"
#include "Attributes.hpp"
#include "Texture.hpp"
@ -82,8 +82,12 @@ namespace sb
{0.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f},
{1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f}
});
inline const static std::shared_ptr<sb::Attributes> color = std::make_shared<sb::Attributes>(sb::Attributes{
{1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {0.8f, 0.0f, 0.3f},
{1.0f, 1.0f, 0.0f}, {0.8f, 0.0f, 0.3f}, {0.8f, 0.0f, 0.3f}
});
Plane() : Model(std::map<std::string, std::shared_ptr<sb::Attributes>>({{"position", position}, {"uv", uv}})) {}
Plane() : Model(std::map<std::string, std::shared_ptr<sb::Attributes>>({{"position", position}, {"uv", uv}, {"color", color}})) {}
};
@ -109,5 +113,3 @@ namespace sb
};
}
#endif

View File

@ -1,20 +1,14 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, and modify without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#include "Configuration.hpp"
#include "Delegate.hpp"
#include "Display.hpp"
#include "Input.hpp"
#include "Box.hpp"
#include "Game.hpp"
#include "Audio.hpp"
#include "Node.hpp"
Node::Node() : Node(nullptr) {}
@ -54,9 +48,14 @@ Configuration& Node::configuration()
return get_root()->configuration();
}
Delegate& Node::get_delegate()
sb::Delegate& Node::delegate()
{
return get_root()->delegate;
return get_root()->_delegate;
}
sb::Delegate& Node::get_delegate()
{
return delegate();
}
const sb::Display& Node::get_display() const

View File

@ -1,15 +1,14 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, and modify without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#ifndef SB_NODE_H_
#define SB_NODE_H_
#pragma once
#include <string>
#include <iostream>
@ -20,7 +19,12 @@
#include "filesystem.hpp"
class Game;
class Delegate;
namespace sb
{
class Delegate;
}
class Input;
class Box;
class Configuration;
@ -47,7 +51,17 @@ public:
bool is_active() const;
const Configuration& configuration() const;
Configuration& configuration();
Delegate& get_delegate();
/*!
* @return the Delegate object
*/
sb::Delegate& delegate();
/*!
* @deprecated use Node::delegate
*/
sb::Delegate& get_delegate();
const sb::Display& get_display() const;
const SDL_Renderer* get_renderer() const;
SDL_Renderer* get_renderer();
@ -85,5 +99,3 @@ private:
bool active = true;
};
#endif

View File

@ -1,5 +1,14 @@
#ifndef Pixels_h_
#define Pixels_h_
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#pragma once
#include "SDL.h"
#include "Box.hpp"
@ -49,5 +58,3 @@ struct Pixels
}
};
#endif

View File

@ -1,5 +1,14 @@
#ifndef Recorder_h_
#define Recorder_h_
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#pragma once
#define GLM_ENABLE_EXPERIMENTAL
@ -70,5 +79,3 @@ public:
virtual std::string class_name() const { return "Recorder"; }
};
#endif

View File

@ -1,9 +1,19 @@
#ifndef Segment_h_
#define Segment_h_
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#pragma once
#include <ostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include "glm/vec2.hpp"
@ -66,5 +76,3 @@ namespace std
{
std::ostream& operator<<(std::ostream&, const Segment&);
}
#endif

View File

@ -1,5 +1,4 @@
#ifndef Sprite_h_
#define Sprite_h_
#pragma once
#include <map>
#include <string>
@ -212,5 +211,3 @@ public:
#include "Pixels.hpp"
#include "extension.hpp"
#endif

135
src/Switch.hpp Normal file
View File

@ -0,0 +1,135 @@
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+--------------+
A switch object contains a state of either on or off which can be read by reading the switch in a bool context. A
switch can be flipped to change the state. If a reaction function has been set, it will run every time the switch
is flipped. The function must accept a bool as its first argument which will provide the state of the switch. It can
also accept an arbitrary number of arguments after that and can return a value.
This example will flip the switch three times and print the state each time.
sb::Switch<> sw1;
sw1.on_state_change([](bool state) {
std::cout << "Door is " << (state ? "OPEN" : "CLOSED") << std::endl;
});
sw1.flip();
sw1.flip();
sw1.flip();
The expected output is
Door is OPEN
Door is CLOSED
Door is OPEN
This example ignores the switch state and adds an arbitrary amount to a running tally each flip.
sb::Switch<int, int, int> sw2;
sw2.on_state_change([](bool state, int tally, int add) -> int {
tally += add;
std::cout << "Adding " << add << " to tally: " << tally << std::endl;
return tally;
});
int tally = 0;
tally = sw2.flip(tally, 1);
tally = sw2.flip(tally, 5);
tally = sw2.flip(tally, 99);
The expected output is
Adding 1 to tally: 1
Adding 5 to tally: 6
Adding 99 to tally: 105
Although the state can just be ignored, it can also be forced to remain in one state. This example uses the
`&` operator to allow access to the switch itself so it can be turned off automatically after every flip.
sb::Switch<void, const std::string&> sw3;
sw3.on_state_change([&](bool state, const std::string& visitor) {
if (state)
{
std::cout << "Come in " << visitor << ", my door is always " << (state ? "OPEN" : "CLOSED") << std::endl;
sw3.flip("");
}
});
sw3.flip("Mario");
sw3.flip("Luigi");
sw3.flip("Toad");
The expected output is
Come in Mario, my door is always OPEN
Come in Luigi, my door is always OPEN
Come in Toad, my door is always OPEN
*/
#pragma once
#include <functional>
namespace sb
{
template<typename return_type = void, typename... arguments>
class Switch
{
private:
using Reaction = std::function<return_type(bool, arguments...)>;
inline static const bool STATE_OFF = false;
inline static const bool STATE_ON = true;
bool state = STATE_OFF;
Reaction reaction;
public:
/*!
* The switch object will initialized to the off state. A reaction function can be specified, otherwise it will have an
* empty reaction function.
*
* @param callback function to run on state change
*/
Switch(Reaction reaction = Reaction()) : reaction(reaction) {}
return_type flip(arguments... args)
{
state = !state;
return reaction(state, args...);
}
/*!
* Assign the reaction function.
*
* @param reaction function to be called when the state is changed
*/
void on_state_change(Reaction reaction)
{
this->reaction = reaction;
}
/*!
* @return true if state is Switch::STATE_ON, false otherwise
*/
bool on()
{
return state;
}
/*!
* @return when called as a boolean, return state
*/
operator bool()
{
return on();
}
};
}

View File

@ -20,8 +20,7 @@
* functions.
*/
#ifndef SB_TEXTURE_H_
#define SB_TEXTURE_H_
#pragma once
#include <sstream>
#include <stdexcept>
@ -144,5 +143,3 @@ namespace sb
void texture_deleter(GLuint*);
}
#endif

View File

@ -1,5 +1,14 @@
#ifndef Timer_h_
#define Timer_h_
/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#pragma once
#include "SDL.h"
@ -33,5 +42,3 @@ public:
void update();
};
#endif

View File

@ -24,8 +24,7 @@
*/
#ifndef SB_VBO_H_
#define SB_VBO_H_
#pragma once
#include <iostream>
#include <sstream>
@ -58,5 +57,3 @@ namespace sb
};
}
#endif

View File

@ -1,3 +1,5 @@
#pragma once
/* MinGW filesystem library is in another location */
#if defined(__MINGW32__)
#include <experimental/filesystem>

93
src/time_it.hpp Normal file
View File

@ -0,0 +1,93 @@
/* /\ +------------------------------------------------------+
* ____/ \____ /| - Open source game framework licensed to freely use, |
* \ / / | copy, modify and sell without restriction |
* +--\ ^__^ /--+ | |
* | ~/ \~ | | - created for <https://foam.shampoo.ooo> |
* | ~~~~~~~~~~~~ | +------------------------------------------------------+
* | SPACE ~~~~~ | /
* | ~~~~~~~ BOX |/
* +--------------+
*
* time_it
* =======
*
* This class is used for printing the running time of a function or lambda. It can be
* inserted inline into existing code by wrapping the block to time in a lambda. It can also be
* given a function and optionally the function's arguments and will return a value of the type specified.
*
* The following code
*
* ~~~{.cpp}
* std::string fake(std::string x, std::string y)
* {
* return x + y;
* }
*
* // Needs SPACEBOX initialization for log call
* int ii = 0;
* time_it()([&] { while (ii++ < 1000); });
* ii = 0;
* int revolving = time_it<int, int>("for revolving")([&](int x) -> int { while (ii++ < 10000); return x; }, 1);
* ii = 0;
* std::cout << "Revolving is " << revolving << std::endl;
* std::string addition = time_it<std::string, std::string, std::string>("for combination")(fake, "Hello, ", "World!");
* std::cout << "Combination is " << addition << std::endl;
* time_it("for many combinations")([&] { while (ii++ < 100000) { fake("a", "b"); } });
* ~~~
*
* gives the expected output
*
* Elapsed time: 2.588e-06s
* Elapsed time for revolving: 2.1547e-05s
* Revolving is 1
* Elapsed time for combination: 7.27e-07s
* Combination is Hello, World!
* Elapsed time for many combinations: 0.0107351s
*/
#pragma once
#include <string>
#include <iostream>
#include <chrono>
#include <functional>
#include <type_traits>
template<typename return_type = void, typename... arguments>
class time_it
{
private:
std::string info = "";
public:
time_it(std::string info = "") : info(info) {};
return_type operator()(const std::function<return_type(arguments...)>&& block, arguments... args)
{
std::cout << "{Elapsed time";
if (!info.empty())
{
std::cout << " " << info;
}
std::cout << ": ";
auto start = std::chrono::steady_clock::now();
std::chrono::duration<float> elapsed;
if constexpr (std::is_same_v<return_type, void>)
{
block(args...);
elapsed = std::chrono::steady_clock::now() - start;
std::cout << elapsed.count() << "s}" << std::endl;
}
else
{
return_type result = block(args...);
elapsed = std::chrono::steady_clock::now() - start;
std::cout << elapsed.count() << "s}" << std::endl;
return result;
}
}
};