Very simple items system to get into the inventory work.

This commit is contained in:
Zed A. Shaw 2025-01-01 13:21:01 -05:00
parent 1962b0c24e
commit 3d461bce6d
15 changed files with 94 additions and 32 deletions

22
assets/items.json Normal file
View file

@ -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": "☀"
}
}

View file

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

View file

@ -6,7 +6,7 @@ namespace components {
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.*/
bool dead;
bool dead = false;
int attack(Combat &target);
};

View file

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

View file

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

View file

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

View file

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

View file

@ -24,7 +24,7 @@ namespace fs = std::filesystem;
* system.
*/
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
Player player{world.entity()};
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<Motion>(player.entity, {0, 0});
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<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<Motion>(enemy, {0,0});
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();
world.set<Position>(enemy2, {game_map.place_entity(2)});
world.set<Motion>(enemy2, {0,0});
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});
auto gold = world.entity();
world.set<Position>(gold, {game_map.place_entity(3)});
world.set<Loot>(gold, {100});
world.set<Tile>(gold, {"$"});
world.set<Tile>(gold, {config.items["CHEST"]["display"]});
auto wall_torch = world.entity();
world.set<Position>(wall_torch, {game_map.place_entity(4)});
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[]) {

View file

@ -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) {
Config config("./assets/config.json");
Config game("./assets/config.json");
Config enemies("./assets/enemies.json");
Config items("./assets/items.json");
Config tiles("./assets/tiles.json");
world.set_the<Config>(config);
world.set_the<MapConfig>({
enemies["PLAYER_TILE"]["display"],
enemies["UNICORN"]["display"],
world.set_the<GameConfig>({
game, enemies, items, tiles
});
auto enemy = config["enemy"];
auto enemy = game["enemy"];
world.set_the<EnemyConfig>({
enemy["HEARING_DISTANCE"]
});

View file

@ -1,5 +1,7 @@
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.
* Components::Tile must also die.
* MapConfig must die.

View file

@ -138,14 +138,25 @@ void System::collision(DinkyECS::World &world, Player &player) {
auto loot = world.get<Loot>(entity);
auto &loot_pos = world.get<Position>(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);
inventory.gold += loot.amount;
light.strength = 70;
light.radius = 2;
if(world.has<LightSource>(entity)) {
auto &new_light = world.get<LightSource>(entity);
world.set<LightSource>(player.entity, new_light);
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);
world.remove<Tile>(entity);
world.remove<Loot>(entity);
world.send<Events::GUI>(Events::GUI::LOOT, entity, loot);
} else {
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) {
auto &tiles = game_map.tiles();

View file

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

View file

@ -20,7 +20,7 @@ TEST_CASE("load a basic gui run but don't loop", "[gui]") {
WorldBuilder builder(game_map);
builder.generate();
const auto &config = world.get_the<MapConfig>();
auto &config = world.get_the<GameConfig>();
// configure a player as a fact of the world
Player player{world.entity()};
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<Motion>(player.entity, {0, 0});
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<LightSource>(player.entity, {6,1});

View file

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

View file

@ -164,7 +164,6 @@ void WorldBuilder::generate() {
string tile_name = room_types[room_type];
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) {