// ----------------------------------------
// Entity 클래스 (기본)
// ----------------------------------------
class Entity {
public:
int id;
float x, y;
enum class Type { Player, Monster } type;
int gridX = -1;
int gridY = -1;
Entity(int id, float x, float y, Type type)
: id(id), x(x), y(y), type(type) {}
};
// ----------------------------------------
// GridManager 확장 (시야 체크, 타입별 저장, broadcast)
// ----------------------------------------
#include <unordered_set>
#include <vector>
#include <cmath>
#include <iostream>
class GridManager {
public:
GridManager(int width, int height, int cellSize);
void addEntity(Entity* entity);
void moveEntity(Entity* entity, float newX, float newY);
void removeEntity(Entity* entity);
std::vector<Entity*> getEntitiesInViewRange(Entity* viewer, float range);
void broadcastToNearby(float x, float y, float range, const std::string& msg);
std::vector<Entity*> getEntitiesInAoEMask(float x, float y, float range, float dirDeg);
private:
struct Cell {
std::unordered_set<Entity*> players;
std::unordered_set<Entity*> monsters;
};
int width, height, cellSize;
std::vector<std::vector<Cell>> grid;
std::pair<int, int> getCellCoord(float x, float y);
void enterCell(Entity* entity, int x, int y);
void leaveCell(Entity* entity, int x, int y);
};
GridManager::GridManager(int width, int height, int cellSize)
: width(width), height(height), cellSize(cellSize) {
grid.resize(width, std::vector<Cell>(height));
}
std::pair<int, int> GridManager::getCellCoord(float x, float y) {
return { static_cast<int>(x) / cellSize, static_cast<int>(y) / cellSize };
}
void GridManager::enterCell(Entity* entity, int x, int y) {
if (x < 0 || x >= width || y < 0 || y >= height) return;
if (entity->type == Entity::Type::Player)
grid[x][y].players.insert(entity);
else
grid[x][y].monsters.insert(entity);
}
void GridManager::leaveCell(Entity* entity, int x, int y) {
if (x < 0 || x >= width || y < 0 || y >= height) return;
if (entity->type == Entity::Type::Player)
grid[x][y].players.erase(entity);
else
grid[x][y].monsters.erase(entity);
}
void GridManager::addEntity(Entity* entity) {
auto [cx, cy] = getCellCoord(entity->x, entity->y);
entity->gridX = cx;
entity->gridY = cy;
enterCell(entity, cx, cy);
}
void GridManager::moveEntity(Entity* entity, float newX, float newY) {
auto [newCX, newCY] = getCellCoord(newX, newY);
if (newCX != entity->gridX || newCY != entity->gridY) {
leaveCell(entity, entity->gridX, entity->gridY);
enterCell(entity, newCX, newCY);
entity->gridX = newCX;
entity->gridY = newCY;
}
entity->x = newX;
entity->y = newY;
}
void GridManager::removeEntity(Entity* entity) {
leaveCell(entity, entity->gridX, entity->gridY);
}
std::vector<Entity*> GridManager::getEntitiesInViewRange(Entity* viewer, float range) {
std::vector<Entity*> result;
auto [cx, cy] = getCellCoord(viewer->x, viewer->y);
int cells = static_cast<int>(std::ceil(range / cellSize));
for (int dx = -cells; dx <= cells; ++dx) {
for (int dy = -cells; dy <= cells; ++dy) {
int x = cx + dx;
int y = cy + dy;
if (x < 0 || y < 0 || x >= width || y >= height) continue;
for (auto* e : grid[x][y].players)
if (e != viewer) result.push_back(e);
for (auto* e : grid[x][y].monsters)
result.push_back(e);
}
}
return result;
}
void GridManager::broadcastToNearby(float x, float y, float range, const std::string& msg) {
auto entities = getEntitiesInAoEMask(x, y, range, 0);
for (auto* e : entities) {
std::cout << "Broadcast to Entity " << e->id << ": " << msg << "\n";
}
}
std::vector<Entity*> GridManager::getEntitiesInAoEMask(float x, float y, float range, float dirDeg) {
std::vector<Entity*> result;
auto [cx, cy] = getCellCoord(x, y);
int cells = static_cast<int>(std::ceil(range / cellSize));
float rangeSq = range * range;
float dirRad = dirDeg * 3.1415926f / 180.0f;
for (int dx = -cells; dx <= cells; ++dx) {
for (int dy = -cells; dy <= cells; ++dy) {
int nx = cx + dx;
int ny = cy + dy;
if (nx < 0 || ny < 0 || nx >= width || ny >= height) continue;
for (auto* e : grid[nx][ny].players) {
float dx = e->x - x;
float dy = e->y - y;
float distSq = dx * dx + dy * dy;
if (distSq <= rangeSq) {
result.push_back(e);
}
}
for (auto* e : grid[nx][ny].monsters) {
float dx = e->x - x;
float dy = e->y - y;
float distSq = dx * dx + dy * dy;
if (distSq <= rangeSq) {
result.push_back(e);
}
}
}
}
return result;
}
// ----------------------------------------
// SkillManager 예제: AoE 스킬 처리
// ----------------------------------------
class SkillManager {
public:
void useAoESkill(Entity* caster, GridManager& grid, float range, float dirDeg) {
auto targets = grid.getEntitiesInAoEMask(caster->x, caster->y, range, dirDeg);
for (Entity* t : targets) {
std::cout << "Skill hit Entity " << t->id << "\n";
}
grid.broadcastToNearby(caster->x, caster->y, range, "Skill Effect Shown");
}
};