diff --git a/assets/config.json b/assets/config.json new file mode 100644 index 0000000..e49536f --- /dev/null +++ b/assets/config.json @@ -0,0 +1,17 @@ +{ + "sounds": { + "ui_click": "assets/sounds/ui_click.ogg", + "ui_hover": "assets/sounds/ui_hover.ogg", + "blank": "assets/sounds/blank.ogg" + }, + "sprites": { + "textures_test": + {"path": "assets/textures_test.png", + "frame_width": 53, + "frame_height": 34 + } + }, + "graphics": { + "smooth_textures": false + } +} diff --git a/assets/shaders.json b/assets/shaders.json new file mode 100644 index 0000000..7a03ad7 --- /dev/null +++ b/assets/shaders.json @@ -0,0 +1,22 @@ +{ + "ui_shader": { + "file_name": "assets/shaders/ui_shader.frag", + "type": "fragment" + }, + "ERROR": { + "file_name": "assets/shaders/ui_error.frag", + "type": "fragment" + }, + "rayview_sprites": { + "file_name": "assets/shaders/rayview_sprites.frag", + "type": "fragment" + }, + "flame": { + "file_name": "assets/shaders/flame_trash.frag", + "type": "fragment" + }, + "lightning": { + "file_name": "assets/shaders/lightning_attack.frag", + "type": "fragment" + } +} diff --git a/assets/shaders/ui_error.frag b/assets/shaders/ui_error.frag new file mode 100644 index 0000000..29ccc8c --- /dev/null +++ b/assets/shaders/ui_error.frag @@ -0,0 +1,18 @@ +uniform vec2 u_resolution; +uniform vec2 u_mouse; +uniform float u_duration; +uniform float u_time; +uniform float u_time_end; +uniform sampler2D texture; +uniform bool is_shape; + +void main() { + if(is_shape) { + vec4 color = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragColor = gl_Color * color; + } else { + vec4 pixel = texture2D(texture, gl_TexCoord[0].xy); + vec4 color = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragColor = gl_Color * color * pixel; + } +} diff --git a/assets/shaders/ui_shader.frag b/assets/shaders/ui_shader.frag new file mode 100644 index 0000000..73b77b4 --- /dev/null +++ b/assets/shaders/ui_shader.frag @@ -0,0 +1,29 @@ +uniform vec2 u_resolution; +uniform vec2 u_mouse; +uniform float u_duration; +uniform float u_time; +uniform float u_time_end; +uniform sampler2D texture; +uniform bool is_shape; +uniform bool hover; + +vec4 blink() { + if(hover) { + return vec4(0.95, 0.95, 1.0, 1.0); + } else { + float tick = (u_time_end - u_time) / u_duration; + float blink = mix(0.5, 1.0, tick); + return vec4(blink, blink, blink, 1.0); + } +} + +void main() { + vec4 color = blink(); + + if(!is_shape) { + vec4 pixel = texture2D(texture, gl_TexCoord[0].xy); + color *= pixel; + } + + gl_FragColor = gl_Color * color; +} diff --git a/assets/shaders/ui_shape_shader.frag b/assets/shaders/ui_shape_shader.frag new file mode 100644 index 0000000..c16d6ea --- /dev/null +++ b/assets/shaders/ui_shape_shader.frag @@ -0,0 +1,12 @@ +uniform vec2 u_resolution; +uniform vec2 u_mouse; +uniform float u_duration; +uniform float u_time; +uniform float u_time_end; + +void main() { + float tick = (u_time_end - u_time) / u_duration; + float blink = smoothstep(1.0, 0.5, tick); + vec4 color = vec4(blink, blink, blink, 1.0); + gl_FragColor = gl_Color * color; +} diff --git a/assets/sounds/blank.ogg b/assets/sounds/blank.ogg new file mode 100644 index 0000000..3322d4b Binary files /dev/null and b/assets/sounds/blank.ogg differ diff --git a/assets/sounds/ui_click.ogg b/assets/sounds/ui_click.ogg new file mode 100644 index 0000000..7ff2e8c Binary files /dev/null and b/assets/sounds/ui_click.ogg differ diff --git a/assets/sounds/ui_hover.ogg b/assets/sounds/ui_hover.ogg new file mode 100644 index 0000000..be6e679 Binary files /dev/null and b/assets/sounds/ui_hover.ogg differ diff --git a/assets/text.otf b/assets/text.otf new file mode 100644 index 0000000..3094772 Binary files /dev/null and b/assets/text.otf differ diff --git a/assets/textures_test.png b/assets/textures_test.png new file mode 100644 index 0000000..1ffb41e Binary files /dev/null and b/assets/textures_test.png differ diff --git a/color.hpp b/color.hpp new file mode 100644 index 0000000..7815893 --- /dev/null +++ b/color.hpp @@ -0,0 +1,15 @@ +#pragma once +#include + +namespace ColorValue { + constexpr const sf::Color BLACK{0, 0, 0}; + constexpr const sf::Color DARK_DARK{10, 10, 10}; + constexpr const sf::Color DARK_MID{30, 30, 30}; + constexpr const sf::Color DARK_LIGHT{60, 60, 60}; + constexpr const sf::Color MID{100, 100, 100}; + constexpr const sf::Color LIGHT_DARK{150, 150, 150}; + constexpr const sf::Color LIGHT_MID{200, 200, 200}; + constexpr const sf::Color LIGHT_LIGHT{230, 230, 230}; + constexpr const sf::Color WHITE{255, 255, 255}; + constexpr const sf::Color TRANSPARENT = sf::Color::Transparent; +} diff --git a/config.cpp b/config.cpp new file mode 100644 index 0000000..ea3ef62 --- /dev/null +++ b/config.cpp @@ -0,0 +1,35 @@ +#include "config.hpp" +#include "dbc.hpp" +#include + +using nlohmann::json; +using fmt::format; + +Config::Config(const std::string src_path) : $src_path(src_path) { + std::ifstream infile($src_path); + $config = json::parse(infile); +} + +json &Config::operator[](const std::string &key) { + dbc::check($config.contains(key), fmt::format("ERROR in config, key {} doesn't exist.", key)); + return $config[key]; +} + +std::wstring Config::wstring(const std::string main_key, const std::string sub_key) { + dbc::check($config.contains(main_key), fmt::format("ERROR wstring main/key in config, main_key {} doesn't exist.", main_key)); + dbc::check($config[main_key].contains(sub_key), fmt::format("ERROR wstring in config, main_key/key {}/{} doesn't exist.", main_key, sub_key)); + + const std::string& str_val = $config[main_key][sub_key]; + std::wstring_convert> $converter; + return $converter.from_bytes(str_val); +} + +std::vector Config::keys() { + std::vector the_fucking_keys; + + for(auto& [key, value] : $config.items()) { + the_fucking_keys.push_back(key); + } + + return the_fucking_keys; +} diff --git a/config.hpp b/config.hpp new file mode 100644 index 0000000..52bf664 --- /dev/null +++ b/config.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +#include +#include + +struct Config { + nlohmann::json $config; + std::string $src_path; + + Config(const std::string src_path); + + Config(nlohmann::json config, std::string src_path) + : $config(config), $src_path(src_path) {} + + nlohmann::json &operator[](const std::string &key); + nlohmann::json &json() { return $config; }; + std::wstring wstring(const std::string main_key, const std::string sub_key); + std::vector keys(); +}; diff --git a/constants.hpp b/constants.hpp new file mode 100644 index 0000000..5372164 --- /dev/null +++ b/constants.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +constexpr const int TEXTURE_WIDTH=256; +constexpr const int TEXTURE_HEIGHT=256; +constexpr const int SCREEN_WIDTH=1280; +constexpr const int SCREEN_HEIGHT=720; + +constexpr const bool VSYNC=false; +constexpr const int FRAME_LIMIT=60; +constexpr const int NUM_SPRITES=1; +constexpr const int MAX_LOG_MESSAGES=17; + +#ifdef NDEBUG +constexpr const bool DEBUG_BUILD=false; +#else +constexpr const bool DEBUG_BUILD=true; +#endif diff --git a/shaders.cpp b/shaders.cpp new file mode 100644 index 0000000..dbc161f --- /dev/null +++ b/shaders.cpp @@ -0,0 +1,78 @@ +#include "shaders.hpp" +#include +#include "dbc.hpp" +#include +#include "config.hpp" +#include "constants.hpp" +#include + +namespace shaders { + using std::shared_ptr, std::make_shared; + + static ShaderManager SMGR; + static bool INITIALIZED = false; + static int VERSION = 0; + + inline void configure_shader_defaults(std::shared_ptr ptr) { + ptr->setUniform("source", sf::Shader::CurrentTexture); + } + + bool load_shader(std::string name, nlohmann::json& settings) { + std::string file_name = settings["file_name"]; + auto ptr = std::make_shared(); + bool good = ptr->loadFromFile(file_name, sf::Shader::Type::Fragment); + + if(good) { + configure_shader_defaults(ptr); + SMGR.shaders.try_emplace(name, name, file_name, ptr); + } + return good; + } + + void init() { + if(!INITIALIZED) { + dbc::check(sf::Shader::isAvailable(), "no shaders?!"); + INITIALIZED = true; + Config config("assets/shaders.json"); + bool good = load_shader("ERROR", config["ERROR"]); + dbc::check(good, "Failed to load ERROR shader. Look in assets/shaders.json"); + + for(auto& [name, settings] : config.json().items()) { + if(name == "ERROR") continue; + + dbc::check(!SMGR.shaders.contains(name), + fmt::format("shader name '{}' duplicated in assets/shaders.json", name)); + good = load_shader(name, settings); + + if(!good) { + dbc::log(fmt::format("failed to load shader {}", name)); + SMGR.shaders.insert_or_assign(name, SMGR.shaders.at("ERROR")); + } + } + } + } + + std::shared_ptr get(const std::string& name) { + dbc::check(INITIALIZED, "you forgot to shaders::init()"); + dbc::check(SMGR.shaders.contains(name), + fmt::format("shader name '{}' not in assets/shaders.json", name)); + auto& rec = SMGR.shaders.at(name); + return rec.ptr; + } + + int reload() { + VERSION++; + INITIALIZED = false; + SMGR.shaders.clear(); + init(); + return VERSION; + } + + bool updated(int my_version) { + return my_version != VERSION; + } + + int version() { + return VERSION; + } +}; diff --git a/shaders.hpp b/shaders.hpp new file mode 100644 index 0000000..33791d9 --- /dev/null +++ b/shaders.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace shaders { + struct Record { + std::string name; + std::string file_name; + std::shared_ptr ptr = nullptr; + }; + + struct ShaderManager { + std::unordered_map shaders; + }; + + std::shared_ptr get(const std::string& name); + void init(); + bool load_shader(std::string& name, nlohmann::json& settings); + bool updated(int my_version); + int reload(); + int version(); +} diff --git a/sound.cpp b/sound.cpp new file mode 100644 index 0000000..cf8c48a --- /dev/null +++ b/sound.cpp @@ -0,0 +1,82 @@ +#include "sound.hpp" +#include "dbc.hpp" +#include +#include "config.hpp" + +namespace sound { + static SoundManager SMGR; + static bool initialized = false; + static bool muted = false; + + using namespace fmt; + using std::make_shared; + namespace fs = std::filesystem; + + SoundPair& get_sound_pair(const std::string& name) { + dbc::check(initialized, "You need to call sound::init() first"); + + if(SMGR.sounds.contains(name)) { + // get the sound from the sound map + return SMGR.sounds.at(name); + } else { + dbc::log(fmt::format("Attempted to stop {} sound but not available.", + name)); + return SMGR.sounds.at("blank"); + } + } + + + void init() { + if(!initialized) { + Config assets("assets/config.json"); + + for(auto& el : assets["sounds"].items()) { + load(el.key(), el.value()); + } + initialized = true; + } + } + + void load(const std::string& name, const std::string& sound_path) { + dbc::check(fs::exists(sound_path), fmt::format("sound file {} does not exist", sound_path)); + + // create the buffer and keep in the buffer map + auto buffer = make_shared(sound_path); + + // set it on the sound and keep in the sound map + auto sound = make_shared(*buffer); + sound->setRelativeToListener(false); + sound->setPosition({0.0f, 0.0f, 1.0f}); + + SMGR.sounds.try_emplace(name, buffer, sound); + } + + void play(const std::string& name, bool loop) { + if(muted) return; + auto& pair = get_sound_pair(name); + pair.sound->setLooping(loop); + // play it + pair.sound->play(); + } + + void stop(const std::string& name) { + auto& pair = get_sound_pair(name); + pair.sound->stop(); + } + + bool playing(const std::string& name) { + auto& pair = get_sound_pair(name); + auto status = pair.sound->getStatus(); + return status == sf::SoundSource::Status::Playing; + } + + void play_at(const std::string& name, float x, float y, float z) { + auto& pair = get_sound_pair(name); + pair.sound->setPosition({x, y, z}); + pair.sound->play(); + } + + void mute(bool setting) { + muted = setting; + } +} diff --git a/sound.hpp b/sound.hpp new file mode 100644 index 0000000..3dc21cc --- /dev/null +++ b/sound.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace sound { + struct SoundPair { + std::shared_ptr buffer; + std::shared_ptr sound; + }; + + struct SoundManager { + std::unordered_map sounds; + }; + + void init(); + void load(const std::string& name, const std::string& path); + void play(const std::string& name, bool loop=false); + void play_at(const std::string& name, float x, float y, float z); + void stop(const std::string& name); + void mute(bool setting); + bool playing(const std::string& name); + SoundPair& get_sound_pair(const std::string& name); +} diff --git a/tests/dbc.cpp b/tests/dbc.cpp new file mode 100644 index 0000000..5c7263d --- /dev/null +++ b/tests/dbc.cpp @@ -0,0 +1,18 @@ +#include +#include "dbc.hpp" + +using namespace dbc; + +TEST_CASE("basic feature tests", "[dbc]") { + log("Logging a message."); + + pre("confirm positive cases work", 1 == 1); + + pre("confirm positive lambda", [&]{ return 1 == 1;}); + + post("confirm positive post", 1 == 1); + + post("confirm postitive post with lamdba", [&]{ return 1 == 1;}); + + check(1 == 1, "one equals 1"); +} diff --git a/tests/shaders.cpp b/tests/shaders.cpp new file mode 100644 index 0000000..2b210ff --- /dev/null +++ b/tests/shaders.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include "shaders.hpp" + +using namespace fmt; + +TEST_CASE("shader loading/init works", "[shaders]") { + shaders::init(); + int version = shaders::version(); + + std::shared_ptr ui_shader = shaders::get("ui_shader"); + auto other_test = shaders::get("ui_shader"); + + REQUIRE(ui_shader != nullptr); + REQUIRE(ui_shader == other_test); + REQUIRE(shaders::updated(version) == false); + + int new_version = shaders::reload(); + REQUIRE(version != shaders::version()); + REQUIRE(version != new_version); + REQUIRE(shaders::version() == new_version); + REQUIRE(shaders::updated(version) == true); + version = new_version; + +} diff --git a/tests/sound.cpp b/tests/sound.cpp new file mode 100644 index 0000000..968e650 --- /dev/null +++ b/tests/sound.cpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include "sound.hpp" + +using namespace fmt; + +TEST_CASE("test sound manager", "[sound]") { + sound::init(); + + sound::play("blank"); + + sound::play_at("blank", 0.1, 0.1, 0.1); +} diff --git a/tests/textures.cpp b/tests/textures.cpp new file mode 100644 index 0000000..910bda8 --- /dev/null +++ b/tests/textures.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include "textures.hpp" + +using namespace fmt; + +TEST_CASE("test texture management", "[textures]") { + textures::init(); + auto spider = textures::get("textures_test"); + REQUIRE(spider.sprite != nullptr); + REQUIRE(spider.texture != nullptr); + + auto image = textures::load_image("assets/textures_test.png"); +} diff --git a/textures.cpp b/textures.cpp new file mode 100644 index 0000000..7ee1ded --- /dev/null +++ b/textures.cpp @@ -0,0 +1,60 @@ +#include "textures.hpp" +#include +#include "dbc.hpp" +#include +#include "config.hpp" +#include "constants.hpp" +#include + +namespace textures { + using std::shared_ptr, std::make_shared; + + static TextureManager TMGR; + static bool initialized = false; + + void load_sprites() { + Config assets("assets/config.json"); + + for(auto& [name, settings] : assets["sprites"].items()) { + auto texture = make_shared(settings["path"]); + + texture->setSmooth(assets["graphics"]["smooth_textures"]); + auto sprite = make_shared(*texture); + + int width = settings["frame_width"]; + int height = settings["frame_height"]; + sprite->setTextureRect({{0,0}, {width, height}}); + + TMGR.sprite_textures.try_emplace(name, sprite, texture); + } + } + + void init() { + if(!initialized) { + load_sprites(); + initialized = true; + } + } + + SpriteTexture get(const std::string& name) { + dbc::check(initialized, "you forgot to call textures::init()"); + dbc::check(TMGR.sprite_textures.contains(name), + fmt::format("!!!!! texture pack does not contain {} sprite", name)); + + auto result = TMGR.sprite_textures.at(name); + + dbc::check(result.sprite != nullptr, + fmt::format("bad sprite from textures::get named {}", name)); + dbc::check(result.texture != nullptr, + fmt::format("bad texture from textures::get named {}", name)); + + return result; + } + + sf::Image load_image(const std::string& filename) { + sf::Image texture; + bool good = texture.loadFromFile(filename); + dbc::check(good, fmt::format("failed to load {}", filename)); + return texture; + } +}; diff --git a/textures.hpp b/textures.hpp new file mode 100644 index 0000000..7be99f2 --- /dev/null +++ b/textures.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace textures { + + struct SpriteTexture { + std::shared_ptr sprite = nullptr; + std::shared_ptr texture = nullptr; + }; + + struct TextureManager { + std::vector surfaces; + std::unordered_map sprite_textures; + std::unordered_map char_to_texture; + }; + + void init(); + + SpriteTexture get(const std::string& name); + + sf::Image load_image(const std::string& filename); +}