mirror of
https://github.com/ProgramSnail/snake_2024.git
synced 2025-12-06 06:28:43 +00:00
functional bots, small refactoring, readme edit
This commit is contained in:
parent
5299e89633
commit
7e67a3bd30
13 changed files with 179 additions and 91 deletions
19
README.md
19
README.md
|
|
@ -1,10 +1,21 @@
|
||||||
# Game (Linux)
|
# Game (Linux) - Slither.io offline clone
|
||||||
|
|
||||||
Game, based on https://github.com/imp5imp5/game_template_linux.
|
- Snake should eat apples to grow
|
||||||
|
- Current score displayed on top of the screen
|
||||||
|
- There are bots that eat apples and can not be destroyed by player. Snake is destroyed when its head intersects bot
|
||||||
|
|
||||||
### Build
|
Game is based on https://github.com/imp5imp5/game_template_linux.
|
||||||
|
|
||||||
|
|
||||||
|
### Build (xmake)
|
||||||
``sudo apt install g++ cmake libx11-dev xmake`` \
|
``sudo apt install g++ cmake libx11-dev xmake`` \
|
||||||
``xmake``
|
``xmake``
|
||||||
|
|
||||||
### Run
|
### Run (xmake)
|
||||||
``xmake run``
|
``xmake run``
|
||||||
|
|
||||||
|
### Build (cmake)
|
||||||
|
``sudo apt install g++ cmake libx11-dev`` \
|
||||||
|
``mkdir build && cd build`` \
|
||||||
|
``cmake -DCMAKE_BUILD_TYPE=Release ..`` \
|
||||||
|
``make``
|
||||||
|
|
|
||||||
|
|
@ -28,16 +28,41 @@ protected:
|
||||||
target_ = map_.find_nearest_food(get_pos());
|
target_ = map_.find_nearest_food(get_pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for case when no food found
|
|
||||||
if (target_.has_value()) {
|
if (target_.has_value()) {
|
||||||
direction_ = (Vecf(target_.value()) - real_pos_).norm();
|
if (target_ != prev_target_) {
|
||||||
// TODO: manually change direction
|
direction_ = (Vecf(target_.value()) - real_pos_).norm();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
direction_ = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prev_target_ = target_;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const Config bot_config_;
|
const Config bot_config_;
|
||||||
|
|
||||||
std::optional<Veci> target_ = {};
|
std::optional<Veci> target_ = {};
|
||||||
|
std::optional<Veci> prev_target_ = {};
|
||||||
float time_from_target_set_ = 0;
|
float time_from_target_set_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
inline Bot::Config default_bot_config = {
|
||||||
|
.time_between_targets = 0.5,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Bot default_bot(Veci pos, Map &map) {
|
||||||
|
return Bot(
|
||||||
|
{
|
||||||
|
.obj =
|
||||||
|
{
|
||||||
|
.pos = pos,
|
||||||
|
.color = {color::GRAY},
|
||||||
|
},
|
||||||
|
.initial_length = 20,
|
||||||
|
.radius = 10,
|
||||||
|
},
|
||||||
|
default_snake_config, default_bot_config, map);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,11 @@ struct Object {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Square : public Object {
|
struct Square : public Object {
|
||||||
int side;
|
int64_t side;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Circle : public Object {
|
struct Circle : public Object {
|
||||||
int radius;
|
int64_t radius;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <iostream>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "Canvas.hpp"
|
#include "Canvas.hpp"
|
||||||
|
|
@ -37,7 +36,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int eat(Veci pos, int dist) { // TODO: faster variant ?
|
int eat(Veci pos, int64_t dist) {
|
||||||
size_t eaten_weight = 0;
|
size_t eaten_weight = 0;
|
||||||
for (auto &food : food_) {
|
for (auto &food : food_) {
|
||||||
if ((pos - food.pos).len_sq() < dist * dist and not food.eaten) {
|
if ((pos - food.pos).len_sq() < dist * dist and not food.eaten) {
|
||||||
|
|
@ -48,7 +47,7 @@ public:
|
||||||
return eaten_weight;
|
return eaten_weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(Veci offset) override {
|
void draw(Veci offset) const override {
|
||||||
for (const auto &food : food_) {
|
for (const auto &food : food_) {
|
||||||
Veci food_pos = utils::to_world_coord(food.pos - offset);
|
Veci food_pos = utils::to_world_coord(food.pos - offset);
|
||||||
if (utils::is_on_screen(food_pos) and not food.eaten) {
|
if (utils::is_on_screen(food_pos) and not food.eaten) {
|
||||||
|
|
@ -92,14 +91,13 @@ private:
|
||||||
.x = utils::rand_to(config_.size.x),
|
.x = utils::rand_to(config_.size.x),
|
||||||
.y = utils::rand_to(config_.size.y),
|
.y = utils::rand_to(config_.size.y),
|
||||||
},
|
},
|
||||||
.weight = config_.min_food_weight +
|
.weight = utils::rand_in_range(config_.min_food_weight,
|
||||||
utils::rand_to(std::abs(config_.max_food_weight -
|
config_.max_food_weight),
|
||||||
config_.min_food_weight)),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double food_lasted(const Food &food) {
|
double food_lasted(const Food &food) const {
|
||||||
return ((current_gen_ - food.gen) * config_.gen_interval +
|
return ((current_gen_ - food.gen) * config_.gen_interval +
|
||||||
time_from_last_gen_) /
|
time_from_last_gen_) /
|
||||||
(config_.food_exists_gens * config_.gen_interval);
|
(config_.food_exists_gens * config_.gen_interval);
|
||||||
|
|
@ -107,6 +105,7 @@ private:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Config config_;
|
const Config config_;
|
||||||
|
|
||||||
double time_from_last_gen_ = 0;
|
double time_from_last_gen_ = 0;
|
||||||
size_t current_gen_ = 0;
|
size_t current_gen_ = 0;
|
||||||
std::deque<Food> food_ = {};
|
std::deque<Food> food_ = {};
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,9 @@
|
||||||
constexpr Veci WORLD_SIZE = {.x = 10000, .y = 10000};
|
constexpr Veci WORLD_SIZE = {.x = 10000, .y = 10000};
|
||||||
|
|
||||||
constexpr double MIN_CONTROL_DISTANCE = 10;
|
constexpr double MIN_CONTROL_DISTANCE = 10;
|
||||||
|
|
||||||
|
constexpr int64_t BOT_GEN_INTERVAL = 1000;
|
||||||
|
|
||||||
|
constexpr int64_t BOT_GEN_RAND_OFFSET = 200;
|
||||||
|
|
||||||
|
constexpr int64_t MIN_BOT_GEN_DISTANCE = 200;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ class Player : public Snake {
|
||||||
public:
|
public:
|
||||||
using Snake::Snake;
|
using Snake::Snake;
|
||||||
|
|
||||||
|
uint get_score() { return get_length() - get_initial_length(); }
|
||||||
|
|
||||||
void act(float dt) override {
|
void act(float dt) override {
|
||||||
int eaten = map_.eat(
|
int eaten = map_.eat(
|
||||||
utils::to_world_coord(get_pos() + utils::get_screen_center()), 20);
|
utils::to_world_coord(get_pos() + utils::get_screen_center()), 20);
|
||||||
|
|
@ -24,10 +26,14 @@ public:
|
||||||
move(dt);
|
move(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(Veci offset) override {
|
void draw(Veci offset) const override {
|
||||||
Snake::draw(offset - utils::get_screen_center());
|
Snake::draw(offset - utils::get_screen_center());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool touches(const SnakeObject &other, Veci offset = {}) override {
|
||||||
|
return Snake::touches(other, offset - utils::get_screen_center());
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void change_direction(float) override {
|
void change_direction(float) override {
|
||||||
Veci cursor = utils::get_cursor();
|
Veci cursor = utils::get_cursor();
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,13 @@ public:
|
||||||
struct Config {
|
struct Config {
|
||||||
Object obj;
|
Object obj;
|
||||||
uint initial_length;
|
uint initial_length;
|
||||||
int radius;
|
int64_t radius;
|
||||||
};
|
};
|
||||||
|
|
||||||
SnakeObject(Config config)
|
SnakeObject(Config config)
|
||||||
: canvas_config_(config), length_{config.initial_length},
|
: canvas_config_(config), length_{config.initial_length},
|
||||||
track_{config.obj.pos} {}
|
track_{config.obj.pos} {}
|
||||||
|
//
|
||||||
|
|
||||||
void add(Veci pos);
|
void add(Veci pos);
|
||||||
|
|
||||||
|
|
@ -50,10 +51,10 @@ public:
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
bool touches(const SnakeObject &other);
|
virtual bool touches(const SnakeObject &other, Veci offset = {});
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const Config canvas_config_;
|
Config canvas_config_;
|
||||||
|
|
||||||
size_t length_;
|
size_t length_;
|
||||||
std::deque<Veci> track_;
|
std::deque<Veci> track_;
|
||||||
|
|
@ -82,7 +83,7 @@ public:
|
||||||
move(dt);
|
move(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(Veci offset) override { SnakeObject::draw(offset); }
|
void draw(Veci offset) const override { SnakeObject::draw(offset); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void change_direction(float dt) = 0;
|
virtual void change_direction(float dt) = 0;
|
||||||
|
|
@ -97,3 +98,21 @@ protected:
|
||||||
Vecf direction_ = {};
|
Vecf direction_ = {};
|
||||||
float move_time_delta_ = 0;
|
float move_time_delta_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
template <Color color = color::WHITE, Veci pos = {}>
|
||||||
|
inline canvas::SnakeObject::Config default_snake_object_config = {
|
||||||
|
.obj =
|
||||||
|
{
|
||||||
|
.pos = pos,
|
||||||
|
.color = color,
|
||||||
|
},
|
||||||
|
.initial_length = 20,
|
||||||
|
.radius = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Snake::Config default_snake_config = {
|
||||||
|
.speed = 100.0,
|
||||||
|
.move_interval = 0.01,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
namespace canvas {
|
namespace canvas {
|
||||||
|
|
||||||
struct Text : public Object {
|
struct Text : public Object {
|
||||||
uint scale;
|
uint64_t scale;
|
||||||
uint value;
|
uint value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,14 @@ inline int rand_to(int x) {
|
||||||
return (is_negative ? -1 : 1) + rand() % std::abs(x);
|
return (is_negative ? -1 : 1) + rand() % std::abs(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int rand_in_range(int x, int y) {
|
||||||
|
if (x > y) {
|
||||||
|
std::swap(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rand_to(y - x) + x;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|
||||||
namespace color {
|
namespace color {
|
||||||
|
|
@ -107,7 +115,7 @@ using color::Color;
|
||||||
class GameObject {
|
class GameObject {
|
||||||
public:
|
public:
|
||||||
virtual void act(float dt) = 0;
|
virtual void act(float dt) = 0;
|
||||||
virtual void draw(Veci offset) = 0;
|
virtual void draw(Veci offset) const = 0;
|
||||||
|
|
||||||
virtual ~GameObject() {}
|
virtual ~GameObject() {}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
template <typename T> struct Vec {
|
template <typename T> struct Vec {
|
||||||
T x = {};
|
T x = {};
|
||||||
|
|
@ -78,7 +79,7 @@ template <typename T> struct Vec {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
int len_sq() const { return dot(*this, *this); }
|
T len_sq() const { return dot(*this, *this); }
|
||||||
|
|
||||||
double len() const { return std::sqrt(len_sq()); }
|
double len() const { return std::sqrt(len_sq()); }
|
||||||
|
|
||||||
|
|
@ -86,14 +87,14 @@ template <typename T> struct Vec {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
int static dot(Vec left, Vec right) {
|
T static dot(Vec left, Vec right) {
|
||||||
return left.x * right.x + left.y * right.y;
|
return left.x * right.x + left.y * right.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
int static cross(Vec left, Vec right) {
|
T static cross(Vec left, Vec right) {
|
||||||
return left.x * right.y - left.y * right.x;
|
return left.x * right.y - left.y * right.x;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using Veci = Vec<int>;
|
using Veci = Vec<int64_t>;
|
||||||
using Vecf = Vec<double>;
|
using Vecf = Vec<double>;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Vec.hpp"
|
|
||||||
|
|
||||||
struct World {
|
|
||||||
float game_time = {};
|
|
||||||
Veci prev_cursor = {};
|
|
||||||
Veci cursor = {};
|
|
||||||
};
|
|
||||||
126
src/Game.cpp
126
src/Game.cpp
|
|
@ -1,37 +1,21 @@
|
||||||
#include "Engine.h"
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <memory.h>
|
#include <iostream>
|
||||||
#include <stdlib.h>
|
#include <vector>
|
||||||
|
|
||||||
// #include <stdio.h>
|
// #include <stdio.h>
|
||||||
|
|
||||||
|
#include "Bot.hpp"
|
||||||
|
#include "Engine.h"
|
||||||
#include "Map.hpp"
|
#include "Map.hpp"
|
||||||
#include "Params.hpp"
|
#include "Params.hpp"
|
||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
#include "Snake.hpp"
|
#include "Snake.hpp"
|
||||||
#include "Text.hpp"
|
#include "Text.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "World.hpp"
|
|
||||||
|
|
||||||
//
|
|
||||||
// You are free to modify this file
|
|
||||||
//
|
|
||||||
|
|
||||||
// is_key_pressed(int button_vk_code) - check if a key is pressed,
|
|
||||||
// use keycodes (VK_SPACE, VK_RIGHT,
|
|
||||||
// VK_LEFT, VK_UP, VK_DOWN, VK_RETURN)
|
|
||||||
//
|
|
||||||
// get_cursor_x(), get_cursor_y() - get mouse cursor position
|
|
||||||
// is_mouse_button_pressed(int button) - check if mouse button is pressed (0 -
|
|
||||||
// left button, 1 - right button) schedule_quit_game() - quit game after act()
|
|
||||||
|
|
||||||
// initialize game data in this function
|
|
||||||
void initialize() { std::srand(std::time(0)); }
|
|
||||||
|
|
||||||
float game_time = 0.0f;
|
|
||||||
|
|
||||||
World world;
|
|
||||||
|
|
||||||
|
namespace world {
|
||||||
Map map{{
|
Map map{{
|
||||||
.gen_interval = 1.0,
|
.gen_interval = 1.0,
|
||||||
.food_exists_gens = 10,
|
.food_exists_gens = 10,
|
||||||
|
|
@ -42,18 +26,46 @@ Map map{{
|
||||||
.food_color = {color::RED},
|
.food_color = {color::RED},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
Player player{canvas::SnakeObject::Config{
|
Player player{default_snake_object_config<{color::GREEN}>,
|
||||||
.obj = {.pos = {}, .color = {color::GREEN}},
|
{
|
||||||
.initial_length = 10,
|
|
||||||
.radius = 10,
|
|
||||||
},
|
|
||||||
Snake::Config{
|
|
||||||
.speed = 200.0,
|
.speed = 200.0,
|
||||||
.move_interval = 0.01,
|
.move_interval = 0.01,
|
||||||
},
|
},
|
||||||
map};
|
map};
|
||||||
|
|
||||||
// std::vector<Bot> bots; // TODO
|
std::vector<Bot> bots;
|
||||||
|
} // namespace world
|
||||||
|
|
||||||
|
std::vector<Bot> gen_bots(Veci player_pos, Map &map) {
|
||||||
|
std::vector<Bot> bots;
|
||||||
|
|
||||||
|
bots.reserve(WORLD_SIZE.x * WORLD_SIZE.y /
|
||||||
|
(BOT_GEN_INTERVAL * BOT_GEN_INTERVAL));
|
||||||
|
for (int x = 0; x < WORLD_SIZE.x; x += BOT_GEN_INTERVAL) {
|
||||||
|
for (int y = 0; y < WORLD_SIZE.y; y += BOT_GEN_INTERVAL) {
|
||||||
|
Veci pos{
|
||||||
|
.x = x +
|
||||||
|
utils::rand_in_range(-BOT_GEN_RAND_OFFSET, BOT_GEN_RAND_OFFSET),
|
||||||
|
.y = y -
|
||||||
|
utils::rand_in_range(-BOT_GEN_RAND_OFFSET, BOT_GEN_RAND_OFFSET),
|
||||||
|
};
|
||||||
|
if ((player_pos - pos).len_sq() >=
|
||||||
|
MIN_BOT_GEN_DISTANCE * MIN_BOT_GEN_DISTANCE) {
|
||||||
|
bots.push_back(default_bot(pos, map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bots;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize game data in this function
|
||||||
|
void initialize() {
|
||||||
|
std::srand(std::time(0));
|
||||||
|
|
||||||
|
world::bots = gen_bots(world::player.get_pos() + utils::get_screen_center(),
|
||||||
|
world::map);
|
||||||
|
}
|
||||||
|
|
||||||
// this function is called to update game data,
|
// this function is called to update game data,
|
||||||
// dt - time elapsed since the previous update (in seconds)
|
// dt - time elapsed since the previous update (in seconds)
|
||||||
|
|
@ -62,22 +74,31 @@ void act(float dt) {
|
||||||
schedule_quit_game();
|
schedule_quit_game();
|
||||||
}
|
}
|
||||||
|
|
||||||
map.act(dt);
|
world::player.act(dt);
|
||||||
|
|
||||||
player.act(dt);
|
for (auto it = world::bots.begin(); it != world::bots.end();) {
|
||||||
|
it->act(dt);
|
||||||
|
|
||||||
game_time += dt;
|
if (world::player.touches(*it)) { // GAME OVER
|
||||||
world.prev_cursor = world.cursor;
|
std::cout << "game over :(\nfinal score is " << world::player.get_score()
|
||||||
|
<< std::endl;
|
||||||
|
schedule_quit_game();
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
world::map.act(dt);
|
||||||
// for (const auto &bot : bots) {
|
}
|
||||||
// if (snake.touches(bot)) { // GAME OVER
|
|
||||||
// schedule_quit_game();
|
void draw_score() {
|
||||||
// }
|
paint::text({
|
||||||
// if (bot.touches(snake)) {
|
{
|
||||||
// // regen bot
|
.pos = {.x = 10, .y = 10},
|
||||||
// }
|
.color = {color::BLUE},
|
||||||
// }
|
},
|
||||||
|
5,
|
||||||
|
world::player.get_score(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill buffer in this function
|
// fill buffer in this function
|
||||||
|
|
@ -85,18 +106,19 @@ void act(float dt) {
|
||||||
// (8 bits per R, G, B)
|
// (8 bits per R, G, B)
|
||||||
void draw() {
|
void draw() {
|
||||||
// clear backbuffer
|
// clear backbuffer
|
||||||
memset(buffer, 0, SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint32_t));
|
std::memset(buffer, 0, SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint32_t));
|
||||||
|
|
||||||
Veci map_offset = Veci(player.get_pos());
|
Veci map_offset = Veci(world::player.get_pos());
|
||||||
|
|
||||||
player.draw(map_offset);
|
world::player.draw(map_offset);
|
||||||
map.draw(map_offset);
|
|
||||||
|
|
||||||
paint::text({
|
for (const auto &bot : world::bots) {
|
||||||
{.pos = {.x = 10, .y = 10}, .color = {color::BLUE}},
|
bot.draw(map_offset);
|
||||||
5,
|
}
|
||||||
player.get_length() - player.get_initial_length(),
|
|
||||||
});
|
world::map.draw(map_offset);
|
||||||
|
|
||||||
|
draw_score();
|
||||||
}
|
}
|
||||||
|
|
||||||
// free game data in this function
|
// free game data in this function
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ void SnakeObject::draw(Veci offset) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SnakeObject::touches(const SnakeObject &other) {
|
bool SnakeObject::touches(const SnakeObject &other, Veci offset) {
|
||||||
int dist = canvas_config_.radius + other.canvas_config_.radius;
|
int64_t dist = canvas_config_.radius + other.canvas_config_.radius;
|
||||||
for (const auto &elem : other.track_) {
|
for (const auto &elem : other.track_) {
|
||||||
if ((get_pos() - elem).len_sq() < dist * dist) {
|
if ((get_pos() - elem - offset).len_sq() < dist * dist) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue