diff --git a/server/gameserver/building.cc b/server/gameserver/building.cc index ec8db6c..751af79 100644 --- a/server/gameserver/building.cc +++ b/server/gameserver/building.cc @@ -33,7 +33,7 @@ void Building::RecalcSelfCollider() obj.y() - obj.height()/2.0 - meta->i->tileheight()/2.0); collider->_max = Vector2D(obj.x() + obj.width()/2.0 - meta->i->tilewidth()/2.0, obj.y() + obj.height()/2.0 - meta->i->tileheight()/2.0); - colliders.push_back(collider); + AddCollider(collider); } } diff --git a/server/gameserver/bullet.cc b/server/gameserver/bullet.cc index 0a4d909..1a3bbeb 100644 --- a/server/gameserver/bullet.cc +++ b/server/gameserver/bullet.cc @@ -44,7 +44,7 @@ void Bullet::RecalcSelfCollider() if (!self_collider_) { self_collider_ = new CircleCollider(); self_collider_->owner = this; - colliders.push_back(self_collider_); + AddCollider(self_collider_); } self_collider_->pos = Vector2D(); self_collider_->rad = gun_meta->i->bullet_rad(); @@ -99,7 +99,6 @@ void Bullet::OnHit(std::set& objects) obstacle->meta->i->damage() > 0.01f) { obstacle->Explosion(this); } - obstacle->ClearColliders(); room->ScatterDrop(obstacle->pos, obstacle->meta->i->drop()); } obstacle->BroadcastFullState(); diff --git a/server/gameserver/constant.h b/server/gameserver/constant.h index adbc317..17ade5a 100755 --- a/server/gameserver/constant.h +++ b/server/gameserver/constant.h @@ -165,3 +165,5 @@ const int FIGHTING_MODE_BULLET_NUM = 10000 * 10000; const int MAX_NODE_ID = 8; const int MAX_INSTANCE_ID = 500; + +const int WALK_ZONE_WIDTH = 100; diff --git a/server/gameserver/entity.cc b/server/gameserver/entity.cc index 0a1b301..6e3d339 100644 --- a/server/gameserver/entity.cc +++ b/server/gameserver/entity.cc @@ -46,6 +46,9 @@ void Entity::GetCircleBox(CircleCollider& circle_box) bool Entity::TestCollision(Entity* b) { App::Instance()->perf.test_times++; + if (b->dead) { + return false; + } for (auto& a_collider : colliders) { for (auto& b_collider : b->colliders) { if (a_collider->Intersect(b_collider)) { @@ -156,3 +159,8 @@ void Entity::BroadcastDeleteState() } } } + +void Entity::AddCollider(ColliderComponent* collider) +{ + colliders.push_back(collider); +} diff --git a/server/gameserver/entity.h b/server/gameserver/entity.h index c17de48..2f521a5 100644 --- a/server/gameserver/entity.h +++ b/server/gameserver/entity.h @@ -50,9 +50,10 @@ class Entity Room* room = nullptr; Vector2D pos; int updated_times = 0; - std::list colliders; bool deleted = false; a8::XTimerAttacher xtimer_attacher; + bool dead = false; + long long dead_frameno = 0; int grid_id = 0; std::set grid_list; @@ -69,9 +70,13 @@ class Entity virtual void GetAabbBox(AabbCollider& aabb_box); virtual void GetCircleBox(CircleCollider& circle_box); bool TestCollision(Entity* b); - void ClearColliders(); void FindLocationWithTarget(Entity* target); void BroadcastFullState(); void BroadcastDeleteState(); + void AddCollider(ColliderComponent* collider); +private: + std::list colliders; + + void ClearColliders(); }; diff --git a/server/gameserver/hero.cc b/server/gameserver/hero.cc index eca2f77..ec1a32f 100644 --- a/server/gameserver/hero.cc +++ b/server/gameserver/hero.cc @@ -25,7 +25,7 @@ void Hero::RecalcSelfCollider() if (!self_collider_) { self_collider_ = new CircleCollider(); self_collider_->owner = this; - colliders.push_back(self_collider_); + AddCollider(self_collider_); } self_collider_->pos = Vector2D(); self_collider_->rad = master->GetRadius(); diff --git a/server/gameserver/human.cc b/server/gameserver/human.cc index f486305..db012de 100644 --- a/server/gameserver/human.cc +++ b/server/gameserver/human.cc @@ -47,12 +47,19 @@ Human::Human():Entity() inventory_[IS_12GAUGE] = FIGHTING_MODE_BULLET_NUM; inventory_[IS_RPG] = FIGHTING_MODE_BULLET_NUM; } + walk_zone = new AabbCollider(); + walk_zone->_min.x = -(WALK_ZONE_WIDTH / 2.0f); + walk_zone->_min.y = -(WALK_ZONE_WIDTH / 2.0f); + walk_zone->_max.x = WALK_ZONE_WIDTH / 2.0f; + walk_zone->_max.y = WALK_ZONE_WIDTH / 2.0f; } Human::~Human() { delete movement ; movement = nullptr; + delete walk_zone; + walk_zone = nullptr; } void Human::Initialize() @@ -279,7 +286,7 @@ void Human::RecalcSelfCollider() if (!self_collider_) { self_collider_ = new CircleCollider(); self_collider_->owner = this; - colliders.push_back(self_collider_); + AddCollider(self_collider_); } self_collider_->pos = Vector2D(); self_collider_->rad = meta->i->radius(); @@ -327,6 +334,43 @@ bool Human::IsCollision() return !objects.empty(); } +bool Human::IsCollisionInWalkZone() +{ + if (room->OverBorder(pos, meta->i->radius())){ + return true; + } + + if (a8::HasBitFlag(status, HS_Jump)) { + return false; + } + + if (last_collider && !last_collider->owner->dead) { + CircleCollider circle_box; + GetCircleBox(circle_box); + if (circle_box.Intersect(last_collider)) { + if (last_collision_door != last_collider->owner) { + return true; + } + } + } + + for (Entity* entity : seen_objects) { + switch (entity->entity_type) { + case ET_Obstacle: + case ET_Building: + { + if (!entity->dead && TestCollision(entity)) { + if (last_collision_door != entity) { + return true; + } + } + } + break; + } + } + return false; +} + void Human::FindPath() { Vector2D old_pos = pos; @@ -372,6 +416,51 @@ void Human::FindPath() pos = old_pos; } +void Human::FindPathInWalkZone() +{ + Vector2D old_pos = pos; + { + float up_dot = Vector2D::UP.Dot(move_dir); + bool at_left_side = Vector2D::LEFT.Dot(move_dir) > 0.0001f; + if (std::abs(up_dot) <= 0.001f) { //相互垂直 + //向上 + pos = old_pos + Vector2D::UP; + if (!IsCollisionInWalkZone()) { + return; + } else { + //向下 + pos = old_pos + Vector2D::DOWN; + if (!IsCollisionInWalkZone()) { + return; + } + } + } else if (up_dot > 0.001f) { //基本相同 + pos = old_pos + (at_left_side ? Vector2D::LEFT : Vector2D::RIGHT); + if (!IsCollisionInWalkZone()) { + return; + } else { + //向上 + pos = old_pos + Vector2D::UP; + if (!IsCollisionInWalkZone()) { + return; + } + } + } else if (up_dot < 0.001f) { //基本相反 + pos = old_pos + (at_left_side ? Vector2D::LEFT : Vector2D::RIGHT); + if (!IsCollisionInWalkZone()) { + return; + } else { + //向下 + pos = old_pos + Vector2D::DOWN; + if (!IsCollisionInWalkZone()) { + return; + } + } + } + } + pos = old_pos; +} + float Human::GetRadius() { return meta->i->radius(); @@ -1719,6 +1808,104 @@ int Human::GetSkinConfigLv(int skin_id) return itr != skin_configs.end() ? itr->second : 0; } +void Human::GenerateWalkZone() +{ + in_walk_zone = true; + walk_zone_center = pos; + seen_colliders.clear(); + seen_objects.clear(); + last_collider = nullptr; + for (auto& grid : grid_list) { + for (Entity* entity : grid->entity_list) { + switch (entity->entity_type) { + case ET_Obstacle: + case ET_Building: + { + if ( + (last_collision_door == nullptr || last_collision_door != entity) + ){ + AabbCollider aabb_box; + entity->GetAabbBox(aabb_box); + if (IntersectAabbAabb(walk_zone_center + walk_zone->_min, + walk_zone_center + walk_zone->_max, + aabb_box.owner->pos + aabb_box._min, + aabb_box.owner->pos + aabb_box._max) + ) { + seen_objects.insert(entity); + } + } + } + break; + default: + { + } + break; + } + } + }//end for +} + +bool Human::InWalkZone() +{ + float delta = 10.0f; + if (pos.x - meta->i->radius() < walk_zone_center.x + walk_zone->_min.x - delta) { + return false; + } + if (pos.x + meta->i->radius() > walk_zone_center.x + walk_zone->_max.x - delta) { + return false; + } + if (pos.y - meta->i->radius() < walk_zone_center.y + walk_zone->_min.y - delta) { + return false; + } + if (pos.y + meta->i->radius() > walk_zone_center.y + walk_zone->_max.y - delta) { + return false; + } + return false; +} + +void Human::ClearWalkZone() +{ + in_walk_zone = false; +} + +void Human::UpdateMoveInWalkZone(int speed) +{ + for (int i = 0; i < speed; ++i) { + Vector2D old_pos = pos; + pos = pos + move_dir; + if (InWalkZone()) { + if (IsCollisionInWalkZone()) { + pos = old_pos; + FindPathInWalkZone(); + if (rand() % 3 == 0) { + i += 1; + } + } + } else { + ClearWalkZone(); + pos = old_pos; + UpdateMoveInMap(speed - i); + return; + } + room->grid_service.MoveHuman(this); + } +} + +void Human::UpdateMoveInMap(int speed) +{ + for (int i = 0; i < speed; ++i) { + Vector2D old_pos = pos; + pos = pos + move_dir; + if (IsCollision()) { + pos = old_pos; + GenerateWalkZone(); + UpdateMoveInWalkZone(speed - i); + return; + } + room->grid_service.MoveHuman(this); + } +} + void Human::ClearFrameData() { if (!new_objects.empty()) { diff --git a/server/gameserver/human.h b/server/gameserver/human.h index c684a8f..46089c8 100644 --- a/server/gameserver/human.h +++ b/server/gameserver/human.h @@ -29,6 +29,7 @@ enum HumanStatus struct xtimer_list; class CircleCollider; +class AabbCollider; class Obstacle; class MovementComponent; class Human : public Entity @@ -123,7 +124,9 @@ class Human : public Entity void Shot(Vector2D& target_dir); void RecalcSelfCollider(); bool IsCollision(); + bool IsCollisionInWalkZone(); void FindPath(); + void FindPathInWalkZone(); float GetRadius(); float GetMaxHP(); void UpdatePoisoning(); @@ -186,6 +189,13 @@ class Human : public Entity int GetWeaponConfigLv(int weapon_id); int GetSkinConfigLv(int skin_id); +protected: + void GenerateWalkZone(); + bool InWalkZone(); + void ClearWalkZone(); + void UpdateMoveInWalkZone(int speed); + void UpdateMoveInMap(int speed); + private: void ClearFrameData(); void GenBattleReportData(a8::MutableXObject* params); @@ -222,6 +232,13 @@ protected: bool follow_synced_active_player = false; MovementComponent* movement = nullptr; + bool in_walk_zone = false; + Vector2D walk_zone_center; + AabbCollider* walk_zone = nullptr; + std::set seen_colliders; + std::set seen_objects; + ColliderComponent* last_collider = nullptr; + private: CircleCollider* self_collider_ = nullptr; long long last_sync_gas_frameno = 0; diff --git a/server/gameserver/obstacle.cc b/server/gameserver/obstacle.cc index 3b9cae6..cb0e896 100644 --- a/server/gameserver/obstacle.cc +++ b/server/gameserver/obstacle.cc @@ -33,7 +33,7 @@ void Obstacle::RecalcSelfCollider() if (!self_collider2_) { self_collider2_ = new AabbCollider(); self_collider2_->owner = this; - colliders.push_back(self_collider2_); + AddCollider(self_collider2_); } if (door_state == DoorStateClose) { self_collider2_->_min = Vector2D(0.0f - door_state0->width() / 2.0f, @@ -51,7 +51,7 @@ void Obstacle::RecalcSelfCollider() if (!self_collider_) { self_collider_ = new CircleCollider(); self_collider_->owner = this; - colliders.push_back(self_collider_); + AddCollider(self_collider_); } self_collider_->pos = Vector2D(); self_collider_->rad = meta->i->height() / 2.0; @@ -62,7 +62,7 @@ void Obstacle::RecalcSelfCollider() if (!self_collider2_) { self_collider2_ = new AabbCollider(); self_collider2_->owner = this; - colliders.push_back(self_collider2_); + AddCollider(self_collider2_); } self_collider2_->_min = Vector2D(meta->i->width() / -2.0f, meta->i->height() / -2.0f); self_collider2_->_max = Vector2D(meta->i->width() / 2.0f, meta->i->height() / 2.0f); @@ -216,7 +216,6 @@ void Obstacle::Explosion(Bullet* bullet) obstacle->dead = obstacle->health <= 0.01f; obstacle->dead_frameno = room->frame_no; if (obstacle->dead) { - obstacle->ClearColliders(); room->ScatterDrop(obstacle->pos, obstacle->meta->i->drop()); } obstacle->BroadcastFullState(); diff --git a/server/gameserver/obstacle.h b/server/gameserver/obstacle.h index c7e234f..4d8a47a 100644 --- a/server/gameserver/obstacle.h +++ b/server/gameserver/obstacle.h @@ -25,8 +25,6 @@ class Obstacle : public Entity public: MetaData::MapThing* meta = nullptr; float health = 0.0f; - bool dead = false; - long long dead_frameno = 0; bool is_door = false; int door_id = 0; diff --git a/server/gameserver/player.cc b/server/gameserver/player.cc index 622cad8..3eff93c 100644 --- a/server/gameserver/player.cc +++ b/server/gameserver/player.cc @@ -13,6 +13,7 @@ #include "loot.h" #include "app.h" #include "movement.h" +#include "collider.h" Player::Player():Human() { @@ -121,6 +122,7 @@ void Player::UpdateMove() #endif return; } +#if 0 int speed = std::max(1, (int)GetSpeed()); #ifdef RAY_DETECTION for (int i = 0; i < speed; ++i) { @@ -162,6 +164,12 @@ void Player::UpdateMove() room->grid_service.MoveHuman(this); } #endif +#endif + if (in_walk_zone) { + UpdateMoveInWalkZone(std::max(1, (int)GetSpeed())); + } else { + UpdateMoveInMap(std::max(1, (int)GetSpeed())); + } if (last_collision_door && !TestCollision(last_collision_door)) { last_collision_door = nullptr; }