diff --git a/server/gameserver/collider.cc b/server/gameserver/collider.cc index c16e327..a90156e 100644 --- a/server/gameserver/collider.cc +++ b/server/gameserver/collider.cc @@ -95,3 +95,91 @@ bool ColliderComponent::Intersect(ColliderComponent* b) } return false; } + +bool ColliderComponent::CalcSafePoint(ColliderComponent* b, Vector2D& new_pos) +{ + switch (type) { + case CT_None: + break; + case CT_Aabb: + { + AabbCollider* a_aabb = (AabbCollider*)this; + switch (b->type) { + case CT_Aabb: + { + AabbCollider* b_aabb = (AabbCollider*)this; + return CalcAabbAabbSafePoint(a_aabb->owner->pos + a_aabb->_min, + a_aabb->owner->pos + a_aabb->_max, + b_aabb->owner->pos + b_aabb->_min, + b_aabb->owner->pos + b_aabb->_max, + new_pos); + } + break; + case CT_Circle: + { + CircleCollider* b_circle = (CircleCollider*)b; + return CalcAabbAabbSafePoint(a_aabb->owner->pos + a_aabb->_min, + a_aabb->owner->pos + a_aabb->_max, + b_circle->owner->pos + b_circle->pos, + b_circle->rad, + new_pos); + } + break; + } + } + break; + case CT_Circle: + { + CircleCollider* a_circle = (CircleCollider*)this; + switch (b->type) { + case CT_Aabb: + { + AabbCollider* b_aabb = (AabbCollider*)b; + return CalcCircleAabbSafePoint( + a_circle->owner->pos + a_circle->pos, + a_circle->rad, + b_aabb->owner->pos + b_aabb->_min, + b_aabb->owner->pos + b_aabb->_max, + new_pos); + } + break; + case CT_Circle: + { + CircleCollider* b_circle = (CircleCollider*)b; + return CalcCircleCircleSafePoint( + a_circle->owner->pos + a_circle->pos, + a_circle->rad, + b_circle->owner->pos + b_circle->pos, + b_circle->rad, + new_pos); + } + break; + } + } + break; + default: + break; + } + return false; +} + +void DestoryCollider(ColliderComponent* collider) +{ + switch (collider->type) { + case CT_Aabb: + { + delete (AabbCollider*)collider; + } + break; + case CT_Circle: + { + delete (CircleCollider*)collider; + } + break; + default: + { + delete collider; + } + break; + } +} diff --git a/server/gameserver/collider.h b/server/gameserver/collider.h index cabbe89..bf70524 100644 --- a/server/gameserver/collider.h +++ b/server/gameserver/collider.h @@ -17,6 +17,7 @@ class ColliderComponent bool Intersect(ColliderComponent* b); bool IntersectSegment(Vector2D& p0, Vector2D& p1); + bool CalcSafePoint(ColliderComponent* b, Vector2D& new_pos); }; class AabbCollider : public ColliderComponent @@ -36,3 +37,5 @@ public: CircleCollider() { type = CT_Circle; }; }; + +void DestoryCollider(ColliderComponent* collider); diff --git a/server/gameserver/collision.cc b/server/gameserver/collision.cc index df975ca..897564c 100644 --- a/server/gameserver/collision.cc +++ b/server/gameserver/collision.cc @@ -115,36 +115,69 @@ bool CircleContainCircle(Vector2D a_pos, float a_rad, Vector2D b_pos, float b_ra return distance < a_rad - b_rad; } -bool CalcCircleAabbSafePoint(Vector2D a_min, Vector2D a_max, Vector2D b_pos, float b_rad, +bool CalcCircleAabbSafePoint(Vector2D a_pos, float b_rad, Vector2D b_min, Vector2D b_max, Vector2D& new_pos) { - new_pos = b_pos; - bool at_left = std::abs(b_pos.x - a_min.x) < std::abs(b_pos.x - a_max.x); - bool at_down = std::abs(b_pos.y - a_min.y) < std::abs(b_pos.y - a_max.y); - float x_len = at_left ? std::abs(b_pos.x - a_min.x) : std::abs(b_pos.x - a_max.x); - float y_len = at_down ? std::abs(b_pos.y - a_min.y) : std::abs(b_pos.y - a_max.y); + new_pos = a_pos; + bool at_left = std::abs(a_pos.x - b_min.x) < std::abs(a_pos.x - b_max.x); + bool at_down = std::abs(a_pos.y - b_min.y) < std::abs(a_pos.y - b_max.y); + float x_len = at_left ? std::abs(a_pos.x - b_min.x) : std::abs(a_pos.x - b_max.x); + float y_len = at_down ? std::abs(a_pos.y - b_min.y) : std::abs(a_pos.y - b_max.y); if (at_left) { if (x_len < y_len) { //左 - new_pos.x = a_min.x - b_rad - 1; + new_pos.x = b_min.x - b_rad - 1; } else { if (at_down) { - new_pos.y = a_min.y - b_rad - 1; + new_pos.y = b_min.y - b_rad - 1; } else { - new_pos.y = a_max.y + b_rad + 1; + new_pos.y = b_max.y + b_rad + 1; } } } else { if (x_len < y_len) { //右 - new_pos.x = a_max.x + b_rad + 1; + new_pos.x = b_max.x + b_rad + 1; } else { if (at_down) { - new_pos.y = a_min.y - b_rad - 1; + new_pos.y = b_min.y - b_rad - 1; } else { - new_pos.y = a_max.y + b_rad + 1; + new_pos.y = b_max.y + b_rad + 1; } } } return true; } + +bool CalcCircleCircleSafePoint(Vector2D a_pos, float a_rad, Vector2D b_pos, float b_rad, + Vector2D& new_pos) +{ + Vector2D dir = a_pos - b_pos; + dir.Normalize(); + new_pos = b_pos + dir*(a_rad + b_rad) + 1; + return true; +} + +bool CalcAabbAabbSafePoint(Vector2D a_min, Vector2D a_max, Vector2D b_min, Vector2D b_max, + Vector2D& new_pos) +{ + Vector2D a_pos = a_min + (a_max - a_min)/2.0f; + float a_rad = (a_max - a_min).Norm(); + Vector2D b_pos = b_min + (b_max - b_min)/2.0f; + float b_rad = (b_max - b_min).Norm(); + Vector2D dir = (a_pos - b_pos); + dir.Normalize(); + new_pos = b_pos + dir*(a_rad + b_rad) + 1; + return true; +} + +bool CalcAabbCircleSafePoint(Vector2D a_min, Vector2D a_max, Vector2D b_pos, float b_rad, + Vector2D& new_pos) +{ + Vector2D a_pos = a_min + (a_max - a_min)/2.0f; + float a_rad = (a_max - a_min).Norm(); + Vector2D dir = a_pos - b_pos; + dir.Normalize(); + new_pos = b_pos + dir*(a_rad + b_rad) + 1; + return true; +} diff --git a/server/gameserver/collision.h b/server/gameserver/collision.h index e5a06e1..d73119b 100644 --- a/server/gameserver/collision.h +++ b/server/gameserver/collision.h @@ -6,6 +6,12 @@ bool IntersectAabbCircle(Vector2D a_min, Vector2D a_max, Vector2D b_pos, float b bool IntersectAabbAabb(Vector2D a_min, Vector2D a_max, Vector2D b_min, Vector2D b_max); bool IntersectCircleCircle(Vector2D a_pos, float a_rad, Vector2D b_pos, float b_rad); bool CircleContainCircle(Vector2D a_pos, float a_rad, Vector2D b_pos, float b_rad); -bool CalcCircleAabbSafePoint(Vector2D a_min, Vector2D a_max, Vector2D b_pos, float b_rad, +bool CalcCircleAabbSafePoint(Vector2D a_pos, float a_rad, Vector2D b_min, Vector2D b_max, + Vector2D& new_pos); +bool CalcCircleCircleSafePoint(Vector2D a_pos, float a_rad, Vector2D b_pos, float b_rad, + Vector2D& new_pos); +bool CalcAabbAabbSafePoint(Vector2D a_min, Vector2D a_max, Vector2D b_min, Vector2D b_max, + Vector2D& new_pos); +bool CalcAabbCircleSafePoint(Vector2D a_min, Vector2D a_max, Vector2D b_pos, float b_rad, Vector2D& new_pos); void TestGlm(); diff --git a/server/gameserver/entity.cc b/server/gameserver/entity.cc index f63e218..0d1c89a 100644 --- a/server/gameserver/entity.cc +++ b/server/gameserver/entity.cc @@ -3,6 +3,7 @@ #include "entity.h" #include "collider.h" #include "room.h" +#include "building.h" Entity::Entity() { @@ -19,6 +20,14 @@ void Entity::Initialize() xtimer_attacher.xtimer = &room->xtimer; } +ColliderComponent* Entity::GetBoxBound() +{ + CircleCollider* collider = new CircleCollider(); + collider->active = true; + collider->owner = this; + return collider; +} + bool Entity::TestCollision(Entity* b) { for (auto& a_collider : colliders) { @@ -35,23 +44,64 @@ void Entity::ClearColliders() { for (auto& itr : colliders) { ColliderComponent* collider = itr; - switch (collider->type) { - case CT_Aabb: - { - delete (AabbCollider*)collider; - } - break; - case CT_Circle: - { - delete (CircleCollider*)collider; - } - break; - default: - { - delete collider; - } - break; - } + DestoryCollider(collider); } colliders.clear(); } + +void Entity::FindLocationWithTarget(Entity* target) +{ + ColliderComponent* a_collider = GetBoxBound(); + Vector2D old_pos = pos; + Vector2D new_pos = pos; + #if 0 + { + std::vector objects; + room->BuildingBoxBoundCollisionDetection(this, objects); + if (objects.size() > 1) { + abort(); + } + if (!objects.empty()) { + Building* building = (Building*)objects[0]; + ColliderComponent* building_collider = building->GetBoxBound(); + bool ret = a_collider->CalcSafePoint(building_collider, new_pos); + if (!ret) { + abort(); + } + if (ret) { + pos = new_pos; + DestoryCollider(a_collider); + DestoryCollider(building_collider); + return; + } else { + DestoryCollider(building_collider); + } + } + } + #endif + ColliderComponent* target_collider = target->GetBoxBound(); + { + bool ret = a_collider->CalcSafePoint(target_collider, new_pos); + if (!ret) { + abort(); + } + } + Vector2D new_pos_dir = new_pos - old_pos; + float distance = (new_pos - old_pos).Norm(); + new_pos_dir.Normalize(); + for (float i = distance; i < 10000000; i += 5.0f) { + pos = old_pos + new_pos_dir * i; + std::vector objects; + int detection_flags = 0; + { + a8::SetBitFlag(detection_flags, ET_Obstacle); + } + room->CollisionDetection(this, detection_flags, objects); + if (objects.empty()) { + break; + } + } + + DestoryCollider(target_collider); + DestoryCollider(a_collider); +} diff --git a/server/gameserver/entity.h b/server/gameserver/entity.h index 617a5db..73fad18 100644 --- a/server/gameserver/entity.h +++ b/server/gameserver/entity.h @@ -57,6 +57,9 @@ class Entity virtual void FillMFObjectPart(cs::MFObjectPart* part_data) {}; virtual void FillMFObjectFull(cs::MFObjectFull* full_data) {}; virtual float GetSpeed() { return 1.0f;}; + virtual ColliderComponent* GetBoxBound(); bool TestCollision(Entity* b); void ClearColliders(); + void FindLocationWithTarget(Entity* target); + }; diff --git a/server/gameserver/human.cc b/server/gameserver/human.cc index 1abce4e..957e30b 100644 --- a/server/gameserver/human.cc +++ b/server/gameserver/human.cc @@ -323,7 +323,6 @@ ColliderComponent* Human::GetFirstCollision() void Human::FindPath() { - ColliderComponent* first_collider = nullptr; Vector2D old_pos = pos; { float up_dot = Vector2D::UP.Dot(move_dir); @@ -649,20 +648,20 @@ void Human::FindLocation() } if (!objects.empty()) { Building* building = (Building*)objects[0]; - Vector2D a_min = Vector2D( + Vector2D b_min = Vector2D( building->pos.x - building->meta->i->tilewidth()/2.0, building->pos.y - building->meta->i->tileheight()/2.0 ); - Vector2D a_max = Vector2D( + Vector2D b_max = Vector2D( building->pos.x + building->meta->i->tilewidth()/2.0, building->pos.y + building->meta->i->tileheight()/2.0 ); Vector2D new_pos; bool ret = CalcCircleAabbSafePoint( - a_min, - a_max, pos, GetRadius(), + b_min, + b_max, new_pos ); if (!ret) { @@ -670,7 +669,27 @@ void Human::FindLocation() } if (ret) { pos = new_pos; + return; } } } + { + std::vector objects; + int detection_flags = 0; + { + a8::SetBitFlag(detection_flags, ET_Obstacle); + a8::SetBitFlag(detection_flags, ET_Player); + } + room->CollisionDetection(this, detection_flags, objects); + if (objects.empty()) { + return; + } + std::sort(objects.begin(), objects.end(), + [this] (Entity* a, Entity *b) -> bool + { + return (this->pos - a->pos).Norm() < (this->pos - b->pos).Norm(); + }); + Entity* target = objects[0]; + FindLocationWithTarget(target); + } } diff --git a/server/gameserver/room.cc b/server/gameserver/room.cc index 6d06fd4..e199fb9 100644 --- a/server/gameserver/room.cc +++ b/server/gameserver/room.cc @@ -762,6 +762,21 @@ void Room::TouchHumanList(a8::XParams param, } } +void Room::TouchEntityList(a8::XParams param, + std::function func) +{ + if (!func) { + return; + } + for (auto& pair : uniid_hash_) { + if (pair.second) { + if (!func(pair.second, param)) { + break; + } + } + } +} + void Room::ProcAddedObjects() { if (!be_added_hash_.empty()) { diff --git a/server/gameserver/room.h b/server/gameserver/room.h index 351884e..adb1176 100644 --- a/server/gameserver/room.h +++ b/server/gameserver/room.h @@ -65,6 +65,8 @@ public: std::function func); void TouchHumanList(a8::XParams param, std::function func); + void TouchEntityList(a8::XParams param, + std::function func); void ScatterDrop(Vector2D center, int drop_id); void DropItem(Vector2D pos, int item_id, int item_count);