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.

This commit is contained in:
Zed A. Shaw 2025-01-06 15:20:54 -05:00
parent 31e5eb7fce
commit f2864a62ee
13 changed files with 119 additions and 76 deletions

View file

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

View file

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

View file

@ -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<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[]) {
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;

View file

@ -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.

View file

@ -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<GameConfig>();
// configure a player as a fact of the world

View file

@ -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);

View file

@ -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);

View file

@ -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<size_t>(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;

View file

@ -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()};

View file

@ -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();

View file

@ -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]);

View file

@ -2,8 +2,10 @@
#include "rand.hpp"
#include <fmt/core.h>
#include <iostream>
#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<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) {
$map.INVARIANT();
dbc::pre("y out of bounds", origin_y + h < $map.$height);

View file

@ -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);
};