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.

main
Zed A. Shaw 2 weeks ago
parent 31e5eb7fce
commit f2864a62ee
  1. 15
      assets/enemies.json
  2. 8
      assets/items.json
  3. 59
      main.cpp
  4. 3
      status.txt
  5. 2
      tests/gui.cpp
  6. 2
      tests/lighting.cpp
  7. 2
      tests/map.cpp
  8. 4
      tests/matrix.cpp
  9. 2
      tests/save.cpp
  10. 2
      tests/tilemap.cpp
  11. 4
      tests/worldbuilder.cpp
  12. 87
      worldbuilder.cpp
  13. 5
      worldbuilder.hpp

@ -2,31 +2,36 @@
"PLAYER_TILE": { "PLAYER_TILE": {
"foreground": [255, 200, 125], "foreground": [255, 200, 125],
"background": [30, 20, 75], "background": [30, 20, 75],
"hp": 100,
"damage": 10,
"display":"\ua66b" "display":"\ua66b"
}, },
"ENEMY_TILE": {
"foreground": [100, 200, 125],
"background": [30, 20, 75],
"display":"\u1d5c"
},
"SNAKE": { "SNAKE": {
"foreground": [90, 172, 74], "foreground": [90, 172, 74],
"background": [30, 20, 75], "background": [30, 20, 75],
"hp": 15,
"damage": 5,
"display":"\u06b1" "display":"\u06b1"
}, },
"GOBLIN": { "GOBLIN": {
"foreground": [50, 200, 125], "foreground": [50, 200, 125],
"background": [30, 20, 75], "background": [30, 20, 75],
"hp": 75,
"damage": 30,
"display":"\u06bf" "display":"\u06bf"
}, },
"UNICORN": { "UNICORN": {
"foreground": [25, 200, 125], "foreground": [25, 200, 125],
"background": [30, 20, 75], "background": [30, 20, 75],
"hp": 50,
"damage": 20,
"display":"\u17a5" "display":"\u17a5"
}, },
"RAT": { "RAT": {
"foreground": [75, 200, 125], "foreground": [75, 200, 125],
"background": [30, 20, 75], "background": [30, 20, 75],
"hp": 5,
"damage": 1,
"display":"\u08ac" "display":"\u08ac"
} }
} }

@ -5,6 +5,7 @@
"foreground": [24, 205, 189], "foreground": [24, 205, 189],
"background": [230, 20, 120], "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.", "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" "display": "\u0f08"
}, },
"SWORD_RUSTY": { "SWORD_RUSTY": {
@ -13,6 +14,7 @@
"foreground": [24, 205, 189], "foreground": [24, 205, 189],
"background": [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.", "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" "display":"\u1e37"
}, },
"CHEST_SMALL": { "CHEST_SMALL": {
@ -20,8 +22,9 @@
"name": "Small Chest", "name": "Small Chest",
"foreground": [24, 205, 189], "foreground": [24, 205, 189],
"background": [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": { "WALL_TORCH": {
"id": "WALL_TORCH", "id": "WALL_TORCH",
@ -29,6 +32,7 @@
"foreground": [24, 205, 189], "foreground": [24, 205, 189],
"background": [24, 205, 189], "background": [24, 205, 189],
"description": "A torch on a wall you can't pick up.", "description": "A torch on a wall you can't pick up.",
"type": "FIXED_LIGHT",
"display": "☀" "display": "☀"
} }
} }

