game2006/server/gameserver/mapservice.cc
aozhiwei aca6f74bbc 1
2022-11-30 11:53:13 +08:00

362 lines
11 KiB
C++

#include "precompile.h"
#include <math.h>
#include <memory.h>
#include "mapservice.h"
#include "collider.h"
#include "entity.h"
#include "roomobstacle.h"
#include "room.h"
#include "metamgr.h"
MapService::MapService()
{
}
MapService::~MapService()
{
for (auto pair : node_hash_) {
CellNode* node = pair.second;
while (node) {
CellNode* pdelnode = node;
node = node->next;
list_del_init(&pdelnode->entry);
delete pdelnode;
}
}
node_hash_.clear();
if (map_cells_) {
free(map_cells_);
map_cells_ = nullptr;
}
}
void MapService::Init(int width, int height, int cell_width)
{
if (width < 1 || height < 1 || cell_width < 1) {
A8_ABORT();
}
map_width_ = width;
map_height_ = height;
cell_width_ = cell_width;
max_grid_id_ = map_width_ * map_height_;
grid_offset_arr_[0] = - (map_width_ + 1);
grid_offset_arr_[1] = - (map_width_ + 0);
grid_offset_arr_[2] = - (map_width_ + -1);
grid_offset_arr_[3] = - 1;
grid_offset_arr_[4] = 0;
grid_offset_arr_[5] = 1;
grid_offset_arr_[6] = map_width_ - 1;
grid_offset_arr_[7] = map_width_;
grid_offset_arr_[8] = map_width_ + 1;
map_cells_ = (list_head*)malloc(sizeof(list_head) * width * height);
memset(map_cells_, 0, sizeof(list_head) * width * height);
for (int i = 0; i < max_grid_id_; ++i) {
INIT_LIST_HEAD(&map_cells_[i]);
}
}
void MapService::UnInit()
{
}
bool MapService::CanAdd(const a8::Vec2& pos, int rad)
{
//上
if (pos.y + rad + 10 > map_height_ * cell_width_) {
return false;
}
//下
if (rad + 10 > pos.y) {
return false;
}
//左
if (rad + 10 > pos.x) {
return false;
}
//右
if (pos.x + rad + 10 > map_width_ * cell_width_) {
return false;
}
return true;
}
void MapService::AddCollider(ColliderComponent* collider)
{
if (!(collider->owner->IsEntityType(ET_Obstacle) ||
collider->owner->IsEntityType(ET_Dummy) ||
collider->owner->IsEntityType(ET_Building))) {
A8_ABORT();
}
CellNode* top_node = nullptr;
CellNode* bot_node = nullptr;
switch (collider->type) {
case CT_Aabb:
case CT_Circle:
{
AabbCollider aabb;
if (collider->type == CT_Aabb) {
aabb = *((AabbCollider*)collider);
} else if (collider->type == CT_Circle) {
CircleCollider* circle_box = (CircleCollider*)collider;
aabb.owner = circle_box->owner;
aabb._min.x = circle_box->pos.x - circle_box->rad;
aabb._min.y = circle_box->pos.y - circle_box->rad;
aabb._max.x = circle_box->pos.x + circle_box->rad;
aabb._max.y = circle_box->pos.y + circle_box->rad;
} else {
return;
}
float min_x = (collider->owner->GetPos() + aabb._min).x - 2;
float min_y = (collider->owner->GetPos() + aabb._min).y - 2;
float max_x = (collider->owner->GetPos() + aabb._max).x + 2;
float max_y = (collider->owner->GetPos() + aabb._max).y + 2;
#if 1
int min_grid_x = floor(min_x / cell_width_);
int min_grid_y = floor(min_y / cell_width_);
int max_grid_x = ceil(max_x / cell_width_);
int max_grid_y = ceil(max_y / cell_width_);
#else
int min_grid_x = ceil(min_x / cell_width_) - 1;
int min_grid_y = ceil(min_y / cell_width_) - 1;
int max_grid_x = ceil(max_x / cell_width_) - 1;
int max_grid_y = ceil(max_y / cell_width_) - 1;
#endif
if (min_grid_x == -1) {
min_grid_x = 0;
}
if (min_grid_x < 0) {
A8_ABORT();
}
if (max_grid_x >= map_width_) {
max_grid_x = map_width_ - 1;
}
if (min_grid_y == -1) {
min_grid_y = 0;
}
if (min_grid_y < 0) {
A8_ABORT();
}
if (max_grid_y >= map_height_) {
max_grid_y = map_height_ - 1;
}
for (int x = min_grid_x; x <= max_grid_x; ++x) {
for (int y = min_grid_y; y <= max_grid_y; ++y) {
int grid_id = x + y * map_width_;
list_head* head = &map_cells_[grid_id];
CellNode* node = new CellNode();
node->collider = collider;
list_add_tail(&node->entry, head);
node->next = top_node;
top_node = node;
if (!bot_node) {
bot_node = node;
}
}
}
}
break;
default:
break;
}
if (top_node) {
auto pair = node_hash_.find(collider);
if (pair != node_hash_.end()) {
bot_node->next = pair->second;
pair->second = top_node;
} else {
node_hash_[collider] = top_node;
}
}
}
void MapService::RemoveCollider(ColliderComponent* collider)
{
auto pair = node_hash_.find(collider);
if (pair != node_hash_.end()) {
CellNode* node = pair->second;
while (node) {
CellNode* pdelnode = node;
node = node->next;
list_del_init(&pdelnode->entry);
delete pdelnode;
}
node_hash_.erase(pair);
}
}
void MapService::GetColliders(Room* room,
float world_x,
float world_y,
std::set<ColliderComponent*>& colliders)
{
int center_grid_id = GetGridId(world_x, world_y);
if (center_grid_id < 0 || center_grid_id >= max_grid_id_) {
return;
}
for (int i = 0; i < a8::ArraySize(grid_offset_arr_); ++i) {
int grid_id = center_grid_id + grid_offset_arr_[i];
if (grid_id >= 0 && grid_id < max_grid_id_) {
list_head* head = &map_cells_[grid_id];
if (list_empty(head)) {
continue;
}
struct CellNode *node, *tmp;
list_for_each_entry_safe(node, tmp, head, entry) {
if (!node->collider->active) {
continue;
}
if (node->collider->tag != 0 && !a8::HasBitFlag(node->collider->tag, kHalfWallTag)) {
continue;
}
switch (node->collider->owner->GetEntityType()) {
case ET_Obstacle:
{
switch (node->collider->owner->GetEntitySubType()) {
case EST_PermanentObstacle:
{
colliders.insert(node->collider);
}
break;
case EST_RoomObstacle:
{
RoomObstacle* obstacle = (RoomObstacle*)node->collider->owner;
if (obstacle->room == room) {
colliders.insert(node->collider);
}
}
break;
default:
{
A8_ABORT();
}
break;
}
}
break;
case ET_Building:
case ET_Dummy:
{
colliders.insert(node->collider);
}
break;
default:
{
A8_ABORT();
}
break;
}
}
}
}
}
void MapService::GetSpecColliders(long long flags,
Room* room,
float world_x,
float world_y,
std::set<ColliderComponent*>& colliders)
{
int center_grid_id = GetGridId(world_x, world_y);
if (center_grid_id < 0 || center_grid_id >= max_grid_id_) {
return;
}
for (int i = 0; i < a8::ArraySize(grid_offset_arr_); ++i) {
int grid_id = center_grid_id + grid_offset_arr_[i];
if (grid_id >= 0 && grid_id < max_grid_id_) {
list_head* head = &map_cells_[grid_id];
if (list_empty(head)) {
continue;
}
struct CellNode *node, *tmp;
list_for_each_entry_safe(node, tmp, head, entry) {
if ((flags & (long long)node->collider->tag) != 0) {
colliders.insert(node->collider);
}
}
}
}
}
int MapService::GetGridId(float world_x, float world_y)
{
int grid_id = (int)(world_x/cell_width_) + (int)(world_y/cell_width_) * map_width_;
return grid_id;
}
bool MapService::CollisionDetection(Room* room,
bool through_wall,
const a8::Vec2& pos,
ColliderComponent* a_collider)
{
ColliderComponent* pickup_collider = nullptr;
return CollisionDetectionAndGetCollider
(
room,
through_wall,
pos,
a_collider,
&pickup_collider
);
}
bool MapService::CollisionDetectionAndGetCollider(Room* room,
bool through_wall,
const a8::Vec2& pos,
ColliderComponent* a_collider,
ColliderComponent** pickup_collider)
{
*pickup_collider = nullptr;
if (room->OverBorder(pos, a_collider)){
return true;
}
if (through_wall) {
return false;
}
std::set<ColliderComponent*> colliders;
GetColliders(room, pos.x, pos.y, colliders);
for (ColliderComponent* collider : colliders) {
switch (collider->owner->GetEntityType()) {
case ET_Obstacle:
{
Obstacle* obstacle = (Obstacle*)collider->owner;
if (obstacle->meta->i->prebattle_hide() && room->GetGasData().GetGasMode() == GasInactive) {
break;
}
if (obstacle->meta->i->collision_hit() != kCollisionHitPass) {
if (!obstacle->IsDead(room) && collider->IntersectEx(pos, a_collider)) {
*pickup_collider = collider;
return true;
}
}
}
break;
case ET_Building:
case ET_Dummy:
{
if (collider->IntersectEx(pos, a_collider)) {
*pickup_collider = collider;
return true;
}
}
break;
default:
break;
}
}
return false;
}