Very simple items system to get into the inventory work.

main
Zed A. Shaw 1 week ago
parent 1962b0c24e
commit 3d461bce6d
  1. 22
      assets/items.json
  2. 2
      assets/tiles.json
  3. 2
      combat.hpp
  4. 15
      components.hpp
  5. 4
      config.cpp
  6. 7
      gui.cpp
  7. 1
      gui.hpp
  8. 26
      main.cpp
  9. 12
      save.cpp
  10. 2
      status.txt
  11. 24
      systems.cpp
  12. 2
      tests/config.cpp
  13. 4
      tests/gui.cpp
  14. 2
      tests/sound.cpp
  15. 1
      worldbuilder.cpp

@ -0,0 +1,22 @@
{
"TORCH": {
"foreground": [24, 205, 189],
"background": [230, 20, 120],
"display": "\u0f08"
},
"SWORD": {
"foreground": [24, 205, 189],
"background": [24, 205, 189],
"display":"\u1e37"
},
"CHEST": {
"foreground": [24, 205, 189],
"background": [24, 205, 189],
"display":"\uaaea"
},
"WALL_TORCH": {
"foreground": [24, 205, 189],
"background": [24, 205, 189],
"display": "☀"
}
}

@ -56,7 +56,7 @@
"BROKEN_TILE": { "BROKEN_TILE": {
"foreground": [159, 164, 15], "foreground": [159, 164, 15],
"background": [199, 15, 79], "background": [199, 15, 79],
"collision": false, "collision": true,
"display":"\u2274" "display":"\u2274"
} }
} }

@ -6,7 +6,7 @@ namespace components {
int damage; int damage;
/* NOTE: This is used to _mark_ entities as dead, to detect ones that have just died. Don't make attack automatically set it.*/ /* NOTE: This is used to _mark_ entities as dead, to detect ones that have just died. Don't make attack automatically set it.*/
bool dead; bool dead = false;
int attack(Combat &target); int attack(Combat &target);
}; };