@ -20,62 +20,6 @@ using namespace components;
using lighting::LightSource; using lighting::LightSource;
namespace fs = std::filesystem; 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<GameConfig>();
// configure a player as a fact of the world
Player player{world.entity()};
world.set_the<Player>(player);
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.enemies["PLAYER_TILE"]["display"]});
world.set<LightSource>(player.entity, {50,1.0});
world.set<Inventory>(player.entity, {5});
auto sword = world.entity();
auto pos = game_map.place_entity(1);
world.set<Position>(sword, {pos.x+1, pos.y+1});
world.set<Tile>(sword, {config.items["SWORD_RUSTY"]["display"]});
world.set<InventoryItem>(sword, {1, config.items["SWORD_RUSTY"]});
world.set<Weapon>(sword, {20});
auto torch = world.entity();
pos = game_map.place_entity(2);
world.set<Position>(torch, {pos.x+1, pos.y+1});
world.set<Tile>(torch, {config.items["TORCH_BAD"]["display"]});
world.set<InventoryItem>(torch, {1, config.items["TORCH_BAD"]});
world.set<LightSource>(torch, {70,2.0f});
auto enemy = world.entity();
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.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, {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, {config.items["CHEST_SMALL"]["display"]});
world.set<InventoryItem>(gold, {1, config.items["CHEST_SMALL"]});
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, {config.items["WALL_TORCH"]["display"]});
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
DinkyECS::World world; DinkyECS::World world;
Map game_map(GAME_MAP_X, GAME_MAP_Y); 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); save::from_file(save_path, world, game_map);
} else { } else {
WorldBuilder builder(game_map); WorldBuilder builder(game_map);
builder.generate(); builder.generate(world);
configure_world(world, game_map);
} }
SpatialMap collider; SpatialMap collider;

@ -1,5 +1,8 @@
TODAY'S GOAL: 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. * 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. * 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. * Colision fails when you place two entities on the same square, but the init_positions adds them and one deletes the other.

