diff --git a/include/Canvas.hpp b/include/Canvas.hpp index f5cd71b..4d123fe 100644 --- a/include/Canvas.hpp +++ b/include/Canvas.hpp @@ -3,23 +3,9 @@ #include #include "Engine.h" +#include "Utils.hpp" #include "Vec.hpp" -enum class Color : uint32_t { - BLACK = 0x000000ff, - WHITE = 0x00ff6000, - - BLUE = 0x000000ff, - GREEN = 0x0000ff00, - CYAN = 0x0000ffff, - RED = 0x00ff0000, - MAGENTA = 0x00ff00ff, - YELLOW = 0x00ffff00, - GRAY = 0x001f1f1f, - ORANGE = 0x00ff6000, -}; -constexpr Color CL_BG = Color::BLACK; - using Screen = uint32_t[SCREEN_HEIGHT][SCREEN_WIDTH]; namespace canvas { diff --git a/include/Map.hpp b/include/Map.hpp index 77bbdab..899f67e 100644 --- a/include/Map.hpp +++ b/include/Map.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include "Canvas.hpp" #include "Utils.hpp" @@ -50,8 +51,12 @@ public: for (const auto &food : food_) { Veci food_pos = food.pos - offset; if (utils::is_valid_pos(food_pos) and not food.eaten) { - paint::circle( - {{.pos = food_pos, .color = config_.food_color}, food.weight * 3}); + paint::circle({{ + .pos = food_pos, + .color = color::scale(config_.food_color, + 1.0 - food_lasted(food)), + }, + food.weight * 2}); } } } @@ -59,21 +64,31 @@ public: private: void generate() { ++current_gen_; - while (food_.front().gen + config_.food_exists_gens < current_gen_) { + while (food_.front().gen + config_.food_exists_gens <= current_gen_) { food_.pop_front(); } for (size_t i = 0; i < config_.gen_food_count; ++i) { food_.push_back({ .gen = current_gen_, - .pos = {.x = rand() % config_.size.x, .y = rand() % config_.size.y}, + .pos = + { + .x = std::rand() % config_.size.x, + .y = std::rand() % config_.size.y, + }, .weight = config_.min_food_weight + - rand() % std::abs(config_.max_food_weight - - config_.min_food_weight), + std::rand() % std::abs(config_.max_food_weight - + config_.min_food_weight), }); } } + double food_lasted(const Food &food) { + return ((current_gen_ - food.gen) * config_.gen_interval + + time_from_last_gen_) / + (config_.food_exists_gens * config_.gen_interval); + } + private: Config config_; double time_from_last_gen_ = 0; diff --git a/include/Player.hpp b/include/Player.hpp index a2237ae..0a29d27 100644 --- a/include/Player.hpp +++ b/include/Player.hpp @@ -5,4 +5,7 @@ struct Player { Vecf pos; Vecf direction; + double speed; + double move_interval; + double move_time_delta; }; diff --git a/include/Snake.hpp b/include/Snake.hpp index f703a66..aff7176 100644 --- a/include/Snake.hpp +++ b/include/Snake.hpp @@ -21,6 +21,12 @@ public: void draw(Veci offset = {}) const; + // + + Veci get_pos() { return pos; } + + // + size_t get_length() { return length; } void set_length(size_t length) { this->length = length; } @@ -33,6 +39,10 @@ public: } } + // + + bool touches(const Worm &other); + protected: std::deque track_; }; diff --git a/include/Utils.hpp b/include/Utils.hpp index 908f9b4..56bc6ff 100644 --- a/include/Utils.hpp +++ b/include/Utils.hpp @@ -38,6 +38,41 @@ inline constexpr Veci get_center() { } // namespace utils +namespace color { + +enum FixedColor : uint32_t { + BLACK = 0x000000ff, + WHITE = 0x00ff6000, + + BLUE = 0x000000ff, + GREEN = 0x0000ff00, + CYAN = 0x0000ffff, + RED = 0x00ff0000, + MAGENTA = 0x00ff00ff, + YELLOW = 0x00ffff00, + GRAY = 0x001f1f1f, + ORANGE = 0x00ff6000, +}; +constexpr FixedColor BG = BLACK; + +struct Color { + uint32_t v; +}; + +inline Color scale(Color color, double scale) { + static constexpr uint32_t COL_BITS = 8; + static constexpr uint32_t COMPONENTS = 4; + for (uint32_t i = COL_BITS; i < COMPONENTS * COL_BITS; i += COL_BITS) { + uint32_t comp = (color.v >> i) % (1 << COL_BITS); + color.v -= (comp << i); + color.v += (uint32_t(comp * scale) << i); + } + return color; +} + +} // namespace color +using color::Color; + // class GameObject { diff --git a/src/Canvas.cpp b/src/Canvas.cpp index 894a741..021af25 100644 --- a/src/Canvas.cpp +++ b/src/Canvas.cpp @@ -8,8 +8,7 @@ namespace paint { void square(const canvas::Square &s) { for (int x = s.pos.x; x < s.pos.x + s.side; ++x) { - std::fill(screen_at(x, s.pos.y), screen_at(x, s.pos.y + s.side), - static_cast(s.color)); + std::fill(screen_at(x, s.pos.y), screen_at(x, s.pos.y + s.side), s.color.v); } } @@ -17,8 +16,7 @@ void circle(const canvas::Circle &c) { for (int x = -c.radius; x < c.radius; ++x) { int size_y = std::sqrt(c.radius * c.radius - std::abs(x) * std::abs(x)); std::fill(screen_at(c.pos.x + x, c.pos.y - size_y), - screen_at(c.pos.x + x, c.pos.y + size_y), - static_cast(c.color)); + screen_at(c.pos.x + x, c.pos.y + size_y), c.color.v); } } diff --git a/src/Game.cpp b/src/Game.cpp index cee4fa3..d3dfb33 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1,11 +1,10 @@ #include "Engine.h" -#include +#include #include #include // #include -#include "Canvas.hpp" #include "Map.hpp" #include "Player.hpp" #include "Snake.hpp" @@ -27,13 +26,16 @@ constexpr double MIN_CONTROL_DISTANCE = 10; // left button, 1 - right button) schedule_quit_game() - quit game after act() // initialize game data in this function -void initialize() { srand(time(0)); } +void initialize() { std::srand(std::time(0)); } float game_time = 0.0f; Player player{ .pos = {.x = 20, .y = 20}, .direction = {.x = 1.0, .y = 0.0}, + .speed = 200.0, + .move_interval = 0.01, + .move_time_delta = 0, }; World world; @@ -44,17 +46,26 @@ Map map{{ .gen_food_count = 1000, .size = {.x = 20000, .y = 20000}, .min_food_weight = 1, - .max_food_weight = 10, - .food_color = Color::RED, + .max_food_weight = 5, + .food_color = {color::RED}, }}; -auto snake = Worm({{.pos = Veci(player.pos), .color = Color::GREEN}, 100, 10}); +// std::vector bots; // TODO + +auto snake = Worm(canvas::WormObject{ + {.pos = Veci(player.pos), .color = {color::GREEN}}, + 10, + 10, +}); // this function is called to update game data, // dt - time elapsed since the previous update (in seconds) void act(float dt) { - if (is_key_pressed(VK_ESCAPE)) + if (is_key_pressed(VK_ESCAPE)) { schedule_quit_game(); + } + + map.act(dt); world.cursor = utils::get_cursor(); if (world.cursor != world.prev_cursor and utils::is_valid_pos(world.cursor)) { @@ -65,17 +76,29 @@ void act(float dt) { } } - int speed = 200; - player.pos += player.direction * dt * speed; + player.pos += player.direction * dt * player.speed; game_time += dt; world.prev_cursor = world.cursor; - snake.add(Veci(player.pos)); + player.move_time_delta += dt; + if (player.move_time_delta > player.move_interval) { + player.move_time_delta -= player.move_interval; + snake.add(Veci(player.pos)); + } - snake.inc_length(map.eat(Veci(player.pos), 30)); + int eaten = map.eat(Veci(player.pos), 20); + snake.inc_length(eaten); - map.act(dt); + // TODO + // for (const auto &bot : bots) { + // if (snake.touches(bot)) { // GAME OVER + // schedule_quit_game(); + // } + // if (bot.touches(snake)) { + // // regen bot + // } + // } } // fill buffer in this function @@ -85,11 +108,10 @@ void draw() { // clear backbuffer memset(buffer, 0, SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint32_t)); - Veci map_offset = Veci(player.pos) - utils::get_center(); + Veci map_offset = Veci(/*player.pos*/ snake.get_pos()) - utils::get_center(); snake.draw(map_offset); map.draw(map_offset); - // paint::circle({{.pos = Veci(pos), .color = Color::ORANGE}, 20}); } // free game data in this function diff --git a/src/Snake.cpp b/src/Snake.cpp index 6b4215c..0c450bd 100644 --- a/src/Snake.cpp +++ b/src/Snake.cpp @@ -1,6 +1,7 @@ #include "Snake.hpp" void Worm::add(Veci pos) { + this->pos = pos; track_.push_back(pos); if (track_.size() > length) { track_.pop_front(); @@ -12,3 +13,13 @@ void Worm::draw(Veci offset) const { paint::circle({{.pos = pos - offset, .color = color}, radius}); } } + +bool Worm::touches(const Worm &other) { + int dist = radius + other.radius; + for (const auto &elem : other.track_) { + if ((pos - elem).len_sq() < dist * dist) { + return true; + } + } + return false; +}