@ -29,7 +29,8 @@ namespace components {
struct Inventory { struct Inventory {
int gold; int gold;
DEFINE_SERIALIZABLE(Inventory, gold); LightSource light;
DEFINE_SERIALIZABLE(Inventory, gold, light);
}; };
struct Tile { struct Tile {
@ -37,9 +38,11 @@ namespace components {
DEFINE_SERIALIZABLE(Tile, chr); DEFINE_SERIALIZABLE(Tile, chr);
}; };
struct MapConfig { struct GameConfig {
std::string PLAYER_TILE; Config game;
std::string ENEMY_TILE; Config enemies;
Config items;
Config tiles;
}; };
struct EnemyConfig { struct EnemyConfig {
@ -50,4 +53,8 @@ namespace components {
bool PATHS=false; bool PATHS=false;
bool LIGHT=false; bool LIGHT=false;
}; };
struct Weapon {
int damage = 0;
};
} }

@ -1,6 +1,9 @@
#include "config.hpp" #include "config.hpp"
#include "dbc.hpp"
#include <fmt/core.h>
using nlohmann::json; using nlohmann::json;
using fmt::format;
Config::Config(const std::string src_path) : $src_path(src_path) { Config::Config(const std::string src_path) : $src_path(src_path) {
std::ifstream infile($src_path); std::ifstream infile($src_path);
@ -8,6 +11,7 @@ Config::Config(const std::string src_path) : $src_path(src_path) {
} }
json &Config::operator[](const std::string &key) { json &Config::operator[](const std::string &key) {
dbc::check($config.contains(key), format("ERROR in config, key {} doesn't exist.", key));
return $config[key]; return $config[key];
} }

@ -52,6 +52,7 @@ void StatusUI::create_render() {
auto status_rend = Renderer([&, player]{ auto status_rend = Renderer([&, player]{
const auto& player_combat = $world.get<Combat>(player.entity); const auto& player_combat = $world.get<Combat>(player.entity);
const auto& inventory = $world.get<Inventory>(player.entity); const auto& inventory = $world.get<Inventory>(player.entity);
const auto& combat = $world.get<Combat>(player.entity);
$status_text = player_combat.hp > 0 ? "NOT DEAD" : "DEAD!!!!!!"; $status_text = player_combat.hp > 0 ? "NOT DEAD" : "DEAD!!!!!!";
std::vector<Element> log_list; std::vector<Element> log_list;
@ -64,8 +65,10 @@ void StatusUI::create_render() {
return hbox({ return hbox({
hflow( hflow(
vbox( vbox(
text(format("HP: {: >3} GOLD: {: >3}", text(format("HP: {: >3} GOLD: {: >3} DMG: {: >3}",
player_combat.hp, inventory.gold)) | border, player_combat.hp,
inventory.gold,
combat.damage)) | border,
text($status_text) | border, text($status_text) | border,
separator(), separator(),
log_box log_box

@ -40,7 +40,6 @@ struct UnDumbTSS {
sf::Sprite sprite; sf::Sprite sprite;
sf::Shader shader; sf::Shader shader;
sf::Shader& load_shader(string filename) { sf::Shader& load_shader(string filename) {
bool good = shader.loadFromFile(filename, sf::Shader::Fragment); bool good = shader.loadFromFile(filename, sf::Shader::Fragment);
dbc::check(good, "shader could not be loaded"); dbc::check(good, "shader could not be loaded");

@ -24,7 +24,7 @@ namespace fs = std::filesystem;
* system. * system.
*/ */
void configure_world(DinkyECS::World &world, Map &game_map) { void configure_world(DinkyECS::World &world, Map &game_map) {
const auto &config = world.get_the<MapConfig>(); auto &config = world.get_the<GameConfig>();
// configure a player as a fact of the world // configure a player as a fact of the world
Player player{world.entity()}; Player player{world.entity()};
world.set_the<Player>(player); world.set_the<Player>(player);
@ -32,7 +32,7 @@ void configure_world(DinkyECS::World &world, Map &game_map) {
world.set<Position>(player.entity, {game_map.place_entity(0)}); world.set<Position>(player.entity, {game_map.place_entity(0)});
world.set<Motion>(player.entity, {0, 0}); world.set<Motion>(player.entity, {0, 0});
world.set<Combat>(player.entity, {100, 10}); world.set<Combat>(player.entity, {100, 10});
world.set<Tile>(player.entity, {config.PLAYER_TILE}); world.set<Tile>(player.entity, {config.enemies["PLAYER_TILE"]["display"]});
world.set<Inventory>(player.entity, {5}); world.set<Inventory>(player.entity, {5});
world.set<LightSource>(player.entity, {70,1.0}); world.set<LightSource>(player.entity, {70,1.0});
@ -40,24 +40,38 @@ void configure_world(DinkyECS::World &world, Map &game_map) {
world.set<Position>(enemy, {game_map.place_entity(1)}); world.set<Position>(enemy, {game_map.place_entity(1)});
world.set<Motion>(enemy, {0,0}); world.set<Motion>(enemy, {0,0});
world.set<Combat>(enemy, {20, 10}); world.set<Combat>(enemy, {20, 10});
world.set<Tile>(enemy, {config.ENEMY_TILE}); world.set<Tile>(enemy, {config.enemies["UNICORN"]["display"]});
auto enemy2 = world.entity(); auto enemy2 = world.entity();
world.set<Position>(enemy2, {game_map.place_entity(2)}); world.set<Position>(enemy2, {game_map.place_entity(2)});
world.set<Motion>(enemy2, {0,0}); world.set<Motion>(enemy2, {0,0});
world.set<Combat>(enemy2, {20, 10}); world.set<Combat>(enemy2, {20, 10});
world.set<Tile>(enemy2, {"*"}); world.set<Tile>(enemy2, {config.enemies["SNAKE"]["display"]});
world.set<LightSource>(enemy2, {60,0.2f}); world.set<LightSource>(enemy2, {60,0.2f});
auto gold = world.entity(); auto gold = world.entity();
world.set<Position>(gold, {game_map.place_entity(3)}); world.set<Position>(gold, {game_map.place_entity(3)});
world.set<Loot>(gold, {100}); world.set<Loot>(gold, {100});
world.set<Tile>(gold, {"$"}); world.set<Tile>(gold, {config.items["CHEST"]["display"]});
auto wall_torch = world.entity(); auto wall_torch = world.entity();
world.set<Position>(wall_torch, {game_map.place_entity(4)}); world.set<Position>(wall_torch, {game_map.place_entity(4)});
world.set<LightSource>(wall_torch, {90,3.0f}); world.set<LightSource>(wall_torch, {90,3.0f});
world.set<Tile>(wall_torch, {""}); world.set<Tile>(wall_torch, {config.items["WALL_TORCH"]["display"]});
auto torch = world.entity();
Point at = game_map.place_entity(2);
world.set<Position>(torch, {{at.x+1, at.y+1}});
world.set<Loot>(torch, {{0}});
world.set<LightSource>(torch, {70,1.5f});
world.set<Tile>(torch, {config.items["TORCH"]["display"]});
auto sword = world.entity();
at = game_map.place_entity(1);
world.set<Position>(sword, {at.x+1, at.y+1});
world.set<Weapon>(sword, {.damage=20});
world.set<Loot>(sword, {{0}});
world.set<Tile>(sword, {config.items["SWORD"]["display"]});
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {

@ -84,16 +84,16 @@ void save::from_file(fs::path path, DinkyECS::World &world_out, Map &map_out) {
} }
void save::load_configs(DinkyECS::World &world) { void save::load_configs(DinkyECS::World &world) {
Config config("./assets/config.json"); Config game("./assets/config.json");
Config enemies("./assets/enemies.json"); Config enemies("./assets/enemies.json");
Config items("./assets/items.json");
Config tiles("./assets/tiles.json");
world.set_the<Config>(config); world.set_the<GameConfig>({
world.set_the<MapConfig>({ game, enemies, items, tiles
enemies["PLAYER_TILE"]["display"],
enemies["UNICORN"]["display"],
}); });
auto enemy = config["enemy"]; auto enemy = game["enemy"];
world.set_the<EnemyConfig>({ world.set_the<EnemyConfig>({
enemy["HEARING_DISTANCE"] enemy["HEARING_DISTANCE"]
}); });

@ -1,5 +1,7 @@
TODAY'S GOAL: TODAY'S GOAL:
* Colision fails when you place two entities on the same square, but the init_positions adds them and one deletes the other.
* Config needs to do asserts that the key exists
* Create a move function for iterators that recalculates their position to make it easy to move them inside the matrix. This can then be used in lighting. Just make an iterator once, and move it around after. * Create a move function for iterators that recalculates their position to make it easy to move them inside the matrix. This can then be used in lighting. Just make an iterator once, and move it around after.
* Components::Tile must also die. * Components::Tile must also die.
* MapConfig must die. * MapConfig must die.

@ -138,14 +138,25 @@ void System::collision(DinkyECS::World &world, Player &player) {
auto loot = world.get<Loot>(entity); auto loot = world.get<Loot>(entity);
auto &loot_pos = world.get<Position>(entity); auto &loot_pos = world.get<Position>(entity);
auto &inventory = world.get<Inventory>(player.entity); auto &inventory = world.get<Inventory>(player.entity);
// BUG: this should go away and be a part of inventory
auto &light = world.get<LightSource>(player.entity);
world.send<Events::GUI>(Events::GUI::LOOT, entity, loot); if(world.has<LightSource>(entity)) {
inventory.gold += loot.amount; auto &new_light = world.get<LightSource>(entity);
light.strength = 70; world.set<LightSource>(player.entity, new_light);
light.radius = 2; inventory.light = new_light;
world.remove<LightSource>(entity);
} else if(world.has<Weapon>(entity)) {
auto &weapon = world.get<Weapon>(entity);
player_combat.damage = weapon.damage;
world.remove<Weapon>(entity);
} else {
// it's just gold
inventory.gold += loot.amount;
}
collider.remove(loot_pos.location); collider.remove(loot_pos.location);
world.remove<Tile>(entity);
world.remove<Loot>(entity);
world.send<Events::GUI>(Events::GUI::LOOT, entity, loot);
} else { } else {
println("UNKNOWN COLLISION TYPE {}", entity); println("UNKNOWN COLLISION TYPE {}", entity);
} }
@ -153,7 +164,6 @@ void System::collision(DinkyECS::World &world, Player &player) {
} }
} }
// BUG: this is kind of massive, need to maybe rethink how systems are designed. I mean...can each system be a callable class instead?
void System::draw_entities(DinkyECS::World &world, Map &game_map, const Matrix &lighting, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y) { void System::draw_entities(DinkyECS::World &world, Map &game_map, const Matrix &lighting, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y) {
auto &tiles = game_map.tiles(); auto &tiles = game_map.tiles();

@ -13,6 +13,8 @@ using std::string;
TEST_CASE("basic configuration system", "[config]") { TEST_CASE("basic configuration system", "[config]") {
Config config("./tests/config.json"); Config config("./tests/config.json");
REQUIRE_THROWS([&]{ config["blahblah"]; }());
auto not_found = config["types"]["NOTFOUND"]; auto not_found = config["types"]["NOTFOUND"];
REQUIRE(not_found == nullptr); REQUIRE(not_found == nullptr);

@ -20,7 +20,7 @@ TEST_CASE("load a basic gui run but don't loop", "[gui]") {
WorldBuilder builder(game_map); WorldBuilder builder(game_map);
builder.generate(); builder.generate();
const auto &config = world.get_the<MapConfig>(); auto &config = world.get_the<GameConfig>();
// configure a player as a fact of the world // configure a player as a fact of the world
Player player{world.entity()}; Player player{world.entity()};
world.set_the<Player>(player); world.set_the<Player>(player);
@ -28,7 +28,7 @@ TEST_CASE("load a basic gui run but don't loop", "[gui]") {
world.set<Position>(player.entity, {game_map.place_entity(0)}); world.set<Position>(player.entity, {game_map.place_entity(0)});
world.set<Motion>(player.entity, {0, 0}); world.set<Motion>(player.entity, {0, 0});
world.set<Combat>(player.entity, {100, 10}); world.set<Combat>(player.entity, {100, 10});
world.set<Tile>(player.entity, {config.PLAYER_TILE}); world.set<Tile>(player.entity, {config.enemies["PLAYER_TILE"]["display"]});
world.set<Inventory>(player.entity, {5}); world.set<Inventory>(player.entity, {5});
world.set<LightSource>(player.entity, {6,1}); world.set<LightSource>(player.entity, {6,1});

@ -8,7 +8,7 @@ using DinkyECS::Entity;
using namespace fmt; using namespace fmt;
TEST_CASE("confirm basic functionality", "[sounds]") { TEST_CASE("confirm basic functionality", "[sounds]") {
REQUIRE_THROWS([&](){SoundManager sounds("./BADassets");}()); REQUIRE_THROWS([&]{SoundManager sounds("./BADassets");}());
SoundManager sounds("./assets"); SoundManager sounds("./assets");

@ -164,7 +164,6 @@ void WorldBuilder::generate() {
string tile_name = room_types[room_type]; string tile_name = room_types[room_type];
stylize_room(i, tile_name, room_size * 0.01f); stylize_room(i, tile_name, room_size * 0.01f);
} }
} }
void WorldBuilder::make_room(size_t origin_x, size_t origin_y, size_t w, size_t h) { void WorldBuilder::make_room(size_t origin_x, size_t origin_y, size_t w, size_t h) {

Loading…
Cancel
Save