aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules4
-rw-r--r--contributing.md26
m---------lib/fontconfig0
-rw-r--r--readme.md1
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/crepe/api/CMakeLists.txt2
-rw-r--r--src/crepe/api/Config.h11
-rw-r--r--src/crepe/api/LoopManager.h4
-rw-r--r--src/crepe/api/Text.cpp12
-rw-r--r--src/crepe/api/Text.h66
-rw-r--r--src/crepe/facade/CMakeLists.txt4
-rw-r--r--src/crepe/facade/DB.cpp12
-rw-r--r--src/crepe/facade/DB.h2
-rw-r--r--src/crepe/facade/Font.cpp21
-rw-r--r--src/crepe/facade/Font.h42
-rw-r--r--src/crepe/facade/FontFacade.cpp43
-rw-r--r--src/crepe/facade/FontFacade.h34
-rw-r--r--src/crepe/facade/SDLContext.cpp10
-rw-r--r--src/crepe/facade/SDLContext.h16
-rw-r--r--src/crepe/manager/SaveManager.cpp49
-rw-r--r--src/crepe/manager/SaveManager.h6
-rw-r--r--src/crepe/system/RenderSystem.cpp26
-rw-r--r--src/crepe/system/RenderSystem.h10
-rw-r--r--src/doc/feature/proxy.dox43
-rw-r--r--src/doc/feature/savemgr.dox80
-rw-r--r--src/example/CMakeLists.txt2
-rw-r--r--src/example/FontExample.cpp55
-rw-r--r--src/example/loadfont.cpp49
-rw-r--r--src/test/DBTest.cpp8
-rw-r--r--src/test/InputTest.cpp7
-rw-r--r--src/test/SaveManagerTest.cpp17
-rw-r--r--src/test/main.cpp3
32 files changed, 620 insertions, 49 deletions
diff --git a/.gitmodules b/.gitmodules
index bd6e7f7..8155600 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -26,3 +26,7 @@
path = lib/whereami/lib
url = https://github.com/gpakosz/whereami
shallow = true
+[submodule "lib/fontconfig"]
+ path = lib/fontconfig
+ url = https://gitlab.freedesktop.org/fontconfig/fontconfig.git
+ shallow = true
diff --git a/contributing.md b/contributing.md
index 0faed2b..5555892 100644
--- a/contributing.md
+++ b/contributing.md
@@ -827,6 +827,32 @@ that you can click on to open them.
int bar = foo[0];
```
</td></tr></table></details>
+- <details><summary>
+ Always explicitly check against <code>NULL</code> (for C APIs) or
+ <code>nullptr</code> (for C++ APIs) when checking if a pointer is valid
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ string foo = "Hello world";
+ if (foo.c_str() == nullptr)
+ // ...
+
+ void * bar = malloc();
+ if (bar == NULL)
+ // ...
+ ```
+ </td><td>
+
+ ```cpp
+ string foo = "Hello world";
+ if (!foo.c_str())
+ // ...
+
+ void * bar = malloc();
+ if (!bar)
+ // ...
+ ```
+ </td></tr></table></details>
## CMakeLists-specific
diff --git a/lib/fontconfig b/lib/fontconfig
new file mode 160000
+Subproject 72b9a48f57de6204d99ce1c217b5609ee92ece9
diff --git a/readme.md b/readme.md
index d309b30..373c682 100644
--- a/readme.md
+++ b/readme.md
@@ -33,6 +33,7 @@ This project uses the following libraries
|Google Test (`GTest`)|1.15.2|
|Berkeley DB (`libdb`)|5.3.21|
|Where Am I?|(latest git `master` version)
+|fontconfig|2.15.0|
> [!NOTE]
> Most of these libraries are likely available from your package manager if you
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 97b21f0..696856c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,10 +9,12 @@ project(crepe C CXX)
find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED)
+find_package(SDL2_ttf REQUIRED)
find_package(SoLoud REQUIRED)
find_package(GTest REQUIRED)
find_package(whereami REQUIRED)
find_library(BERKELEY_DB db)
+find_library(FONTCONFIG_LIB fontconfig)
add_library(crepe SHARED)
add_executable(test_main EXCLUDE_FROM_ALL)
@@ -24,9 +26,11 @@ target_include_directories(crepe
target_link_libraries(crepe
PRIVATE soloud
PUBLIC SDL2
+ PUBLIC SDL2_ttf
PUBLIC SDL2_image
PUBLIC ${BERKELEY_DB}
PUBLIC whereami
+ PUBLIC ${FONTCONFIG_LIB}
)
add_subdirectory(crepe)
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index 8f84f06..e8d6f92 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -20,6 +20,7 @@ target_sources(crepe PUBLIC
Button.cpp
UIObject.cpp
AI.cpp
+ Text.cpp
Scene.cpp
)
@@ -51,4 +52,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Button.h
UIObject.h
AI.h
+ Text.h
)
diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h
index ed1cf38..6b9e3ca 100644
--- a/src/crepe/api/Config.h
+++ b/src/crepe/api/Config.h
@@ -76,6 +76,17 @@ struct Config final {
*/
std::string root_pattern = ".crepe-root";
} asset;
+ //! Default font options
+ struct {
+ /**
+ * \brief Default font size
+ *
+ * Using the SDL_ttf library the font size needs to be set when loading the font.
+ * This config option is the font size at which all fonts will be loaded initially.
+ *
+ */
+ unsigned int size = 16;
+ } font;
//! Configuration for click tolerance.
struct {
//! The maximum number of pixels the mouse can move between MouseDown and MouseUp events to be considered a click.
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index 40e6b38..1d23cbf 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -72,6 +72,8 @@ private:
//! Global context
Mediator mediator;
+ //! SDLContext instance
+ SDLContext sdl_context{mediator};
//! Component manager instance
ComponentManager component_manager{mediator};
//! Scene manager instance
@@ -84,8 +86,6 @@ private:
ResourceManager resource_manager{mediator};
//! Save manager instance
SaveManager save_manager{mediator};
- //! SDLContext instance
- SDLContext sdl_context{mediator};
private:
/**
diff --git a/src/crepe/api/Text.cpp b/src/crepe/api/Text.cpp
new file mode 100644
index 0000000..54a4370
--- /dev/null
+++ b/src/crepe/api/Text.cpp
@@ -0,0 +1,12 @@
+#include "../facade/FontFacade.h"
+
+#include "Text.h"
+
+using namespace crepe;
+
+Text::Text(game_object_id_t id, const vec2 & dimensions, const vec2 & offset,
+ const std::string & font_family, const Data & data, const std::string & text)
+ : UIObject(id, dimensions, offset),
+ text(text),
+ data(data),
+ font_family(font_family) {}
diff --git a/src/crepe/api/Text.h b/src/crepe/api/Text.h
new file mode 100644
index 0000000..c30dc80
--- /dev/null
+++ b/src/crepe/api/Text.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <optional>
+#include <string>
+
+#include "../Component.h"
+
+#include "Asset.h"
+#include "Color.h"
+#include "UIObject.h"
+
+namespace crepe {
+/**
+ * \brief Text UIObject component for displaying text
+ *
+ * This class can be used to display text on screen. By setting the font_family to a font already stored on the current device it will automatically be loaded in.
+ */
+class Text : public UIObject {
+public:
+ //! Text data that does not have to be set in the constructor
+ struct Data {
+ /**
+ * \brief fontsize for text rendering
+ *
+ * \note this is not the actual font size that is loaded in.
+ *
+ * Since SDL_TTF requires the font size when loading in the font it is not possible to switch the font size.
+ * The default font size that is loaded is set in the Config.
+ * Instead this value is used to upscale the font texture which can cause blurring or distorted text when upscaling or downscaling too much.
+ */
+ unsigned int font_size = 16;
+
+ //! Layer sorting level of the text
+ const int sorting_in_layer = 0;
+
+ //! Order within the sorting text
+ const int order_in_layer = 0;
+
+ //! Label text color.
+ Color text_color = Color::BLACK;
+ };
+
+public:
+ /**
+ *
+ * \param dimensions Width and height of the UIObject.
+ * \param offset Offset of the UIObject relative to its transform
+ * \param text The text to be displayed.
+ * \param font_family The font style name to be displayed.
+ * \param data Data struct containing extra text parameters.
+ * \param font Optional font asset that can be passed or left empty.
+ */
+ Text(game_object_id_t id, const vec2 & dimensions, const vec2 & offset,
+ const std::string & font_family, const Data & data, const std::string & text = "");
+
+ //! Label text.
+ std::string text = "";
+ //! font family name
+ std::string font_family = "";
+ //! Font asset variable if this is not set, it will use the font_family to create an asset.
+ std::optional<Asset> font;
+ //! Data instance
+ Data data;
+};
+
+} // namespace crepe
diff --git a/src/crepe/facade/CMakeLists.txt b/src/crepe/facade/CMakeLists.txt
index 0598e16..243ae46 100644
--- a/src/crepe/facade/CMakeLists.txt
+++ b/src/crepe/facade/CMakeLists.txt
@@ -4,6 +4,8 @@ target_sources(crepe PUBLIC
SoundContext.cpp
SDLContext.cpp
DB.cpp
+ FontFacade.cpp
+ Font.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -12,5 +14,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
SoundContext.h
SDLContext.h
DB.h
+ FontFacade.h
+ Font.h
)
diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp
index 95cf606..ae2d4bc 100644
--- a/src/crepe/facade/DB.cpp
+++ b/src/crepe/facade/DB.cpp
@@ -21,12 +21,6 @@ DB::DB(const string & path) {
const char * file = path.empty() ? NULL : path.c_str();
ret = this->db->open(this->db.get(), NULL, file, NULL, libdb::DB_BTREE, DB_CREATE, 0);
if (ret != 0) throw runtime_error(format("db->open: {}", libdb::db_strerror(ret)));
-
- // create cursor
- libdb::DBC * cursor;
- ret = this->db->cursor(this->db.get(), NULL, &cursor, 0);
- if (ret != 0) throw runtime_error(format("db->cursor: {}", libdb::db_strerror(ret)));
- this->cursor = {cursor, [](libdb::DBC * cursor) { cursor->close(cursor); }};
}
libdb::DBT DB::to_thing(const string & thing) const noexcept {
@@ -42,10 +36,10 @@ string DB::get(const string & key) {
libdb::DBT db_val;
memset(&db_val, 0, sizeof(libdb::DBT));
- int ret = this->cursor->get(this->cursor.get(), &db_key, &db_val, DB_FIRST);
+ int ret = this->db->get(this->db.get(), NULL, &db_key, &db_val, 0);
if (ret == 0) return {static_cast<char *>(db_val.data), db_val.size};
- string err = format("cursor->get: {}", libdb::db_strerror(ret));
+ string err = format("db->get: {}", libdb::db_strerror(ret));
if (ret == DB_NOTFOUND) throw out_of_range(err);
else throw runtime_error(err);
}
@@ -54,7 +48,7 @@ void DB::set(const string & key, const string & value) {
libdb::DBT db_key = this->to_thing(key);
libdb::DBT db_val = this->to_thing(value);
int ret = this->db->put(this->db.get(), NULL, &db_key, &db_val, 0);
- if (ret != 0) throw runtime_error(format("cursor->get: {}", libdb::db_strerror(ret)));
+ if (ret != 0) throw runtime_error(format("db->get: {}", libdb::db_strerror(ret)));
}
bool DB::has(const std::string & key) {
diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h
index 115c0f1..84cdf19 100644
--- a/src/crepe/facade/DB.h
+++ b/src/crepe/facade/DB.h
@@ -61,8 +61,6 @@ public:
private:
//! RAII wrapper around \c DB struct
std::unique_ptr<libdb::DB, std::function<void(libdb::DB *)>> db;
- //! RAII wrapper around \c DBC struct
- std::unique_ptr<libdb::DBC, std::function<void(libdb::DBC *)>> cursor;
private:
/**
diff --git a/src/crepe/facade/Font.cpp b/src/crepe/facade/Font.cpp
new file mode 100644
index 0000000..771002f
--- /dev/null
+++ b/src/crepe/facade/Font.cpp
@@ -0,0 +1,21 @@
+#include <SDL2/SDL_ttf.h>
+
+#include "../api/Asset.h"
+#include "../api/Config.h"
+
+#include "Font.h"
+
+using namespace std;
+using namespace crepe;
+
+Font::Font(const Asset & src, Mediator & mediator) : Resource(src, mediator) {
+ const Config & config = Config::get_instance();
+ const std::string FONT_PATH = src.get_path();
+ TTF_Font * loaded_font = TTF_OpenFont(FONT_PATH.c_str(), config.font.size);
+ if (loaded_font == NULL) {
+ throw runtime_error(format("Font: {} (path: {})", TTF_GetError(), FONT_PATH));
+ }
+ this->font = {loaded_font, [](TTF_Font * close_font) { TTF_CloseFont(close_font); }};
+}
+
+TTF_Font * Font::get_font() const { return this->font.get(); }
diff --git a/src/crepe/facade/Font.h b/src/crepe/facade/Font.h
new file mode 100644
index 0000000..b208d96
--- /dev/null
+++ b/src/crepe/facade/Font.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <SDL2/SDL_ttf.h>
+#include <memory>
+
+#include "../Resource.h"
+#include "../api/Config.h"
+
+namespace crepe {
+
+class Asset;
+/**
+ * \brief Resource for managing font creation and destruction
+ *
+ * This class is a wrapper around an SDL_ttf font instance, encapsulating font loading and usage.
+ * It loads a font from an Asset and manages its lifecycle. The font is automatically unloaded
+ * when this object is destroyed.
+ */
+class Font : public Resource {
+
+public:
+ /**
+ * \param src The Asset containing the font file path and metadata to load the font.
+ * \param mediator The Mediator object used for managing the SDL context or related systems.
+ */
+ Font(const Asset & src, Mediator & mediator);
+ /**
+ * \brief Gets the underlying TTF_Font resource.
+ *
+ * This function returns the raw pointer to the SDL_ttf TTF_Font object that represents
+ * the loaded font. This can be used with SDL_ttf functions to render text.
+ *
+ * \return The raw TTF_Font object wrapped in a unique pointer.
+ */
+ TTF_Font * get_font() const;
+
+private:
+ //! The SDL_ttf font object with custom deleter.
+ std::unique_ptr<TTF_Font, std::function<void(TTF_Font *)>> font = nullptr;
+};
+
+} // namespace crepe
diff --git a/src/crepe/facade/FontFacade.cpp b/src/crepe/facade/FontFacade.cpp
new file mode 100644
index 0000000..87f95ab
--- /dev/null
+++ b/src/crepe/facade/FontFacade.cpp
@@ -0,0 +1,43 @@
+#include <fontconfig/fontconfig.h>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include "FontFacade.h"
+
+using namespace std;
+using namespace crepe;
+
+FontFacade::FontFacade() {
+ if (!FcInit()) throw runtime_error("Failed to initialize Fontconfig.");
+}
+
+FontFacade::~FontFacade() { FcFini(); }
+
+Asset FontFacade::get_font_asset(const string & font_family) {
+ FcPattern * raw_pattern
+ = FcNameParse(reinterpret_cast<const FcChar8 *>(font_family.c_str()));
+ if (raw_pattern == NULL) throw runtime_error("Failed to create font pattern.");
+
+ unique_ptr<FcPattern, function<void(FcPattern *)>> pattern{
+ raw_pattern, [](FcPattern * p) { FcPatternDestroy(p); }};
+
+ FcConfig * config = FcConfigGetCurrent();
+ if (config == NULL) throw runtime_error("Failed to get current Fontconfig configuration.");
+
+ FcResult result;
+ FcPattern * raw_matched_pattern = FcFontMatch(config, pattern.get(), &result);
+ if (raw_matched_pattern == NULL) throw runtime_error("No matching font found.");
+
+ unique_ptr<FcPattern, function<void(FcPattern *)>> matched_pattern
+ = {raw_matched_pattern, [](FcPattern * p) { FcPatternDestroy(p); }};
+
+ FcChar8 * file_path = nullptr;
+ FcResult res = FcPatternGetString(matched_pattern.get(), FC_FILE, 0, &file_path);
+ if (res != FcResultMatch || file_path == NULL)
+ throw runtime_error("Failed to get font file path.");
+
+ string font_file_path = reinterpret_cast<const char *>(file_path);
+ return Asset(font_file_path);
+}
diff --git a/src/crepe/facade/FontFacade.h b/src/crepe/facade/FontFacade.h
new file mode 100644
index 0000000..9761070
--- /dev/null
+++ b/src/crepe/facade/FontFacade.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <memory>
+
+#include "../api/Asset.h"
+
+namespace crepe {
+
+/**
+ *
+ * \brief Font facade class for converting font family names to absolute file paths
+ *
+ */
+class FontFacade {
+public:
+ FontFacade();
+ ~FontFacade();
+ FontFacade(const FontFacade & other) = delete;
+ FontFacade & operator=(const FontFacade & other) = delete;
+ FontFacade(FontFacade && other) noexcept = delete;
+ FontFacade & operator=(FontFacade && other) noexcept = delete;
+ /**
+ *
+ * \brief Facade function to convert a font_family into an asset.
+ *
+ * This function uses the FontConfig library to convert a font family name (Arial, Inter, Helvetica) and converts it to the font source path.
+ * This function returns a default font path if the font_family name doesnt exist or cant be found
+ * \param font_family Name of the font family name.
+ * \return Asset with filepath to the corresponding font.
+ */
+ Asset get_font_asset(const std::string & font_family);
+};
+
+} // namespace crepe
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index f331517..fffbe34 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -6,6 +6,8 @@
#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_surface.h>
+#include <SDL2/SDL_ttf.h>
+#include <SDL2/SDL_video.h>
#include <array>
#include <cmath>
#include <cstddef>
@@ -30,6 +32,9 @@ using namespace std;
SDLContext::SDLContext(Mediator & mediator) {
dbg_trace();
+ if (TTF_Init() == -1) {
+ throw runtime_error(format("SDL_ttf initialization failed: {}", TTF_GetError()));
+ }
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError()));
}
@@ -71,6 +76,7 @@ SDLContext::~SDLContext() {
// thread that SDL_Init() was called on? This has caused problems for me
// before.
IMG_Quit();
+ TTF_Quit();
SDL_Quit();
}
@@ -407,3 +413,7 @@ void SDLContext::set_color_texture(const Texture & texture, const Color & color)
SDL_SetTextureColorMod(texture.get_img(), color.r, color.g, color.b);
SDL_SetTextureAlphaMod(texture.get_img(), color.a);
}
+
+Asset SDLContext::get_font_from_name(const std::string & font_family) {
+ return this->font_facade.get_font_asset(font_family);
+}
diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h
index b9c7fbd..b687f87 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -17,8 +17,10 @@
#include "api/KeyCodes.h"
#include "api/Sprite.h"
#include "api/Transform.h"
+#include "types.h"
#include "EventData.h"
+#include "FontFacade.h"
namespace crepe {
class Texture;
@@ -242,6 +244,20 @@ private:
CameraAuxiliaryData cam_aux_data;
private:
+ //! instance of the font_facade
+ FontFacade font_facade{};
+
+public:
+ /**
+ * \brief Function to Get asset from font_family
+ *
+ * This function uses the FontFacade function to convert a font_family to an asset.
+ *
+ * \param font_family name of the font style that needs to be used (will return an asset with default font path of the font_family doesnt exist)
+ *
+ * \return asset with the font style absolute path
+ */
+ Asset get_font_from_name(const std::string & font_family);
//! variable to store the state of each key (true = pressed, false = not pressed)
keyboard_state_t keyboard_state;
//! lookup table for converting SDL_SCANCODES to Keycodes
diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp
index 691ea2f..f313ed2 100644
--- a/src/crepe/manager/SaveManager.cpp
+++ b/src/crepe/manager/SaveManager.cpp
@@ -129,9 +129,32 @@ template void SaveManager::set(const string &, const float &);
template void SaveManager::set(const string &, const double &);
template <typename T>
+T SaveManager::get(const string & key) {
+ return this->deserialize<T>(this->get_db().get(key));
+}
+template uint8_t SaveManager::get(const string &);
+template int8_t SaveManager::get(const string &);
+template uint16_t SaveManager::get(const string &);
+template int16_t SaveManager::get(const string &);
+template uint32_t SaveManager::get(const string &);
+template int32_t SaveManager::get(const string &);
+template uint64_t SaveManager::get(const string &);
+template int64_t SaveManager::get(const string &);
+template float SaveManager::get(const string &);
+template double SaveManager::get(const string &);
+template string SaveManager::get(const string &);
+
+template <typename T>
ValueBroker<T> SaveManager::get(const string & key, const T & default_value) {
if (!this->has(key)) this->set<T>(key, default_value);
- return this->get<T>(key);
+ T value;
+ return {
+ [this, key](const T & target) { this->set<T>(key, target); },
+ [this, key, value]() mutable -> const T & {
+ value = this->get<T>(key);
+ return value;
+ },
+ };
}
template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &);
template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &);
@@ -144,27 +167,3 @@ template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &);
template ValueBroker<float> SaveManager::get(const string &, const float &);
template ValueBroker<double> SaveManager::get(const string &, const double &);
template ValueBroker<string> SaveManager::get(const string &, const string &);
-
-template <typename T>
-ValueBroker<T> SaveManager::get(const string & key) {
- T value;
- return {
- [this, key](const T & target) { this->set<T>(key, target); },
- [this, key, value]() mutable -> const T & {
- DB & db = this->get_db();
- value = this->deserialize<T>(db.get(key));
- return value;
- },
- };
-}
-template ValueBroker<uint8_t> SaveManager::get(const string &);
-template ValueBroker<int8_t> SaveManager::get(const string &);
-template ValueBroker<uint16_t> SaveManager::get(const string &);
-template ValueBroker<int16_t> SaveManager::get(const string &);
-template ValueBroker<uint32_t> SaveManager::get(const string &);
-template ValueBroker<int32_t> SaveManager::get(const string &);
-template ValueBroker<uint64_t> SaveManager::get(const string &);
-template ValueBroker<int64_t> SaveManager::get(const string &);
-template ValueBroker<float> SaveManager::get(const string &);
-template ValueBroker<double> SaveManager::get(const string &);
-template ValueBroker<string> SaveManager::get(const string &);
diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h
index 61a978d..1e34bc0 100644
--- a/src/crepe/manager/SaveManager.h
+++ b/src/crepe/manager/SaveManager.h
@@ -36,17 +36,17 @@ public:
ValueBroker<T> get(const std::string & key, const T & default_value);
/**
- * \brief Get a read/write reference to a value
+ * \brief Get a value directly
*
* \param key The value key
*
- * \return Read/write reference to the value
+ * \return The value
*
* \note Attempting to read this value before it is initialized (i.e. set) will result in an
* exception
*/
template <typename T>
- ValueBroker<T> get(const std::string & key);
+ T get(const std::string & key);
/**
* \brief Set a value directly
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index 505433a..62d42ec 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -2,13 +2,16 @@
#include <cassert>
#include <cmath>
#include <functional>
+#include <optional>
#include <stdexcept>
#include <vector>
#include "../api/Camera.h"
#include "../api/ParticleEmitter.h"
#include "../api/Sprite.h"
+#include "../api/Text.h"
#include "../api/Transform.h"
+#include "../facade/Font.h"
#include "../facade/SDLContext.h"
#include "../facade/Texture.h"
#include "../manager/ComponentManager.h"
@@ -120,8 +123,14 @@ void RenderSystem::render() {
this->update_camera();
RefVector<Sprite> sprites = mgr.get_components_by_type<Sprite>();
+ ResourceManager & resource_manager = this->mediator.resource_manager;
RefVector<Sprite> sorted_sprites = this->sort(sprites);
-
+ RefVector<Text> text_components = mgr.get_components_by_type<Text>();
+ for (Text & text : text_components) {
+ const Transform & transform
+ = mgr.get_components_by_id<Transform>(text.game_object_id).front().get();
+ this->render_text(text, transform);
+ }
for (const Sprite & sprite : sorted_sprites) {
if (!sprite.active) continue;
const Transform & transform
@@ -134,3 +143,18 @@ void RenderSystem::render() {
this->render_normal(sprite, transform);
}
}
+void RenderSystem::render_text(Text & text, const Transform & tm) {
+ SDLContext & ctx = this->mediator.sdl_context;
+
+ if (!text.font.has_value()) {
+ text.font.emplace(ctx.get_font_from_name(text.font_family));
+ }
+
+ ResourceManager & resource_manager = this->mediator.resource_manager;
+
+ if (!text.font.has_value()) {
+ return;
+ }
+ const Asset & font_asset = text.font.value();
+ const Font & res = resource_manager.get<Font>(font_asset);
+}
diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h
index fc7b46e..56a0553 100644
--- a/src/crepe/system/RenderSystem.h
+++ b/src/crepe/system/RenderSystem.h
@@ -10,7 +10,7 @@ namespace crepe {
class Camera;
class Sprite;
class Transform;
-
+class Text;
/**
* \brief Manages rendering operations for all game objects.
*
@@ -50,7 +50,13 @@ private:
* \return true if particles have been rendered
*/
bool render_particle(const Sprite & sprite, const double & scale);
-
+ /**
+ * \brief Renders all Text components
+ *
+ * \param text The text component to be rendered.
+ * \param tm the Transform component that holds the position,rotation and scale
+ */
+ void render_text(Text & text, const Transform & tm);
/**
* \brief renders a sprite with a Transform component on the screen
*
diff --git a/src/doc/feature/proxy.dox b/src/doc/feature/proxy.dox
new file mode 100644
index 0000000..66bbd2f
--- /dev/null
+++ b/src/doc/feature/proxy.dox
@@ -0,0 +1,43 @@
+// vim:ft=doxygen
+namespace crepe {
+/**
+
+\defgroup feature_proxy Proxy utility
+\ingroup feature
+\brief Use ValueBroker as if it were a regular variable
+
+Proxy provides operators that allow you to use a ValueBroker instance as if it
+were a regular variable. Proxy implements a constructor that allows it to be
+used as a substitute return type for any function that returns ValueBroker.
+
+\see ValueBroker
+\see Proxy
+
+\par Example
+
+```cpp
+#include <crepe/util/Proxy.h>
+#include <crepe/ValueBroker.h>
+
+int calculation(int value) {
+ return 3 * value;
+}
+
+void anywhere() {
+ crepe::ValueBroker<int> foo_handle;
+ crepe::Proxy foo = foo_handle;
+
+ // implicitly calls .set()
+ foo += 10;
+
+ // implicitly calls .get()
+ int out = calculation(foo);
+
+ // explicitly cast (also calls .get())
+ int casted = int(foo);
+}
+
+```
+
+*/
+}
diff --git a/src/doc/feature/savemgr.dox b/src/doc/feature/savemgr.dox
new file mode 100644
index 0000000..6aeab03
--- /dev/null
+++ b/src/doc/feature/savemgr.dox
@@ -0,0 +1,80 @@
+// vim:ft=doxygen
+namespace crepe {
+/**
+
+\defgroup feature_savemgr Save data
+\ingroup feature
+\brief Functions to persistently store and retrieve arbitrary values
+
+The SaveManager may be used to persistently store game state such as player
+progress, statistics, achievements, etc. It works like a key-value store, where
+the key is a string and the value is an arbitrary type.
+
+SaveManager implements the following:
+
+- Storage and retrieval of primitive types and strings.
+- Automatic initialization of the database using default values.
+- The underlying database format is journaled, which reduces the likelihood of
+ players losing save data when an unexpected crash occurs while the SaveManager
+ is writing to disk.
+
+\see SaveManager
+
+\par Example
+
+The SaveManager instance reference may be retrieved by calling \c
+get_save_manager(). This function is available---
+
+- Within (derivatives of) Script
+
+- \todo Within (derivatives of) Scene
+
+- \todo As a public member function of LoopManager
+
+```cpp
+// Retrieve save manager
+crepe::SaveManager & save_manager = get_save_manager();
+```
+
+SaveManager may be used *explicitly*, using the \ref SaveManager::set "set()",
+\ref SaveManager::get "get()" and \ref SaveManager::has "has()" methods:
+```cpp
+// Check if the key "foo" exists, and initialize it to 3 if it doesn't
+if (!save_manager.has("foo"))
+ save_manager.set<int>("foo", 3);
+// Get value of key "foo"
+int foo = save_manager.get<int>("foo");
+
+// ~~~ arbitrary game code ~~~
+foo += 10;
+// ~~~ arbitrary game code ~~~
+
+// Save "foo" back to database
+save_manager.set<int>("foo", foo);
+```
+
+Alternatively, SaveManager::get may be called with a default value as second
+parameter. This changes its return type to ValueBroker, which acts as a
+read/write handle to the specific key requested, and remembers the key and its
+value type for you:
+```cpp
+// Get a read/write handle to the value stored in key "foo", and initialize it
+// to 3 if it doesn't exist yet
+ValueBroker foo_handle = save_manager.get<int>("foo", 3);
+int foo = foo_handle.get();
+
+// ~~~ arbitrary game code ~~~
+foo += 10;
+// ~~~ arbitrary game code ~~~
+
+// Save back to database
+foo_handle.set(foo);
+```
+
+To further simplify game code, the return value of SaveManager::get may be
+implicitly cast to Proxy instead of ValueBroker. This allows the database value
+to be used as if it were a regular variable. This usage is detailed separately
+in \"\ref feature_proxy\".
+
+*/
+}
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index 187ed46..f62414e 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -19,4 +19,6 @@ endfunction()
add_example(rendering_particle)
add_example(game)
add_example(button)
+add_example(loadfont)
+add_example(FontExample)
add_example(AITest)
diff --git a/src/example/FontExample.cpp b/src/example/FontExample.cpp
new file mode 100644
index 0000000..6a334b1
--- /dev/null
+++ b/src/example/FontExample.cpp
@@ -0,0 +1,55 @@
+#include <SDL2/SDL_ttf.h>
+#include <chrono>
+#include <crepe/api/Camera.h>
+#include <crepe/api/Config.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/LoopManager.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/Script.h>
+#include <crepe/api/Text.h>
+#include <crepe/facade/Font.h>
+#include <crepe/facade/SDLContext.h>
+#include <crepe/manager/EventManager.h>
+#include <crepe/manager/Mediator.h>
+#include <crepe/manager/ResourceManager.h>
+#include <exception>
+#include <iostream>
+#include <memory>
+using namespace crepe;
+using namespace std;
+using namespace std::chrono;
+class TestScript : public Script {
+public:
+ steady_clock::time_point start_time;
+ virtual void init() override { start_time = steady_clock::now(); }
+ virtual void update() override {
+ auto now = steady_clock::now();
+ auto elapsed = duration_cast<seconds>(now - start_time).count();
+
+ if (elapsed >= 5) {
+ Mediator & med = mediator;
+ EventManager & event_mgr = med.event_manager;
+ event_mgr.trigger_event<ShutDownEvent>();
+ }
+ }
+};
+class TestScene : public Scene {
+public:
+ void load_scene() override {
+ GameObject text_object = this->new_object("test", "test", vec2{0, 0}, 0, 1);
+ text_object.add_component<Text>(vec2(100, 100), vec2(0, 0), "OpenSymbol",
+ Text::Data{});
+ text_object.add_component<BehaviorScript>().set_script<TestScript>();
+ text_object.add_component<Camera>(ivec2{300, 300}, vec2{100, 100}, Camera::Data{});
+ }
+ std::string get_name() const override { return "hey"; }
+};
+int main() {
+ // Config& config = Config::get_instance();
+ // config.log.level = Log::Level::TRACE;
+ LoopManager engine;
+ engine.add_scene<TestScene>();
+ engine.start();
+
+ return 0;
+}
diff --git a/src/example/loadfont.cpp b/src/example/loadfont.cpp
new file mode 100644
index 0000000..e459332
--- /dev/null
+++ b/src/example/loadfont.cpp
@@ -0,0 +1,49 @@
+#include <SDL2/SDL_ttf.h>
+#include <crepe/api/Asset.h>
+#include <crepe/api/Text.h>
+#include <crepe/facade/Font.h>
+#include <crepe/facade/FontFacade.h>
+#include <crepe/facade/SDLContext.h>
+#include <crepe/manager/Mediator.h>
+#include <crepe/manager/ResourceManager.h>
+#include <exception>
+#include <iostream>
+#include <memory>
+#include <optional>
+using namespace crepe;
+int main() {
+
+ // SDLFontContext font_facade;
+ Mediator mediator;
+ FontFacade font_facade{};
+ SDLContext sdl_context{mediator};
+ // ComponentManager component_manager{mediator};
+ ResourceManager resource_manager{mediator};
+ try {
+ // Correct way to create a unique pointer for Text
+ std::unique_ptr<Text> label = std::make_unique<Text>(
+ 1, vec2(100, 100), vec2(0, 0), "OpenSymbol", Text::Data{}, "test text");
+ // std::cout << "Path: " << label->font.get_path() << std::endl;
+ Asset asset1 = font_facade.get_font_asset("OpenSymbol");
+ std::cout << asset1.get_path() << std::endl;
+ std::unique_ptr<Text> label2 = std::make_unique<Text>(
+ 1, vec2(100, 100), vec2(0, 0), "fsaafdafsdafsdafsdasfdds", Text::Data{});
+ Asset asset = Asset("test test");
+ label->font.emplace(asset);
+ std::cout << label->font.value().get_path() << std::endl;
+ // label2->font = std::make_optional(asset);
+ // std::cout << "Path: " << label2->font.get_path() << std::endl;
+ ResourceManager & resource_mgr = mediator.resource_manager;
+ const Font & res = resource_manager.get<Font>(label->font.value());
+ // TTF_Font * test_font = res.get_font();
+ // if (test_font == NULL) {
+ // std::cout << "error with font" << std::endl;
+ // } else {
+ // std::cout << "correct font retrieved" << std::endl;
+ // }
+ } catch (const std::exception & e) {
+ std::cout << "Standard exception thrown: " << e.what() << std::endl;
+ }
+
+ return 0;
+}
diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp
index 99dedff..7f2c339 100644
--- a/src/test/DBTest.cpp
+++ b/src/test/DBTest.cpp
@@ -27,3 +27,11 @@ TEST_F(DBTest, Has) {
db.set("foo", "bar");
EXPECT_EQ(db.has("foo"), true);
}
+
+TEST_F(DBTest, MultipleKeys) {
+ db.set("foo", "foo");
+ db.set("bar", "bar");
+
+ EXPECT_EQ(db.get("foo"), "foo");
+ EXPECT_EQ(db.get("bar"), "bar");
+}
diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp
index d893276..2d844d4 100644
--- a/src/test/InputTest.cpp
+++ b/src/test/InputTest.cpp
@@ -1,7 +1,11 @@
-#include "system/RenderSystem.h"
#include <gtest/gtest.h>
+
+#include <crepe/manager/ResourceManager.h>
+#include <crepe/system/RenderSystem.h>
+
#define protected public
#define private public
+
#include "api/KeyCodes.h"
#include "manager/ComponentManager.h"
#include "manager/EventManager.h"
@@ -29,6 +33,7 @@ public:
SDLContext sdl_context{mediator};
InputSystem input_system{mediator};
+ ResourceManager resman{mediator};
RenderSystem render{mediator};
EventManager event_manager{mediator};
//GameObject camera;
diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp
index e9b0c29..7609e69 100644
--- a/src/test/SaveManagerTest.cpp
+++ b/src/test/SaveManagerTest.cpp
@@ -27,8 +27,8 @@ TEST_F(SaveManagerTest, ReadWrite) {
mgr.set<string>("foo", "bar");
ASSERT_TRUE(mgr.has("foo"));
- ValueBroker value = mgr.get<string>("foo");
- EXPECT_EQ(value.get(), "bar");
+ string value = mgr.get<string>("foo");
+ EXPECT_EQ(value, "bar");
}
TEST_F(SaveManagerTest, DefaultValue) {
@@ -36,5 +36,16 @@ TEST_F(SaveManagerTest, DefaultValue) {
ASSERT_EQ(value.get(), 3);
value.set(5);
- ASSERT_EQ(value.get(), 5);
+ EXPECT_EQ(value.get(), 5);
+}
+
+TEST_F(SaveManagerTest, MultipleKeys) {
+ ValueBroker foo = mgr.get<int>("foo", 1);
+ ValueBroker bar = mgr.get<int>("bar", 2);
+
+ EXPECT_EQ(foo.get(), 1);
+ EXPECT_EQ(bar.get(), 2);
+
+ EXPECT_EQ(mgr.get<int>("foo"), 1);
+ EXPECT_EQ(mgr.get<int>("bar"), 2);
}
diff --git a/src/test/main.cpp b/src/test/main.cpp
index ed2aed5..0e1bc75 100644
--- a/src/test/main.cpp
+++ b/src/test/main.cpp
@@ -1,6 +1,7 @@
-#include <crepe/api/Config.h>
#include <gtest/gtest.h>
+#include <crepe/api/Config.h>
+
using namespace crepe;
using namespace testing;