diff --git a/include/Canvas.hpp b/include/Canvas.hpp index f842dbd..f5cd71b 100644 --- a/include/Canvas.hpp +++ b/include/Canvas.hpp @@ -1,12 +1,9 @@ #pragma once -#include #include -#include -# #include "Engine.h" -#include "Utils.hpp" +#include "Vec.hpp" enum class Color : uint32_t { BLACK = 0x000000ff, @@ -25,99 +22,27 @@ constexpr Color CL_BG = Color::BLACK; using Screen = uint32_t[SCREEN_HEIGHT][SCREEN_WIDTH]; -class CanvasObject { -public: - struct Config { - Veci pos; - int size; - Color color = Color ::WHITE; - }; - CanvasObject(Config config) - : pos_(config.pos), size_(config.size), color_(config.color), - shown_(false) {} +namespace canvas { - virtual void move_to(Veci) = 0; - virtual void show() = 0; - virtual void hide() = 0; - - virtual ~CanvasObject() { - if (shown_) { - // hide(); // TODO - } - } - -protected: - Veci pos_; - int size_; - Color color_; - bool shown_; +struct Object { + Veci pos; + Color color; }; -class StatelessCanvasObject : public CanvasObject { -public: - using CanvasObject::CanvasObject; - - void move_to(Veci pos) override { - if (not shown_) { - return; - } - hide(); - pos_ = pos; - show(); - } - - void show() override { fill(color_); } - - void hide() override { fill(CL_BG); } - -protected: - virtual void fill(Color color) = 0; +struct Square : public Object { + int side; }; -class Square : public StatelessCanvasObject { -public: - using StatelessCanvasObject::StatelessCanvasObject; - -protected: - void fill(Color color) override { - for (int x = pos_.x; x < pos_.x + size_; ++x) { - std::fill(screen_at(x, pos_.y), screen_at(x, pos_.y + size_), - static_cast(color)); - } - } +struct Circle : public Object { + int radius; }; -class Circle : public StatelessCanvasObject { -public: - using StatelessCanvasObject::StatelessCanvasObject; +} // namespace canvas -protected: - void fill(Color color) override { - for (int x = -size_; x < size_; ++x) { - int size_y = std::sqrt(size_ * size_ - std::abs(x) * std::abs(x)); - std::fill(screen_at(pos_.x + x, pos_.y - size_y), - screen_at(pos_.x + x, pos_.y + size_y), - static_cast(color)); - } - } -}; +namespace paint { -class Worm : public CanvasObject { - Worm(CanvasObject::Config config, size_t max_track_size) - : CanvasObject(config), max_track_size_(max_track_size) {} +void square(const canvas::Square &s); -protected: - const size_t max_track_size_; - std::vector track_; -}; +void circle(const canvas::Circle &c); -// class Canvas { -// public: -// Canvas(Screen &screen) : screen_(screen) {} - -// size_t insert(CanvasObject obj) {} -// void erase(size_t id) {} - -// private: -// Screen &screen_; -// }; +} // namespace paint diff --git a/include/Map.hpp b/include/Map.hpp index faa239b..77bbdab 100644 --- a/include/Map.hpp +++ b/include/Map.hpp @@ -2,6 +2,7 @@ #include +#include "Canvas.hpp" #include "Utils.hpp" #include "Vec.hpp" @@ -13,17 +14,23 @@ class Map : public GameObject { bool eaten = false; }; - static constexpr double GEN_INTERVAL = 1.0; - static constexpr size_t FOOD_EXISTS_GENS = 10; - static constexpr size_t GEN_FOOD_COUNT = 1000; - static constexpr int SIZE_X = 20000; - static constexpr int SIZE_Y = 20000; - public: + struct Config { + double gen_interval; + size_t food_exists_gens; + size_t gen_food_count; + Veci size; + int min_food_weight; + int max_food_weight; + Color food_color; + }; + + Map(Config config) : config_(config) {} + void act(float dt) override { time_from_last_gen_ += dt; - if (time_from_last_gen_ > GEN_INTERVAL) { - time_from_last_gen_ -= GEN_INTERVAL; + if (time_from_last_gen_ > config_.gen_interval) { + time_from_last_gen_ -= config_.gen_interval; generate(); } } @@ -39,23 +46,36 @@ public: return eaten_weight; } - void draw() override {} // TODO - -private: - void generate() { - ++current_gen_; - while (food_.front().gen + FOOD_EXISTS_GENS < current_gen_) { - food_.pop_front(); - } - - for (size_t i = 0; i < GEN_FOOD_COUNT; ++i) { - food_.push_back({.gen = current_gen_, - .pos = {.x = rand() % SIZE_X, .y = rand() % SIZE_Y}, - .weight = 1}); + void draw(Veci offset) override { + 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}); + } } } private: + void generate() { + ++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}, + .weight = config_.min_food_weight + + rand() % std::abs(config_.max_food_weight - + config_.min_food_weight), + }); + } + } + +private: + Config config_; double time_from_last_gen_ = 0; size_t current_gen_ = 0; std::deque food_ = {}; diff --git a/include/Player.hpp b/include/Player.hpp index 7132100..a2237ae 100644 --- a/include/Player.hpp +++ b/include/Player.hpp @@ -1,6 +1,8 @@ #pragma once -class Player { -public: -private: +#include "Vec.hpp" + +struct Player { + Vecf pos; + Vecf direction; }; diff --git a/include/Snake.hpp b/include/Snake.hpp index e69de29..f703a66 100644 --- a/include/Snake.hpp +++ b/include/Snake.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "Canvas.hpp" + +namespace canvas { + +struct WormObject : public Object { + size_t length; + int radius; +}; + +} // namespace canvas + +class Worm : protected canvas::WormObject { +public: + Worm(canvas::WormObject obj) : WormObject(obj), track_{pos} {} + + void add(Veci pos); + + void draw(Veci offset = {}) const; + + size_t get_length() { return length; } + + void set_length(size_t length) { this->length = length; } + + void inc_length(int inc) { + if (-inc > static_cast(length)) { + length = 0; + } else { + length += inc; + } + } + +protected: + std::deque track_; +}; diff --git a/include/Utils.hpp b/include/Utils.hpp index c68737e..908f9b4 100644 --- a/include/Utils.hpp +++ b/include/Utils.hpp @@ -5,6 +5,8 @@ #include "Engine.h" #include "Vec.hpp" +namespace utils { + namespace safe { inline int lx(int x) { return std::max(0, x); } @@ -30,12 +32,18 @@ template inline bool is_valid_pos(Vec pos) { inline Veci get_cursor() { return {.x = get_cursor_y(), .y = get_cursor_x()}; } +inline constexpr Veci get_center() { + return {.x = SCREEN_HEIGHT / 2, .y = SCREEN_WIDTH / 2}; +} + +} // namespace utils + // class GameObject { public: virtual void act(float dt) = 0; - virtual void draw() = 0; + virtual void draw(Veci offset) = 0; virtual ~GameObject() {} }; diff --git a/include/Vec.hpp b/include/Vec.hpp index 0352b80..4f35b1f 100644 --- a/include/Vec.hpp +++ b/include/Vec.hpp @@ -3,13 +3,13 @@ #include template struct Vec { - T x; - T y; + T x = {}; + T y = {}; // template explicit operator Vec() { - return Vec{.x = x, .y = y}; + return Vec{.x = static_cast(x), .y = static_cast(y)}; } // diff --git a/include/World.hpp b/include/World.hpp new file mode 100644 index 0000000..3571cca --- /dev/null +++ b/include/World.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "Vec.hpp" + +struct World { + float game_time = {}; + Veci prev_cursor = {}; + Veci cursor = {}; +}; diff --git a/src/Canvas.cpp b/src/Canvas.cpp index e69de29..894a741 100644 --- a/src/Canvas.cpp +++ b/src/Canvas.cpp @@ -0,0 +1,25 @@ +#include "Canvas.hpp" + +#include "Utils.hpp" + +using namespace utils; + +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)); + } +} + +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)); + } +} + +} // namespace paint diff --git a/src/Game.cpp b/src/Game.cpp index 3c4dbfd..cee4fa3 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -6,6 +6,11 @@ // #include #include "Canvas.hpp" +#include "Map.hpp" +#include "Player.hpp" +#include "Snake.hpp" +#include "Utils.hpp" +#include "World.hpp" constexpr double MIN_CONTROL_DISTANCE = 10; @@ -22,12 +27,28 @@ 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() {} +void initialize() { srand(time(0)); } float game_time = 0.0f; -Vecf pos = {.x = 20, .y = 20}; -Vecf direction{.x = 1.0, .y = 0.0}; -Veci prev_cursor = {.x = 0, .y = 0}; + +Player player{ + .pos = {.x = 20, .y = 20}, + .direction = {.x = 1.0, .y = 0.0}, +}; + +World world; + +Map map{{ + .gen_interval = 1.0, + .food_exists_gens = 10, + .gen_food_count = 1000, + .size = {.x = 20000, .y = 20000}, + .min_food_weight = 1, + .max_food_weight = 10, + .food_color = Color::RED, +}}; + +auto snake = Worm({{.pos = Veci(player.pos), .color = Color::GREEN}, 100, 10}); // this function is called to update game data, // dt - time elapsed since the previous update (in seconds) @@ -35,23 +56,26 @@ void act(float dt) { if (is_key_pressed(VK_ESCAPE)) schedule_quit_game(); - Veci cursor = get_cursor(); - if (cursor != prev_cursor and is_valid_pos(cursor)) { - Vecf diff = Vecf(cursor) - pos; + world.cursor = utils::get_cursor(); + if (world.cursor != world.prev_cursor and utils::is_valid_pos(world.cursor)) { + Vecf diff(world.cursor - utils::get_center()); // - pos; if (diff.len() > MIN_CONTROL_DISTANCE) { - direction = diff.norm(); + player.direction = diff.norm(); } } int speed = 200; - pos += direction * dt * speed; + player.pos += player.direction * dt * speed; game_time += dt; - prev_cursor = cursor; - // std::cout << "pos: " << pos.x << ' ' << pos.y << std::endl; - // std::cout << "curs: " << get_cursor().x << ' ' << get_cursor().y << - // std::endl; + world.prev_cursor = world.cursor; + + snake.add(Veci(player.pos)); + + snake.inc_length(map.eat(Veci(player.pos), 30)); + + map.act(dt); } // fill buffer in this function @@ -61,8 +85,11 @@ void draw() { // clear backbuffer memset(buffer, 0, SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint32_t)); - Circle sq({.pos = Veci(pos), .size = 20}); - sq.show(); + Veci map_offset = Veci(player.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 new file mode 100644 index 0000000..6b4215c --- /dev/null +++ b/src/Snake.cpp @@ -0,0 +1,14 @@ +#include "Snake.hpp" + +void Worm::add(Veci pos) { + track_.push_back(pos); + if (track_.size() > length) { + track_.pop_front(); + } +} + +void Worm::draw(Veci offset) const { + for (const auto &pos : track_) { + paint::circle({{.pos = pos - offset, .color = color}, radius}); + } +}