Fixed up building enemies and items using componentsin the JSON.

main
Zed A. Shaw 2 weeks ago
parent 9ce4fbd552
commit 222b39c403
  1. 15
      assets/enemies.json
  2. 8
      assets/items.json
  3. 28
      components.hpp
  4. 13
      dbc.cpp
  5. 3
      gui.cpp
  6. 22
      matrix.hpp
  7. 5
      save.cpp
  8. 1
      save.hpp
  9. 1
      status.txt
  10. 6
      systems.cpp
  11. 6
      tests/components.cpp
  12. 2
      tests/matrix.cpp
  13. 26
      worldbuilder.cpp

@ -4,7 +4,8 @@
"background": [30, 20, 75], "background": [30, 20, 75],
"components": [ "components": [
{"type": "Tile", "config": {"chr": "\ua66b"}}, {"type": "Tile", "config": {"chr": "\ua66b"}},
{"type": "Combat", "config": {"hp": 200, "damage": 15}} {"type": "Combat", "config": {"hp": 200, "damage": 15}},
{"type": "EnemyConfig", "config": {"hearing_distance": 5}}
] ]
}, },
"SNAKE": { "SNAKE": {
@ -12,7 +13,8 @@
"background": [30, 20, 75], "background": [30, 20, 75],
"components": [ "components": [
{"type": "Tile", "config": {"chr": "\u06b1"}}, {"type": "Tile", "config": {"chr": "\u06b1"}},
{"type": "Combat", "config": {"hp": 20, "damage": 15}} {"type": "Combat", "config": {"hp": 20, "damage": 15}},
{"type": "EnemyConfig", "config": {"hearing_distance": 6}}
] ]
}, },
"GOBLIN": { "GOBLIN": {
@ -21,7 +23,8 @@
"components": [ "components": [
{"type": "LightSource", "config": {"strength": 70, "radius": 1.8}}, {"type": "LightSource", "config": {"strength": 70, "radius": 1.8}},
{"type": "Tile", "config": {"chr": "\u06bf"}}, {"type": "Tile", "config": {"chr": "\u06bf"}},
{"type": "Combat", "config": {"hp": 50, "damage": 35}} {"type": "Combat", "config": {"hp": 50, "damage": 35}},
{"type": "EnemyConfig", "config": {"hearing_distance": 4}}
] ]
}, },
"UNICORN": { "UNICORN": {
@ -29,7 +32,8 @@
"background": [30, 20, 75], "background": [30, 20, 75],
"components": [ "components": [
{"type": "Tile", "config": {"chr": "\u17a5"}}, {"type": "Tile", "config": {"chr": "\u17a5"}},
{"type": "Combat", "config": {"hp": 2000, "damage": 5}} {"type": "Combat", "config": {"hp": 100, "damage": 5}},
{"type": "EnemyConfig", "config": {"hearing_distance": 3}}
] ]
}, },
"RAT": { "RAT": {
@ -37,7 +41,8 @@
"background": [30, 20, 75], "background": [30, 20, 75],
"components": [ "components": [
{"type": "Tile", "config": {"chr": "\u08ac"}}, {"type": "Tile", "config": {"chr": "\u08ac"}},
{"type": "Combat", "config": {"hp": 10, "damage": 5}} {"type": "Combat", "config": {"hp": 10, "damage": 5}},
{"type": "EnemyConfig", "config": {"hearing_distance": 10}}
] ]
} }
} }

