Compare commits

..

14 Commits

Author SHA1 Message Date
m
73b49dd403 Pushing whats there 2024-11-22 13:46:04 -05:00
m
70b762b2ce Some corrections to cellular automata generation 2023-07-23 15:41:01 -04:00
53e3bf7fa4 Merge pull request 'feature/rla-19_Zoom' (#20) from feature/rla-19_Zoom into master
Reviewed-on: #20
2023-07-23 18:39:48 +00:00
m
ddfa3c1dc9 Zoom fully working and code cleaned up, scaling x and y now done in game rather than rendering, closes #19 2023-07-23 14:39:16 -04:00
m
3d056481b2 Merge branch 'master' into feature/rla-19_Zoom 2023-07-23 14:34:34 -04:00
m
206868efc5 Working zoom 2023-07-23 14:33:59 -04:00
f2f875a976 Merge pull request 'Implements cellular automata, closes #13' (#16) from feature/rla-13_CellularAutomata into master
Reviewed-on: #16
2023-07-23 17:45:59 +00:00
m
a11e4fa5a1 Implements cellular automata, closes #13 2023-07-23 13:45:25 -04:00
f5bd7c949a Merge pull request 'Removes the test alog, cleans up some renderer stuff, starts work on cellular automata, closes #14' (#15) from feature/rla-14_AlgorithmsUI into master
Reviewed-on: #15
2023-07-23 14:58:44 +00:00
m
f6b6293532 Removes the test alog, cleans up some renderer stuff, starts work on cellular automata, closes #14 2023-07-23 10:58:01 -04:00
daef8463bf Merge pull request 'feature/rla-11_AlgorithmsP1' (#12) from feature/rla-11_AlgorithmsP1 into master
Reviewed-on: #12
2023-07-23 05:04:15 +00:00
m
abf3e3a719 Cleaned up drunken walk, closes #11 2023-07-23 01:03:10 -04:00
m
d7fa6069f8 Drunken walk working but needs some updates as it can go 'off map' and then walk back in, effectively 2023-07-23 00:57:19 -04:00
55e2186c86 Merge pull request 'feature/rla-7_Logging' (#10) from feature/rla-7_Logging into master
Reviewed-on: #10
2023-07-19 02:01:13 +00:00
16 changed files with 304 additions and 34 deletions

View File

@@ -3,7 +3,7 @@ tile_size_w = 48
tile_size_h = 48
[map]
map_size_w = 20
map_size_h = 20
map_size_w = 80
map_size_h = 50
floor_char = '.'
wall_char = '#'

View File

@@ -23,5 +23,6 @@ Necessary:
init_function(map_w, map_h)
Should initialize the function fresh with a given width and height.
update(map)
update()
Called updates_per_second times per second. Should calculate new map state and draw on the map as needed. The map will have the previous state retained.
Returns true if finished, else false

View File

@@ -0,0 +1,84 @@
CellularAutomata = {
map_w = 0,
map_h = 0,
iterations = 0,
}
function CellularAutomata.get_neighbors(map, x, y)
local min_x = math.max(0, x-1)
local min_y = math.max(0, y-1)
local max_x = math.min(x+1, map:get_width()-1)
local max_y = math.min(y+1, map:get_height()-1)
local result = {}
for mx = min_x, max_x do
for my = min_y, max_y do
table.insert(result, {x = mx, y = my})
end
end
return result
end
function CellularAutomata:initialize(w, h)
self.map_w = w
self.map_h = h
self.iterations = 3
math.randomseed()
for mx = 0, w-1 do
for my = 0, h-1 do
local is_wall = math.random(0, 1) == 1
if is_wall then
visualizer.map:draw_wall(mx, my)
else
visualizer.map:draw_floor(mx, my)
end
end
end
end
function CellularAutomata:update()
if self.iterations <= 0 then
return true
end
local old_map = visualizer.map
local new_map = old_map:clone()
for mx = 0, self.map_w - 1 do
for my = 0, self.map_h - 1 do
local neighbors = self.get_neighbors(old_map, mx, my)
local expected_neighbors = 9
local walls = expected_neighbors - #neighbors
local required_walls = 5
--print(old_map:is_wall(mx, my))
--print(new_map:is_wall(mx, my))
for k,v in ipairs(neighbors) do
if old_map:is_wall(v.x, v.y) then
walls = walls + 1
end
end
if walls >= required_walls then
new_map:draw_wall(mx, my)
else
new_map:draw_floor(mx, my)
end
--print(old_map:is_wall(mx, my))
--print(new_map:is_wall(mx, my))
end
end
-- Now draw the new map onto the existing one
for mx = 0, self.map_w - 1 do
for my = 0, self.map_h - 1 do
if new_map:is_wall(mx, my) then
old_map:draw_wall(mx, my)
else
old_map:draw_floor(mx, my)
end
end
end
self.iterations = self.iterations - 1
return false
end
visualizer.algorithm_manager:register_algorithm("Cellular Automata", function (w, h) CellularAutomata:initialize(w, h) end, function () return CellularAutomata:update() end, 1)

View File

@@ -0,0 +1,36 @@
DrunkenWalk = {
curr_x = 0,
curr_y = 0,
map_w = 0,
map_h = 0,
iterations = 0
}
function DrunkenWalk:initialize(w, h)
math.randomseed()
self.map_w = w
self.map_h = h
self.iterations = 200
visualizer.map:fill(true)
self.curr_x = math.random(0, w-1)
self.curr_y = math.random(0, h-1);
end
function DrunkenWalk:update()
new_x = self.curr_x + math.random(-1, 1)
new_y = self.curr_y + math.random(-1, 1)
while new_x >= self.map_w or new_x < 0 or new_y >= self.map_h or new_y < 0 do
new_x = self.curr_x + math.random(-1, 1)
new_y = self.curr_y + math.random(-1, 1)
end
self.curr_x = new_x
self.curr_y = new_y
visualizer.map:draw_floor(self.curr_x, self.curr_y)
self.iterations = self.iterations - 1
if self.iterations == 0 then
return true
end
return false
end
visualizer.algorithm_manager:register_algorithm("Drunken Walk", function (w, h) DrunkenWalk:initialize(w, h) end, function () return DrunkenWalk:update() end, 60)

View File

@@ -62,6 +62,8 @@ public:
virtual void draw_point(int x, int y, Color color) = 0;
virtual void draw_text(std::optional<TextRenderDetails> details, std::string text,int x, int y)=0;
virtual void draw_sprite(Sprite sprite, int x, int y)=0;
virtual void draw_sprite(Sprite sprite, int x, int y, float scale)=0;
virtual TextRenderDetails get_default_text_render_details()=0;
virtual ~Renderer() = default;
};

View File

@@ -20,7 +20,7 @@ void SdlRenderer::draw_text(std::optional<TextRenderDetails> details, std::strin
TextRenderDetails unpacked_details = details.value_or(this->renderer_params.default_font);
std::shared_ptr<TTF_Font> font = this->font_manager->fetch_resource(SdlFontArgs(unpacked_details.size, unpacked_details.font_path));
std::shared_ptr<SDL_Texture> texture = this->texture_manager->load_text_as_texture(text, font, unpacked_details.color);
render_texture(x, y, texture, nullptr);
render_texture(x, y, texture, nullptr, nullptr);
}
SdlRenderer::~SdlRenderer() {
@@ -34,7 +34,7 @@ SdlRenderer::~SdlRenderer() {
SDL_Quit();
}
void SdlRenderer::render_texture(int x, int y, const std::shared_ptr<SDL_Texture>& texture, SDL_Rect *src) {
void SdlRenderer::render_texture(int x, int y, const std::shared_ptr<SDL_Texture>& texture, SDL_Rect *src, SDL_Rect *dest) {
int w, h;
if(src == nullptr) {
SDL_QueryTexture(texture.get(), nullptr, nullptr, &w, &h);
@@ -43,11 +43,21 @@ void SdlRenderer::render_texture(int x, int y, const std::shared_ptr<SDL_Texture
w = src->w;
h = src->h;
}
SDL_Rect rect = {x,y,w,h};
SDL_RenderCopy(this->renderer.get(), texture.get(), src, &rect);
SDL_Rect destination_rect;
if(dest == nullptr) {
destination_rect = {x,y,w,h};
}
else {
destination_rect = *dest;
}
SDL_RenderCopy(this->renderer.get(), texture.get(), src, &destination_rect);
}
void SdlRenderer::draw_sprite(Sprite sprite, int x, int y) {
this->draw_sprite(sprite, x, y, 1);
}
void SdlRenderer::draw_sprite(Sprite sprite, int x, int y, float scale) {
SpriteSheet *sprite_sheet = sprite.sprite_sheet;
TextureManagerFetchArgs args = TextureManagerFetchArgs {sprite_sheet->path, sprite_sheet->color_key};
std::shared_ptr<SDL_Texture> texture = this->texture_manager->fetch_resource(args);
@@ -57,7 +67,8 @@ void SdlRenderer::draw_sprite(Sprite sprite, int x, int y) {
int src_y = l*sprite_sheet->sprite_height;
int src_x = (sprite_sheet->sprite_width * sprite.index) % w;
auto source = SDL_Rect{src_x, src_y, sprite_sheet->sprite_width, sprite_sheet->sprite_height};
render_texture(x, y, texture, &source);
auto dest = SDL_Rect {x, y, (int)std::ceil(sprite_sheet->sprite_width * scale), (int)std::ceil(sprite_sheet->sprite_height * scale)};
render_texture(x, y, texture, &source, &dest);
}
SdlRenderer::SdlRenderer(RendererParams renderer_params) : renderer_params(std::move(renderer_params)) {
@@ -74,7 +85,7 @@ SdlRenderer::SdlRenderer(RendererParams renderer_params) : renderer_params(std::
printf("Failed to initialize font loading: %s\n", TTF_GetError());
exit(EXIT_FAILURE);
}
SDL_Window* sdl_window = SDL_CreateWindow(renderer_params.title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, renderer_params.width, renderer_params.height, SDL_WINDOW_SHOWN);
SDL_Window* sdl_window = SDL_CreateWindow(this->renderer_params.title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, this->renderer_params.width, this->renderer_params.height, SDL_WINDOW_SHOWN);
if(sdl_window == nullptr) {
printf("error making window: %s\n", SDL_GetError());
exit(EXIT_FAILURE);

View File

@@ -18,22 +18,22 @@ struct SDL_WindowDeleter {
class SdlRenderer : public Renderer {
public:
~SdlRenderer() override;
SdlRenderer(RendererParams renderer_params);
explicit SdlRenderer(RendererParams renderer_params);
void draw_text(std::optional<TextRenderDetails> details, std::string text, int x, int y) override;
void flush() override;
void draw_point(int x, int y, Color color) override;
void draw_sprite(Sprite sprite, int x, int y) override;
void draw_sprite(Sprite sprite, int x, int y, float scale) override;
TextRenderDetails get_default_text_render_details() override {
return this->renderer_params.default_font;
}
private:
RendererParams renderer_params;
std::unique_ptr<SDL_Window, SDL_WindowDeleter> window;
std::shared_ptr<SDL_Renderer> renderer = nullptr;
std::unique_ptr<SdlTextureManager> texture_manager = nullptr;
std::unique_ptr<SdlFontManager> font_manager = nullptr;
void render_texture(int x, int y, const std::shared_ptr<SDL_Texture>& texture, SDL_Rect *src);
void render_texture(int x, int y, const std::shared_ptr<SDL_Texture>& texture, SDL_Rect *src, SDL_Rect *dest);
};

View File

@@ -11,6 +11,6 @@ std::string LuaContextManager::get_key(std::string args) {
// TODO: Switch the arg to a struct and use that to init libraries
std::shared_ptr<sol::state> LuaContextManager::load_resource(std::string args) {
auto lua = std::make_shared<sol::state>();
lua->open_libraries(sol::lib::base, sol::lib::package);
lua->open_libraries(sol::lib::base, sol::lib::package, sol::lib::math, sol::lib::table);
return lua;
}

View File

@@ -25,7 +25,9 @@ void Camera::set_bounds(int max_x, int max_y) {
this->cached_bounds = calculate_bounds();
}
Rectangle Camera::calculate_bounds() {
Rectangle Camera::calculate_bounds() const {
auto tiles_rendered_x = this->get_tiles_rendered_x();
auto tiles_rendered_y = this->get_tiles_rendered_y();
int minx = std::clamp(center.x-(tiles_rendered_x/2), 0, std::max(0,max_x - tiles_rendered_x));
int miny = std::clamp(center.y-(tiles_rendered_y/2), 0, std::max(0,max_y - tiles_rendered_y));
int width = std::min(tiles_rendered_x, max_x-minx);
@@ -50,3 +52,20 @@ ranges::any_view<Point> Camera::get_range() {
});
}) | ranges::views::join;
}
float Camera::get_scale() const {
return scale;
}
void Camera::set_scale(float scale) {
this->scale = scale;
this->cached_bounds = calculate_bounds();
}
int Camera::get_tiles_rendered_x() const {
return (int)std::ceil(tiles_rendered_x / this->scale);
}
int Camera::get_tiles_rendered_y() const {
return (int)std::ceil(tiles_rendered_y / this->scale);
}

View File

@@ -20,7 +20,15 @@ private:
int max_x=0;
int max_y=0;
Rectangle cached_bounds=Rectangle();
Rectangle calculate_bounds();
Rectangle calculate_bounds() const;
float scale = 1;
[[nodiscard]] int get_tiles_rendered_x() const;
[[nodiscard]] int get_tiles_rendered_y() const;
public:
[[nodiscard]] float get_scale() const;
void set_scale(float scale);
public:
Rectangle get_bounds();
void move_camera(Point new_center);

View File

@@ -3,8 +3,10 @@
//
#include "algorithmmanager.hpp"
#include "../engine/modules/logger.hpp"
#include <utility>
#include <fmt/format.h>
void AlgorithmManager::register_algorithm(std::string name, sol::function initialize, sol::function update,
unsigned int updates_per_second) {
@@ -19,6 +21,42 @@ void AlgorithmManager::register_algorithm(std::string name, sol::function initia
}
ranges::any_view<DungeonAlgorithm> AlgorithmManager::get_algorithms() {
return algorithms;
ranges::any_view<DungeonAlgorithm*, ranges::category::random_access | ranges::category::sized> AlgorithmManager::get_algorithms() {
auto view = algorithms | ranges::view::transform([](auto& algo) {
return &algo;
});
return view;
}
void AlgorithmManager::load_algorithm(DungeonAlgorithm *algorithm, int w, int h) {
algorithm_done = false;
ticks = 0;
loaded_algorithm = algorithm;
auto init_result = loaded_algorithm->initialize(w, h);
if(!init_result.valid()) {
sol::error err = init_result;
Logger::log(LogLevel::ERROR, fmt::format("error while initializing lua: {}", err.what()));
}
}
void AlgorithmManager::tick_algorithm() {
if(this->loaded_algorithm == nullptr || algorithm_done) {
return;
}
if(ticks >= this->loaded_algorithm->frames_per_update) {
ticks = 0;
auto update_result = this->loaded_algorithm->update();
if(update_result.valid()) {
algorithm_done = update_result;
if(algorithm_done) {
Logger::log(LogLevel::DEBUG, "Finished running algorithm");
}
}
else {
algorithm_done = true;
sol::error err = update_result;
Logger::log(LogLevel::ERROR, fmt::format("An error occurred while running update: {}", err.what()));
}
}
ticks++;
}

View File

@@ -8,8 +8,8 @@
#include <range/v3/view.hpp>
struct DungeonAlgorithm {
unsigned int frames_per_update=1;
sol::function update;
sol::function initialize;
sol::protected_function update;
sol::protected_function initialize;
std::string name;
};
@@ -17,17 +17,18 @@ class AlgorithmManager {
private:
std::vector<DungeonAlgorithm> algorithms;
unsigned int target_fps=1;
unsigned int ticks=0;
DungeonAlgorithm* loaded_algorithm = nullptr;
bool algorithm_done = false;
public:
void register_algorithm(std::string name, sol::function initialize, sol::function update, unsigned int updates_per_second);
void setTargetFps(unsigned int targetFps) {
target_fps = targetFps;
ranges::any_view<DungeonAlgorithm*, ranges::category::random_access | ranges::category::sized> get_algorithms();
void load_algorithm(DungeonAlgorithm* algorithm, int w, int h);
void tick_algorithm();
DungeonAlgorithm* get_currently_running_algorithm() {
return algorithm_done ? nullptr : loaded_algorithm;
}
ranges::any_view<DungeonAlgorithm> get_algorithms();
explicit AlgorithmManager(unsigned int targetFps) : target_fps(targetFps) {}
AlgorithmManager() = default;
};

View File

@@ -4,12 +4,16 @@
#include "tilemap.hpp"
#include "../engine/utility/point.hpp"
#include "../engine/modules/logger.hpp"
#include <fmt/format.h>
void TileMap::draw_floor(int x, int y) {
Logger::log(LogLevel::DEBUG, fmt::format("Drawing floor at x: {} y: {}", x, y));
grid.insert(x, y, floor_tile);
}
void TileMap::draw_wall(int x, int y) {
Logger::log(LogLevel::DEBUG, fmt::format("Drawing wall at x: {} y: {}", x, y));
grid.insert(x, y, wall_tile);
}
@@ -18,6 +22,7 @@ char TileMap::get_tile(int x, int y) {
}
void TileMap::fill(bool wall) {
Logger::log(LogLevel::DEBUG, fmt::format("Running fill with wall: {}", wall));
for(Point p : grid.get_range()) {
if(wall) {
draw_wall(p.x, p.y);
@@ -26,7 +31,6 @@ void TileMap::fill(bool wall) {
draw_floor(p.x, p.y);
}
}
}
bool TileMap::is_wall(int x, int y) {
@@ -40,3 +44,7 @@ int TileMap::get_width() {
int TileMap::get_height() {
return grid.get_height();
}
TileMap TileMap::clone() {
return {this->get_width(), this->get_height(), this->wall_tile, this->floor_tile};
}

View File

@@ -21,6 +21,7 @@ public:
bool is_wall(int x, int y);
int get_width();
int get_height();
TileMap clone();
TileMap(int width, int height, char wall_tile, char floor_tile) {
this->grid = Grid2D<char>(width, height);

View File

@@ -2,6 +2,7 @@
// Created by m on 12/3/21.
//
#include <filesystem>
#include "visualizer.hpp"
#include "tilemap.hpp"
@@ -9,13 +10,24 @@ bool Visualizer::update(InputResult input) {
if(input.keycode == KEY_ESCAPE) {
return true;
}
this->algorithm_manager.tick_algorithm();
switch(input.keycode) {
case KEY_RIGHT: x += 1; break;
case KEY_LEFT: x -= 1; break;
case KEY_UP: y -= 1; break;
case KEY_DOWN: y += 1; break;
case KEY_KP_PLUS:
this->camera.set_scale(this->camera.get_scale() * 2); break;
case KEY_KP_MINUS:
this->camera.set_scale(this->camera.get_scale() / 2); break;
default: break;
}
if(input.keycode >= InputProcessorKeycode::KEY_1 && input.keycode <= InputProcessorKeycode::KEY_9) {
if(this->keycode_to_algorithm.contains(input.keycode)) {
auto algorithm = this->keycode_to_algorithm[input.keycode];
this->algorithm_manager.load_algorithm(algorithm, this->map_size_w, this->map_size_h);
}
}
this->x = std::clamp(this->x, 0, (int)this->tile_map.get_width()-1);
this->y = std::clamp(this->y, 0, (int)this->tile_map.get_height()-1);
@@ -28,6 +40,9 @@ bool Visualizer::update(InputResult input) {
void Visualizer::render(Renderer *renderer) {
SpriteSheet tiles = SpriteSheet{"sprites/map1.bmp", tile_width, tile_height};
SpriteSheet characters = SpriteSheet{"sprites/character.bmp", tile_width, tile_height, COLOR_BLACK};
float scale = this->camera.get_scale();
int scaled_tile_width = (int)std::ceil(tile_width * scale);
int scaled_tile_height = (int)std::ceil(tile_height * scale);
auto grass = Sprite{&tiles, 0};
auto wall = Sprite{&tiles, 497};
auto character = Sprite{&characters, this->sprite_index};
@@ -35,15 +50,27 @@ void Visualizer::render(Renderer *renderer) {
auto wp = camera.camera_coords_to_screen_coords(p);
int wx = wp.x, wy = wp.y;
if(this->tile_map.get_tile(p.x, p.y) == floor_char) {
renderer->draw_sprite(grass, wx*tile_width, wy*tile_height);
renderer->draw_sprite(grass, wx*scaled_tile_width, wy*scaled_tile_height, scale);
}
else {
renderer->draw_sprite(wall, wx*tile_width, wy*tile_height);
renderer->draw_sprite(wall, wx*scaled_tile_width, wy*scaled_tile_height, scale);
}
if(p.x == x && p.y == y) {
renderer->draw_sprite(character, wx*tile_width, wy*tile_height);
renderer->draw_sprite(character, wx*scaled_tile_width, wy*scaled_tile_height, scale);
}
}
// Draw keycode to algo mappings
auto default_font_details = renderer->get_default_text_render_details();
auto running_algorithm = this->algorithm_manager.get_currently_running_algorithm();
for(auto pairing : this->keycode_to_algorithm) {
auto index = pairing.first-InputProcessorKeycode::KEY_1;
auto draw_params = default_font_details;
if(pairing.second == running_algorithm) {
draw_params.color = COLOR_RED;
}
renderer->draw_text(draw_params, fmt::format("{}: {}", index+1, pairing.second->name), 0, this->window_height-((index+1)*default_font_details.size));
}
}
void Visualizer::initialize(GameInitArgs args) {
@@ -63,6 +90,7 @@ void Visualizer::initialize(GameInitArgs args) {
this->camera = Camera(Point{x,y}, tilesx, tilesy, tile_map.get_width(), tile_map.get_height());
this->algorithm_manager = AlgorithmManager(args.target_fps);
this->initialize_context();
this->load_algorithms();
Logger::log(LogLevel::DEBUG, "Game initialization finished");
}
@@ -83,9 +111,40 @@ void Visualizer::initialize_context() {
tilemap_lua["draw_wall"] = &TileMap::draw_wall;
tilemap_lua["fill"] = &TileMap::fill;
tilemap_lua["is_wall"] = &TileMap::is_wall;
tilemap_lua["clone"] = &TileMap::clone;
tilemap_lua["get_width"] = &TileMap::get_width;
tilemap_lua["get_height"] = &TileMap::get_height;
auto algo_manager_lua = lua->new_usertype<AlgorithmManager>("AlgorithmManager", sol::no_constructor);
algo_manager_lua["register_algorithm"] = &AlgorithmManager::register_algorithm;
auto game_state_lua = lua->new_usertype<Visualizer>("Visualizer", sol::no_constructor);
game_state_lua.set("algorithm_manager", sol::readonly(&Visualizer::algorithm_manager));
game_state_lua.set("map", &Visualizer::tile_map);
lua->set("visualizer", this);
}
void Visualizer::load_algorithms() {
auto lua = lua_context_manager.get_default_context();
const std::string algorithms_directory = "scripts/algorithms";
for(const auto& file : std::filesystem::directory_iterator(algorithms_directory)) {
lua->script_file(file.path());
}
Logger::log(LogLevel::DEBUG, "finished loading lua files");
auto algorithms = this->algorithm_manager.get_algorithms();
auto algorithm_count = algorithms.size();
Logger::log(LogLevel::DEBUG, fmt::format("algorithms: {}", algorithm_count));
for(int code = InputProcessorKeycode::KEY_1;code < InputProcessorKeycode::KEY_9;code++) {
auto index = code-InputProcessorKeycode::KEY_1;
if(algorithms.size() <= index) {
break;
}
auto algo = algorithms[index];
this->keycode_to_algorithm[(InputProcessorKeycode)code] = algo;
}
}
Visualizer::~Visualizer() = default;

View File

@@ -38,8 +38,9 @@ private:
int window_height = 0;
TileMap tile_map = TileMap(0, 0, DEFAULT_WALL_CHAR[0], DEFAULT_FLOOR_CHAR[0]);
Camera camera = Camera();
LuaContextManager lua_context_manager = LuaContextManager();
LuaContextManager lua_context_manager;
AlgorithmManager algorithm_manager;
std::unordered_map<InputProcessorKeycode, DungeonAlgorithm*> keycode_to_algorithm;
int x = 0;
int y=0;
@@ -54,6 +55,7 @@ private:
void initialize_map(int width, int height);
void initialize_context();
void load_algorithms();
};