@ -18,7 +18,7 @@ TEST_CASE("load a basic gui run but don't loop", "[gui]") {
save::load_configs(world); save::load_configs(world);
Map game_map(40, 40); Map game_map(40, 40);
WorldBuilder builder(game_map); WorldBuilder builder(game_map);
builder.generate(); builder.generate_map();
auto &config = world.get_the<GameConfig>(); auto &config = world.get_the<GameConfig>();
// configure a player as a fact of the world // configure a player as a fact of the world

@ -12,7 +12,7 @@ using namespace lighting;
TEST_CASE("lighting a map works", "[lighting]") { TEST_CASE("lighting a map works", "[lighting]") {
Map map(20,23); Map map(20,23);
WorldBuilder builder(map); WorldBuilder builder(map);
builder.generate(); builder.generate_map();
Point light1 = map.place_entity(0); Point light1 = map.place_entity(0);
Point light2 = map.place_entity(1); Point light2 = map.place_entity(1);

@ -17,7 +17,7 @@ json load_test_data(const string &fname) {
TEST_CASE("camera control", "[map]") { TEST_CASE("camera control", "[map]") {
Map map(20, 20); Map map(20, 20);
WorldBuilder builder(map); WorldBuilder builder(map);
builder.generate(); builder.generate_map();
Point center = map.center_camera({10,10}, 5, 5); Point center = map.center_camera({10,10}, 5, 5);

@ -190,7 +190,7 @@ TEST_CASE("prototype flood algorithm", "[matrix:flood]") {
Map map(width,height); Map map(width,height);
WorldBuilder builder(map); WorldBuilder builder(map);
builder.generate(); builder.generate_map();
if(map.room_count() < 2) continue; if(map.room_count() < 2) continue;
@ -282,7 +282,7 @@ TEST_CASE("viewport iterator", "[matrix:viewport]") {
size_t height = Random::uniform<size_t>(21, 25); size_t height = Random::uniform<size_t>(21, 25);
Map map(width,height); Map map(width,height);
WorldBuilder builder(map); WorldBuilder builder(map);
builder.generate(); builder.generate_map();
size_t view_width = width/2; size_t view_width = width/2;
size_t view_height = height/2; size_t view_height = height/2;

@ -58,7 +58,7 @@ TEST_CASE("basic save a world", "[save]") {
DinkyECS::World world; DinkyECS::World world;
Map map(20, 20); Map map(20, 20);
WorldBuilder builder(map); WorldBuilder builder(map);
builder.generate(); builder.generate_map();
// 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()};

@ -15,7 +15,7 @@ TEST_CASE("tilemap can load tiles and make a map", "[tilemap]") {
Map map(width,height); Map map(width,height);
WorldBuilder builder(map); WorldBuilder builder(map);
builder.generate(); builder.generate_map();
TileMap tiles(map.width(), map.height()); TileMap tiles(map.width(), map.height());
auto& walls = map.walls(); auto& walls = map.walls();

@ -12,13 +12,13 @@ using std::string;
TEST_CASE("bsp algo test", "[builder]") { TEST_CASE("bsp algo test", "[builder]") {
Map map(31, 20); Map map(31, 20);
WorldBuilder builder(map); WorldBuilder builder(map);
builder.generate(); builder.generate_map();
} }
TEST_CASE("pathing", "[builder]") { TEST_CASE("pathing", "[builder]") {
Map map(23, 14); Map map(23, 14);
WorldBuilder builder(map); WorldBuilder builder(map);
builder.generate(); builder.generate_map();
matrix::dump("WALLS", map.$walls, 0,0); matrix::dump("WALLS", map.$walls, 0,0);
println("wall at 0,0=={}", map.$walls[0][0]); println("wall at 0,0=={}", map.$walls[0][0]);

@ -2,8 +2,10 @@
#include "rand.hpp" #include "rand.hpp"
#include <fmt/core.h> #include <fmt/core.h>
#include <iostream> #include <iostream>
#include "components.hpp"
using namespace fmt; using namespace fmt;
using namespace components;
inline int make_split(Room &cur, bool horiz) { inline int make_split(Room &cur, bool horiz) {
size_t dimension = horiz ? cur.height : cur.width; 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; PointList holes;
Room root{ Room root{
.x = 0, .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<GameConfig>();
auto item = world.entity();
auto pos = game_map.place_entity(in_room);
json item_data = config.items[name];
world.set<Position>(item, {pos.x+1, pos.y+1});
world.set<Tile>(item, {item_data["display"]});
if(item_data["type"] == "WEAPON") {
world.set<InventoryItem>(item, {1, item_data});
world.set<Weapon>(item, {20});
} else if(item_data["type"] == "LIGHT") {
world.set<InventoryItem>(item, {1, item_data});
world.set<LightSource>(item, {70,2.0f});
} else if(item_data["type"] == "LOOT") {
world.set<InventoryItem>(item, {1, item_data});
world.set<Loot>(item, {100});
} else if(item_data["type"] == "FIXED_LIGHT") {
world.set<LightSource>(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<GameConfig>();
auto enemy = world.entity();
auto enemy_data = config.enemies[name];
world.set<Position>(enemy, {game_map.place_entity(in_room)});
world.set<Motion>(enemy, {0,0});
world.set<Tile>(enemy, {enemy_data["display"]});
world.set<Combat>(enemy, {enemy_data["hp"], enemy_data["damage"]});
return enemy;
}
void WorldBuilder::place_entities(DinkyECS::World &world) {
auto &config = world.get_the<GameConfig>();
// 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>(player);
world.set<Combat>(player.entity, {100, 10});
world.set<LightSource>(player.entity, {50,1.0});
world.set<Inventory>(player.entity, {5});
{
std::vector<std::string> 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<std::string> 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) { void WorldBuilder::make_room(size_t origin_x, size_t origin_y, size_t w, size_t h) {
$map.INVARIANT(); $map.INVARIANT();
dbc::pre("y out of bounds", origin_y + h < $map.$height); dbc::pre("y out of bounds", origin_y + h < $map.$height);

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "map.hpp" #include "map.hpp"
#include "dinkyecs.hpp"
class WorldBuilder { class WorldBuilder {
public: public:
@ -11,11 +12,13 @@ class WorldBuilder {
void partition_map(Room &cur, int depth); 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 make_room(size_t origin_y, size_t origin_x, size_t width, size_t height);
void add_door(Room &room); void add_door(Room &room);
void generate();
void set_door(Room &room, int value); void set_door(Room &room, int value);
void place_rooms(); void place_rooms();
bool dig_tunnel(PointList &holes, Point &src, Point &target); bool dig_tunnel(PointList &holes, Point &src, Point &target);
void tunnel_doors(PointList &holes, Room &src, Room &target); void tunnel_doors(PointList &holes, Room &src, Room &target);
void update_door(Point &at, int wall_or_space); void update_door(Point &at, int wall_or_space);
void stylize_room(int room, string tile_name, float size); void stylize_room(int room, string tile_name, float size);
void generate_map();
void place_entities(DinkyECS::World &world);
void generate(DinkyECS::World &world);
}; };

Loading…
Cancel
Save