@ -8,8 +8,7 @@
"inventory_count": 1, "inventory_count": 1,
"components": [ "components": [
{"type": "LightSource", "config": {"strength": 70, "radius": 2.0}}, {"type": "LightSource", "config": {"strength": 70, "radius": 2.0}},
{"type": "Tile", "config": {"chr": "\u0f08"}}, {"type": "Tile", "config": {"chr": "\u0f08"}}
{"type": "Weapon", "config": {"damage": 35}}
] ]
}, },
"SWORD_RUSTY": { "SWORD_RUSTY": {
@ -57,9 +56,8 @@
"description": "A torch on a wall you can't pick up.", "description": "A torch on a wall you can't pick up.",
"inventory_count": 0, "inventory_count": 0,
"components": [ "components": [
{"type": "Tile", "config": {"chr": "\u06bf"}}, {"type": "Tile", "config": {"chr": "\u077e"}},
{"type": "LightSource", "config": {"strength": 60, "radius": 1.8}} {"type": "LightSource", "config": {"strength": 60, "radius": 1.8}}
], ]
"display": "☀"
} }
} }

@ -1,10 +1,9 @@
#pragma once #pragma once
#include "dinkyecs.hpp" #include "dinkyecs.hpp"
#include "map.hpp"
#include "combat.hpp" #include "combat.hpp"
#include "inventory.hpp" #include "inventory.hpp"
#include <deque>
#include "tser.hpp" #include "tser.hpp"
#include "config.hpp"
namespace components { namespace components {
struct Player { struct Player {
@ -41,7 +40,7 @@ namespace components {
}; };
struct EnemyConfig { struct EnemyConfig {
int HEARING_DISTANCE; int hearing_distance = 10;
}; };
struct Debug { struct Debug {
@ -52,4 +51,27 @@ namespace components {
struct Weapon { struct Weapon {
int damage = 0; int damage = 0;
}; };
inline void configure(DinkyECS::World &world, DinkyECS::Entity entity, json& entity_data) {
for(auto &comp : entity_data["components"]) {
json& config = comp["config"];
if(comp["type"] == "Weapon") {
world.set<Weapon>(entity, {config["damage"]});
} else if(comp["type"] == "LightSource") {
world.set<LightSource>(entity, {config["strength"], config["radius"]});
} else if(comp["type"] == "Loot") {
world.set<Loot>(entity, {config["amount"]});
} else if(comp["type"] == "Tile") {
world.set<Tile>(entity, {config["chr"]});
} else if(comp["type"] == "EnemyConfig") {
world.set<EnemyConfig>(entity, {config["hearing_distance"]});
} else if(comp["type"] == "Combat") {
world.set<Combat>(entity, {config["hp"], config["damage"]});
} else {
dbc::sentinel(fmt::format("ITEM COMPONENT TYPE MISSING: {}",
std::string(comp["type"])));
}
}
}
} }

@ -2,17 +2,19 @@
#include <iostream> #include <iostream>
void dbc::log(const string &message) { void dbc::log(const string &message) {
fmt::print("{}\n", message); std::cerr << message << std::endl;
} }
void dbc::sentinel(const string &message) { void dbc::sentinel(const string &message) {
string err = fmt::format("[SENTINEL!] {}\n", message); string err = fmt::format("[SENTINEL!] {}", message);
dbc::log(err);
throw dbc::SentinelError{err}; throw dbc::SentinelError{err};
} }
void dbc::pre(const string &message, bool test) { void dbc::pre(const string &message, bool test) {
if(!test) { if(!test) {
string err = fmt::format("[PRE!] {}\n", message); string err = fmt::format("[PRE!] {}", message);
dbc::log(err);
throw dbc::PreCondError{err}; throw dbc::PreCondError{err};
} }
} }
@ -23,7 +25,8 @@ void dbc::pre(const string &message, std::function<bool()> tester) {
void dbc::post(const string &message, bool test) { void dbc::post(const string &message, bool test) {
if(!test) { if(!test) {
string err = fmt::format("[POST!] {}\n", message); string err = fmt::format("[POST!] {}", message);
dbc::log(err);
throw dbc::PostCondError{err}; throw dbc::PostCondError{err};
} }
} }
@ -35,7 +38,7 @@ void dbc::post(const string &message, std::function<bool()> tester) {
void dbc::check(bool test, const string &message) { void dbc::check(bool test, const string &message) {
if(!test) { if(!test) {
string err = fmt::format("[CHECK!] {}\n", message); string err = fmt::format("[CHECK!] {}\n", message);
fmt::println("{}", err); dbc::log(err);
throw dbc::CheckError{err}; throw dbc::CheckError{err};
} }
} }

@ -72,8 +72,7 @@ void InventoryUI::update_menu_list(Inventory& inventory) {
$menu_list.clear(); $menu_list.clear();
for(size_t i = 0; i < inventory.count(); i++) { for(size_t i = 0; i < inventory.count(); i++) {
auto& item = inventory.get(i); auto& item = inventory.get(i);
$menu_list.push_back(fmt::format("{} {} ({})", $menu_list.push_back(fmt::format("{} ({})",
string(item.data["display"]),
string(item.data["name"]), string(item.data["name"]),
item.count)); item.count));

@ -10,8 +10,15 @@ namespace matrix {
using std::vector, std::queue, std::array; using std::vector, std::queue, std::array;
using std::min, std::max, std::floor; using std::min, std::max, std::floor;
typedef vector<int> Row; template<typename T>
typedef vector<Row> Matrix; using BaseRow = vector<T>;
template<typename T>
using Base = vector<BaseRow<T>>;
using Row = vector<int>;
using Matrix = vector<Row>;
/* /*
* Just a quick thing to reset a matrix to a value. * Just a quick thing to reset a matrix to a value.
@ -40,6 +47,17 @@ namespace matrix {
return mat.size(); return mat.size();
} }
template<typename T>
inline Base<T> make_base(size_t width, size_t height) {
Base<T> result(height, BaseRow<T>(width));
return result;
}
inline Matrix make(size_t width, size_t height) {
Matrix result(height, Row(width));
return result;
}
inline size_t next_x(size_t x, size_t width) { inline size_t next_x(size_t x, size_t width) {
return (x + 1) * ((x + 1) < width); return (x + 1) * ((x + 1) < width);
} }

@ -92,9 +92,4 @@ void save::load_configs(DinkyECS::World &world) {
world.set_the<GameConfig>({ world.set_the<GameConfig>({
game, enemies, items, tiles game, enemies, items, tiles
}); });
auto enemy = game["enemy"];
world.set_the<EnemyConfig>({
enemy["HEARING_DISTANCE"]
});
} }

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "components.hpp" #include "components.hpp"
#include "map.hpp"
#include "dinkyecs.hpp" #include "dinkyecs.hpp"
#include "tser.hpp" #include "tser.hpp"
#include <filesystem> #include <filesystem>

@ -1,5 +1,6 @@
TODAY'S GOAL: TODAY'S GOAL:
* Goblins will be in the world and not move or are already dead.
* https://pkl-lang.org/ * https://pkl-lang.org/
* Check out https://github.com/stephenberry/glaze * Check out https://github.com/stephenberry/glaze
* Things are still in walls because I +1 the x,y if they're colliding. * Things are still in walls because I +1 the x,y if they're colliding.

@ -32,15 +32,17 @@ void System::lighting(DinkyECS::World &world, Map &game_map, LightRender &light,
void System::enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player) { void System::enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player) {
// TODO: this will be on each enemy not a global thing // TODO: this will be on each enemy not a global thing
const auto &config = world.get_the<EnemyConfig>();
const auto &player_position = world.get<Position>(player.entity); const auto &player_position = world.get<Position>(player.entity);
game_map.set_target(player_position.location); game_map.set_target(player_position.location);
game_map.make_paths(); game_map.make_paths();
world.query<Position, Motion>([&](const auto &ent, auto &position, auto &motion) { world.query<Position, Motion>([&](const auto &ent, auto &position, auto &motion) {
if(ent != player.entity) { if(ent != player.entity) {
dbc::check(world.has<EnemyConfig>(ent), "enemy is missing config");
const auto &config = world.get<EnemyConfig>(ent);
Point out = position.location; // copy Point out = position.location; // copy
if(game_map.distance(out) < config.HEARING_DISTANCE) { if(game_map.distance(out) < config.hearing_distance) {
game_map.neighbors(out); game_map.neighbors(out);
motion = { int(out.x - position.location.x), int(out.y - position.location.y)}; motion = { int(out.x - position.location.x), int(out.y - position.location.y)};
} }

@ -36,9 +36,6 @@ TEST_CASE("all components can work in the world", "[components]") {
auto tile = world.get<Tile>(ent1); auto tile = world.get<Tile>(ent1);
REQUIRE(tile.chr == "Z"); REQUIRE(tile.chr == "Z");
auto enemy = world.get<EnemyConfig>(ent1);
REQUIRE(enemy.HEARING_DISTANCE == 4);
} }
TEST_CASE("all components can be facts", "[components]") { TEST_CASE("all components can be facts", "[components]") {
@ -72,9 +69,6 @@ TEST_CASE("all components can be facts", "[components]") {
auto tile = world.get_the<Tile>(); auto tile = world.get_the<Tile>();
REQUIRE(tile.chr == "Z"); REQUIRE(tile.chr == "Z");
auto enemy = world.get_the<EnemyConfig>();
REQUIRE(enemy.HEARING_DISTANCE == 4);
} }
TEST_CASE("confirm combat works", "[components]") { TEST_CASE("confirm combat works", "[components]") {

@ -86,7 +86,7 @@ TEST_CASE("thrash matrix iterators", "[matrix]") {
size_t width = Random::uniform<size_t>(1, 100); size_t width = Random::uniform<size_t>(1, 100);
size_t height = Random::uniform<size_t>(1, 100); size_t height = Random::uniform<size_t>(1, 100);
Matrix test(width, matrix::Row(height)); Matrix test(height, matrix::Row(width));
random_matrix(test); random_matrix(test);
// first make a randomized matrix // first make a randomized matrix

@ -168,28 +168,6 @@ void WorldBuilder::generate_map() {
} }
} }
void configure_components(DinkyECS::World &world, DinkyECS::Entity entity, json& entity_data) {
for(auto &comp : entity_data["components"]) {
json& config = comp["config"];
if(comp["type"] == "Weapon") {
world.set<Weapon>(entity, {config["damage"]});
} else if(comp["type"] == "LightSource") {
world.set<LightSource>(entity, {config["strength"], config["radius"]});
} else if(comp["type"] == "Loot") {
world.set<Loot>(entity, {config["amount"]});
} else if(comp["type"] == "Tile") {
world.set<Tile>(entity, {config["chr"]});
} else if(comp["type"] == "EnemyConfig") {
world.set<EnemyConfig>(entity, {config["hearing_distance"]});
} else if(comp["type"] == "Combat") {
world.set<Combat>(entity, {config["hp"], config["damage"]});
} else {
dbc::sentinel(format("ITEM COMPONENT TYPE MISSING: {}",
std::string(comp["type"])));
}
}
}
DinkyECS::Entity place_item(DinkyECS::World &world, Map &game_map, std::string name, int in_room) { DinkyECS::Entity place_item(DinkyECS::World &world, Map &game_map, std::string name, int in_room) {
auto &config = world.get_the<GameConfig>(); auto &config = world.get_the<GameConfig>();
@ -203,7 +181,7 @@ DinkyECS::Entity place_item(DinkyECS::World &world, Map &game_map, std::string n
} }
if(item_data.contains("components")) { if(item_data.contains("components")) {
configure_components(world, item, item_data); components::configure(world, item, item_data);
} }
return item; return item;
} }
@ -217,7 +195,7 @@ DinkyECS::Entity place_combatant(DinkyECS::World &world, Map &game_map, std::str
world.set<Motion>(enemy, {0,0}); world.set<Motion>(enemy, {0,0});
if(enemy_data.contains("components")) { if(enemy_data.contains("components")) {
configure_components(world, enemy, enemy_data); components::configure(world, enemy, enemy_data);
} }
return enemy; return enemy;
} }

Loading…
Cancel
Save