From f2864a62eecd5b0998d0be0314ce52cd3b5f4275 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Mon, 6 Jan 2025 15:20:54 -0500 Subject: [PATCH] Game now loads random enemies and items into rooms but in rudimentary way. Need to now randomize more of it and make it more robust so only changing the .json is needed to get new effects and enemies. --- assets/enemies.json | 15 +++++--- assets/items.json | 8 +++- main.cpp | 59 +--------------------------- status.txt | 3 ++ tests/gui.cpp | 2 +- tests/lighting.cpp | 2 +- tests/map.cpp | 2 +- tests/matrix.cpp | 4 +- tests/save.cpp | 2 +- tests/tilemap.cpp | 2 +- tests/worldbuilder.cpp | 4 +- worldbuilder.cpp | 87 +++++++++++++++++++++++++++++++++++++++++- worldbuilder.hpp | 5 ++- 13 files changed, 119 insertions(+), 76 deletions(-) diff --git a/assets/enemies.json b/assets/enemies.json index e19e47d..2633ab4 100644 --- a/assets/enemies.json +++ b/assets/enemies.json @@ -2,31 +2,36 @@ "PLAYER_TILE": { "foreground": [255, 200, 125], "background": [30, 20, 75], + "hp": 100, + "damage": 10, "display":"\ua66b" }, - "ENEMY_TILE": { - "foreground": [100, 200, 125], - "background": [30, 20, 75], - "display":"\u1d5c" - }, "SNAKE": { "foreground": [90, 172, 74], "background": [30, 20, 75], + "hp": 15, + "damage": 5, "display":"\u06b1" }, "GOBLIN": { "foreground": [50, 200, 125], "background": [30, 20, 75], + "hp": 75, + "damage": 30, "display":"\u06bf" }, "UNICORN": { "foreground": [25, 200, 125], "background": [30, 20, 75], + "hp": 50, + "damage": 20, "display":"\u17a5" }, "RAT": { "foreground": [75, 200, 125], "background": [30, 20, 75], + "hp": 5, + "damage": 1, "display":"\u08ac" } } diff --git a/assets/items.json b/assets/items.json index 04d5137..f8b675f 100644 --- a/assets/items.json +++ b/assets/items.json @@ -5,6 +5,7 @@ "foreground": [24, 205, 189], "background": [230, 20, 120], "description": "A torch that barely lights the way. You wonder if it'd be better to not see the person who murders you.", + "type": "LIGHT", "display": "\u0f08" }, "SWORD_RUSTY": { @@ -13,6 +14,7 @@ "foreground": [24, 205, 189], "background": [24, 205, 189], "description": "A sword left to rot in a deep hole where it acquired a patina of dirt and tetanus. You aren't sure if it's more deadly for you to hold it or for the people you stab with it.", + "type": "WEAPON", "display":"\u1e37" }, "CHEST_SMALL": { @@ -20,8 +22,9 @@ "name": "Small Chest", "foreground": [24, 205, 189], "background": [24, 205, 189], - "display":"\uaaea", - "description": "A small chest of gold. You wonder who would leave something like this around." + "description": "A small chest of gold. You wonder who would leave something like this around.", + "type": "LOOT", + "display":"\uaaea" }, "WALL_TORCH": { "id": "WALL_TORCH", @@ -29,6 +32,7 @@ "foreground": [24, 205, 189], "background": [24, 205, 189], "description": "A torch on a wall you can't pick up.", + "type": "FIXED_LIGHT", "display": "☀" } } diff --git a/main.cpp b/main.cpp index 305bf0e..c63e042 100644 --- a/main.cpp +++ b/main.cpp @@ -20,62 +20,6 @@ using namespace components; using lighting::LightSource; namespace fs = std::filesystem; -/* - * This needs to be turned into a real world generator - * system. - */ -void configure_world(DinkyECS::World &world, Map &game_map) { - auto &config = world.get_the(); - // configure a player as a fact of the world - Player player{world.entity()}; - world.set_the(player); - - world.set(player.entity, {game_map.place_entity(0)}); - world.set(player.entity, {0, 0}); - world.set(player.entity, {100, 10}); - world.set(player.entity, {config.enemies["PLAYER_TILE"]["display"]}); - world.set(player.entity, {50,1.0}); - world.set(player.entity, {5}); - - auto sword = world.entity(); - auto pos = game_map.place_entity(1); - world.set(sword, {pos.x+1, pos.y+1}); - world.set(sword, {config.items["SWORD_RUSTY"]["display"]}); - world.set(sword, {1, config.items["SWORD_RUSTY"]}); - world.set(sword, {20}); - - auto torch = world.entity(); - pos = game_map.place_entity(2); - world.set(torch, {pos.x+1, pos.y+1}); - world.set(torch, {config.items["TORCH_BAD"]["display"]}); - world.set(torch, {1, config.items["TORCH_BAD"]}); - world.set(torch, {70,2.0f}); - - auto enemy = world.entity(); - world.set(enemy, {game_map.place_entity(1)}); - world.set(enemy, {0,0}); - world.set(enemy, {20, 10}); - world.set(enemy, {config.enemies["UNICORN"]["display"]}); - - auto enemy2 = world.entity(); - world.set(enemy2, {game_map.place_entity(2)}); - world.set(enemy2, {0,0}); - world.set(enemy2, {20, 10}); - world.set(enemy2, {config.enemies["SNAKE"]["display"]}); - world.set(enemy2, {60,0.2f}); - - auto gold = world.entity(); - world.set(gold, {game_map.place_entity(3)}); - world.set(gold, {100}); - world.set(gold, {config.items["CHEST_SMALL"]["display"]}); - world.set(gold, {1, config.items["CHEST_SMALL"]}); - - auto wall_torch = world.entity(); - world.set(wall_torch, {game_map.place_entity(4)}); - world.set(wall_torch, {90,3.0f}); - world.set(wall_torch, {config.items["WALL_TORCH"]["display"]}); -} - int main(int argc, char *argv[]) { DinkyECS::World world; Map game_map(GAME_MAP_X, GAME_MAP_Y); @@ -87,8 +31,7 @@ int main(int argc, char *argv[]) { save::from_file(save_path, world, game_map); } else { WorldBuilder builder(game_map); - builder.generate(); - configure_world(world, game_map); + builder.generate(world); } SpatialMap collider; diff --git a/status.txt b/status.txt index 095f43a..da160f1 100644 --- a/status.txt +++ b/status.txt @@ -1,5 +1,8 @@ TODAY'S GOAL: +* I don't handle death at all. It crashes when I die. +* https://pkl-lang.org/ +* Check out https://github.com/stephenberry/glaze * Things are still in walls because I +1 the x,y if they're colliding. * Config loader should setup the "id" based on the key to avoid errors. * Colision fails when you place two entities on the same square, but the init_positions adds them and one deletes the other. diff --git a/tests/gui.cpp b/tests/gui.cpp index 9107cb6..1544163 100644 --- a/tests/gui.cpp +++ b/tests/gui.cpp @@ -18,7 +18,7 @@ TEST_CASE("load a basic gui run but don't loop", "[gui]") { save::load_configs(world); Map game_map(40, 40); WorldBuilder builder(game_map); - builder.generate(); + builder.generate_map(); auto &config = world.get_the(); // configure a player as a fact of the world diff --git a/tests/lighting.cpp b/tests/lighting.cpp index f9d348e..1ce4583 100644 --- a/tests/lighting.cpp +++ b/tests/lighting.cpp @@ -12,7 +12,7 @@ using namespace lighting; TEST_CASE("lighting a map works", "[lighting]") { Map map(20,23); WorldBuilder builder(map); - builder.generate(); + builder.generate_map(); Point light1 = map.place_entity(0); Point light2 = map.place_entity(1); diff --git a/tests/map.cpp b/tests/map.cpp index 2ac78d8..1fa2fc7 100644 --- a/tests/map.cpp +++ b/tests/map.cpp @@ -17,7 +17,7 @@ json load_test_data(const string &fname) { TEST_CASE("camera control", "[map]") { Map map(20, 20); WorldBuilder builder(map); - builder.generate(); + builder.generate_map(); Point center = map.center_camera({10,10}, 5, 5); diff --git a/tests/matrix.cpp b/tests/matrix.cpp index 3533524..20c4b3b 100644 --- a/tests/matrix.cpp +++ b/tests/matrix.cpp @@ -190,7 +190,7 @@ TEST_CASE("prototype flood algorithm", "[matrix:flood]") { Map map(width,height); WorldBuilder builder(map); - builder.generate(); + builder.generate_map(); if(map.room_count() < 2) continue; @@ -282,7 +282,7 @@ TEST_CASE("viewport iterator", "[matrix:viewport]") { size_t height = Random::uniform(21, 25); Map map(width,height); WorldBuilder builder(map); - builder.generate(); + builder.generate_map(); size_t view_width = width/2; size_t view_height = height/2; diff --git a/tests/save.cpp b/tests/save.cpp index 3ec1996..c70d6dd 100644 --- a/tests/save.cpp +++ b/tests/save.cpp @@ -58,7 +58,7 @@ TEST_CASE("basic save a world", "[save]") { DinkyECS::World world; Map map(20, 20); WorldBuilder builder(map); - builder.generate(); + builder.generate_map(); // configure a player as a fact of the world Player player{world.entity()}; diff --git a/tests/tilemap.cpp b/tests/tilemap.cpp index 86185c9..bae4b20 100644 --- a/tests/tilemap.cpp +++ b/tests/tilemap.cpp @@ -15,7 +15,7 @@ TEST_CASE("tilemap can load tiles and make a map", "[tilemap]") { Map map(width,height); WorldBuilder builder(map); - builder.generate(); + builder.generate_map(); TileMap tiles(map.width(), map.height()); auto& walls = map.walls(); diff --git a/tests/worldbuilder.cpp b/tests/worldbuilder.cpp index 916f60b..ff97012 100644 --- a/tests/worldbuilder.cpp +++ b/tests/worldbuilder.cpp @@ -12,13 +12,13 @@ using std::string; TEST_CASE("bsp algo test", "[builder]") { Map map(31, 20); WorldBuilder builder(map); - builder.generate(); + builder.generate_map(); } TEST_CASE("pathing", "[builder]") { Map map(23, 14); WorldBuilder builder(map); - builder.generate(); + builder.generate_map(); matrix::dump("WALLS", map.$walls, 0,0); println("wall at 0,0=={}", map.$walls[0][0]); diff --git a/worldbuilder.cpp b/worldbuilder.cpp index 68b3763..377f374 100644 --- a/worldbuilder.cpp +++ b/worldbuilder.cpp @@ -2,8 +2,10 @@ #include "rand.hpp" #include #include +#include "components.hpp" using namespace fmt; +using namespace components; inline int make_split(Room &cur, bool horiz) { size_t dimension = horiz ? cur.height : cur.width; @@ -119,7 +121,7 @@ void WorldBuilder::stylize_room(int room, string tile_name, float size) { } } -void WorldBuilder::generate() { +void WorldBuilder::generate_map() { PointList holes; Room root{ .x = 0, @@ -166,6 +168,89 @@ void WorldBuilder::generate() { } } +DinkyECS::Entity place_item(DinkyECS::World &world, Map &game_map, std::string name, int in_room) { + auto &config = world.get_the(); + auto item = world.entity(); + auto pos = game_map.place_entity(in_room); + json item_data = config.items[name]; + + world.set(item, {pos.x+1, pos.y+1}); + world.set(item, {item_data["display"]}); + + if(item_data["type"] == "WEAPON") { + world.set(item, {1, item_data}); + world.set(item, {20}); + } else if(item_data["type"] == "LIGHT") { + world.set(item, {1, item_data}); + world.set(item, {70,2.0f}); + } else if(item_data["type"] == "LOOT") { + world.set(item, {1, item_data}); + world.set(item, {100}); + } else if(item_data["type"] == "FIXED_LIGHT") { + world.set(item, {90,3.0f}); + } else { + dbc::sentinel(format("ITEM MISSING TYPE: {}", name)); + } + + return item; +} + + +DinkyECS::Entity place_combatant(DinkyECS::World &world, Map &game_map, std::string name, int in_room) { + auto &config = world.get_the(); + auto enemy = world.entity(); + auto enemy_data = config.enemies[name]; + world.set(enemy, {game_map.place_entity(in_room)}); + world.set(enemy, {0,0}); + world.set(enemy, {enemy_data["display"]}); + world.set(enemy, {enemy_data["hp"], enemy_data["damage"]}); + return enemy; +} + +void WorldBuilder::place_entities(DinkyECS::World &world) { + auto &config = world.get_the(); + // configure a player as a fact of the world + + auto player_ent = place_combatant(world, $map, "PLAYER_TILE", 0); + // configure player in the world + Player player{player_ent}; + world.set_the(player); + world.set(player.entity, {100, 10}); + world.set(player.entity, {50,1.0}); + world.set(player.entity, {5}); + + { + std::vector keys; + for(auto &el : config.items.json().items()) { + keys.push_back(el.key()); + } + + for(size_t room_num = 1; room_num < $map.room_count(); room_num++) { + std::string key = keys[room_num % keys.size()]; + place_item(world, $map, key, room_num); + } + } + + { + std::vector keys; + for(auto &el : config.enemies.json().items()) { + keys.push_back(el.key()); + } + + for(size_t room_num = $map.room_count() - 1; room_num > 0; room_num--) { + if(room_num % 2 == 0) { + std::string key = keys[room_num % keys.size()]; + place_combatant(world, $map, key, room_num); + } + } + } +} + +void WorldBuilder::generate(DinkyECS::World &world) { + generate_map(); + place_entities(world); +} + void WorldBuilder::make_room(size_t origin_x, size_t origin_y, size_t w, size_t h) { $map.INVARIANT(); dbc::pre("y out of bounds", origin_y + h < $map.$height); diff --git a/worldbuilder.hpp b/worldbuilder.hpp index 0f111ba..e4d39c6 100644 --- a/worldbuilder.hpp +++ b/worldbuilder.hpp @@ -1,6 +1,7 @@ #pragma once #include "map.hpp" +#include "dinkyecs.hpp" class WorldBuilder { public: @@ -11,11 +12,13 @@ class WorldBuilder { void partition_map(Room &cur, int depth); void make_room(size_t origin_y, size_t origin_x, size_t width, size_t height); void add_door(Room &room); - void generate(); void set_door(Room &room, int value); void place_rooms(); bool dig_tunnel(PointList &holes, Point &src, Point &target); void tunnel_doors(PointList &holes, Room &src, Room &target); void update_door(Point &at, int wall_or_space); void stylize_room(int room, string tile_name, float size); + void generate_map(); + void place_entities(DinkyECS::World &world); + void generate(DinkyECS::World &world); };