game2005/server/gameserver/obstacle.cc
aozhiwei 780a22c734 1
2021-06-17 23:42:36 +08:00

734 lines
22 KiB
C++

#include "precompile.h"
#include "obstacle.h"
#include "metamgr.h"
#include "room.h"
#include "collider.h"
#include "building.h"
#include "human.h"
#include "app.h"
#include "typeconvert.h"
#include "bullet.h"
#include "perfmonitor.h"
enum ObstacleDataFlags_e
{
kHealth = 1
};
Obstacle::Obstacle():Entity()
{
++PerfMonitor::Instance()->entity_num[ET_Obstacle];
}
Obstacle::~Obstacle()
{
--PerfMonitor::Instance()->entity_num[ET_Obstacle];
}
void Obstacle::Initialize()
{
Entity::Initialize();
health_ = meta->i->hp();
RecalcSelfCollider();
}
void Obstacle::RecalcSelfCollider()
{
if (is_door_) {
//门
if (!self_collider2_) {
self_collider2_ = new AabbCollider();
self_collider2_->owner = this;
AddEntityCollider(self_collider2_);
a8::Vec2 old_pos = GetPos();
{
SetPos(a8::Vec2(building_->GetX() + door_state1_->x() - building_->meta->i->tilewidth() / 2.0,
building_->GetY() + door_state1_->y() - building_->meta->i->tileheight() / 2.0)
);
self_collider2_->_min = a8::Vec2(0.0f - door_state0_->width() / 2.0f,
0.0f - door_state0_->height() / 2.0f);
self_collider2_->_max = a8::Vec2(door_state0_->width() / 2.0f, door_state0_->height() / 2.0f);
permanent_map_service->AddCollider(self_collider2_);
}
{
SetPos(a8::Vec2(building_->GetX() + door_state0_->x() - building_->meta->i->tilewidth() / 2.0,
building_->GetY() + door_state0_->y() - building_->meta->i->tileheight() / 2.0)
);
self_collider2_->_min = a8::Vec2(0.0f - door_state1_->width() / 2.0f,
0.0f - door_state1_->height() / 2.0f);
self_collider2_->_max = a8::Vec2(door_state1_->width() / 2.0f, door_state1_->height() / 2.0f);
permanent_map_service->AddCollider(self_collider2_);
}
SetPos(old_pos);
}
} else if (!Throughable()){
switch (meta->i->type()) {
case 1:
{
if (!self_collider_) {
self_collider_ = new CircleCollider();
self_collider_->owner = this;
AddEntityCollider(self_collider_);
}
self_collider_->pos = a8::Vec2();
self_collider_->rad = meta->i->height() / 2.0;
permanent_map_service->AddCollider(self_collider_);
}
break;
case 2:
{
if (!self_collider2_) {
self_collider2_ = new AabbCollider();
self_collider2_->owner = this;
AddEntityCollider(self_collider2_);
}
self_collider2_->_min = a8::Vec2(meta->i->width() / -2.0f, meta->i->height() / -2.0f);
self_collider2_->_max = a8::Vec2(meta->i->width() / 2.0f, meta->i->height() / 2.0f);
permanent_map_service->AddCollider(self_collider2_);
}
break;
default:
{
abort();
}
break;
}
}
if (!self_collider_) {
self_collider_ = new CircleCollider();
self_collider_->owner = this;
AddEntityCollider(self_collider_);
self_collider_->pos = a8::Vec2();
self_collider_->rad = 0.0f;
}
for (auto collider : colliders_) {
collider->tag = collider_tag;
collider->param1 = collider_param1;
collider->param2 = collider_param2;
if (meta->i->attack_type() == 3) {
a8::SetBitFlag(collider->tag, kHalfWallTag);
}
}
}
void Obstacle::FillMFObjectPart(Room* room, Human* hum, cs::MFObjectPart* part_data)
{
part_data->set_object_type(ET_Obstacle);
cs::MFObstaclePart* p = part_data->mutable_union_obj_2();
p->set_obj_uniid(GetUniId());
TypeConvert::ToPb(GetPos(), p->mutable_pos());
p->set_scale(1.0f);
}
void Obstacle::FillMFObjectFull(Room* room, Human* hum, cs::MFObjectFull* full_data)
{
full_data->set_object_type(ET_Obstacle);
if (IsClientCached(hum)) {
int object_flags = 0;
a8::SetBitFlag(object_flags, kOfReadCache);
full_data->set_obj_uniid(GetUniId());
full_data->set_object_flags(object_flags);
return;
}
cs::MFObstacleFull* p = full_data->mutable_union_obj_2();
if (CanClientCache(hum)) {
int object_flags = 0;
a8::SetBitFlag(object_flags, kOfWriteCache);
#if 0
full_data->set_obj_uniid(GetUniId());
#endif
full_data->set_object_flags(object_flags);
AddClientCache(hum);
}
p->set_obj_uniid(GetUniId());
TypeConvert::ToPb(GetPos(), p->mutable_pos());
p->set_scale(1.0f);
p->set_obstacle_id(meta->i->thing_id());
p->set_health(GetHealth(room));
p->set_dead(IsDead(room));
p->set_dead_at_thisframe(IsDead(room) ? GetDeadFrameNo(room) <= room->GetFrameNo() : false);
p->set_is_door(is_door_);
if (is_door_ && IsPermanent()) {
ObstacleData* data = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(data->flags, kHealth)) {
data->health = health_;
a8::SetBitFlag(data->flags, kHealth);
}
p->set_door_id(door_id_);
p->set_door_open_times(data->door_open_times);
p->set_door_old_state((int)data->door_state);
p->set_door_new_state((int)data->door_state);
p->set_door_house_uniid(door_house_uniid_);
p->set_door_house_id(building_->meta->i->mapid());
if (data->door_state == DoorStateClose) {
p->set_door_width(door_state0_->width());
p->set_door_height(door_state0_->height());
} else {
p->set_door_width(door_state1_->width());
p->set_door_height(door_state1_->height());
}
}
if (meta->i->thing_type() == kObstacleHideHouse) {
ObstacleData* data = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(data->flags, kHealth)) {
data->health = health_;
a8::SetBitFlag(data->flags, kHealth);
}
if (GetInteractionData(hum)) {
p->set_button_name("脱离");
} else {
p->set_button_name("躲藏");
}
}
}
void Obstacle::GetAabbBox(AabbCollider& aabb_box)
{
if (self_collider2_) {
aabb_box = *self_collider2_;
return;
}
if (self_collider_) {
aabb_box.active = true;
aabb_box.owner = this;
aabb_box.tag = collider_tag;
aabb_box._min.x = -self_collider_->rad;
aabb_box._min.y = -self_collider_->rad;
aabb_box._max.x = self_collider_->rad;
aabb_box._max.y = self_collider_->rad;
return;
}
aabb_box.tag = collider_tag;
aabb_box.active = true;
aabb_box.owner = this;
}
void Obstacle::GetCircleBox(CircleCollider& circle_box)
{
}
bool Obstacle::IsDead(Room* room)
{
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
return p->dead;
} else {
return dead_;
}
}
long long Obstacle::GetDeadFrameNo(Room* room)
{
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
return p->dead_frameno;
} else {
return dead_frameno_;
}
}
void Obstacle::OnPreCollision(Room* room)
{
if (!is_door_) {
return;
}
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
if (p->door_state == DoorStateClose) {
self_collider2_->_min = a8::Vec2(0.0f - door_state0_->width() / 2.0f,
0.0f - door_state0_->height() / 2.0f);
self_collider2_->_max = a8::Vec2(door_state0_->width() / 2.0f,
door_state0_->height() / 2.0f);
} else {
self_collider2_->_min = a8::Vec2(0.0f - door_state1_->width() / 2.0f,
0.0f - door_state1_->height() / 2.0f);
self_collider2_->_max = a8::Vec2(door_state1_->width() / 2.0f,
door_state1_->height() / 2.0f);
}
}
}
void Obstacle::Explosion(Bullet* bullet)
{
Room* room = bullet->room;
float old_rad = self_collider_->rad;
if (self_collider_) {
self_collider_->rad = meta->i->damage_dia();
}
if (meta->i->damage_dia() > 0.01f &&
meta->i->damage() > 0.01f) {
std::set<Entity*> objects;
std::set<GridCell*> tmp_grids;
room->grid_service->GetAllCellsByXy(room, GetX(), GetY(), tmp_grids);
room->grid_service->TraverseAllLayerHumanList
(
room->GetRoomIdx(),
tmp_grids,
[this, room, &objects] (Human* hum, bool& stop)
{
if (TestCollision(room, hum)) {
objects.insert(hum);
}
});
room->grid_service->TraverseAllLayerEntityList
(
room->GetRoomIdx(),
tmp_grids,
[this, room, &objects] (Entity* entity, bool& stop)
{
switch (entity->GetEntityType()) {
case ET_Obstacle:
case ET_Building:
{
if (entity != this && TestCollision(room, entity)) {
objects.insert(entity);
}
}
break;
default:
{
}
break;
}
});
a8::Vec2 bomb_pos = GetPos();
#if 1
room->frame_event.AddExplosionEx(bullet->sender,
meta->i->thing_id(),
bomb_pos,
meta->i->explosion_effect());
#else
room->frame_event.AddExplosion(bullet, meta->i->thing_id(), bomb_pos);
#endif
for (auto& target : objects) {
switch (target->GetEntityType()) {
case ET_Player:
{
Human* hum = (Human*)target;
if (!hum->dead && hum->team_id != team_id_) {
float dmg = meta->i->damage();
float def = hum->ability.def;
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
if (GetMasterId(bullet->room)) {
Human* master = bullet->room->GetHumanByUniId(GetMasterId(bullet->room));
if (master) {
hum->DecHP(finaly_dmg, master->GetUniId(), master->GetName(), VW_Mine);
} else {
hum->DecHP(finaly_dmg, VP_Mine, TEXT("battle_server_killer_mine", "地雷"), VW_Mine);
}
} else {
hum->DecHP(finaly_dmg, VP_Mine, TEXT("battle_server_killer_mine", "地雷"), VW_Mine);
}
}
}
break;
case ET_Obstacle:
{
Obstacle* obstacle = (Obstacle*)target;
if (!obstacle->IsDead(room) &&
obstacle->Attackable() &&
!obstacle->IsTerminatorAirDropBox(room)) {
float dmg = meta->i->damage();
float def = 0;
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
obstacle->SetHealth(room,
std::max(0.0f, obstacle->health_ - finaly_dmg));
if (obstacle->GetHealth(room) <= 0.01f) {
obstacle->Die(room);
}
if (obstacle->IsDead(room)) {
bullet->sender.Get()->DropItems(obstacle);
}
obstacle->BroadcastFullState(room);
}
}
break;
default:
break;
}
}
}
if (self_collider_) {
self_collider_->rad = old_rad;
}
}
void Obstacle::SetDoorInfo(Building* building, int door_id_x)
{
MetaData::Building::Door* door_meta = &building->meta->doors[door_id_x];
is_door_ = true;
door_id_ = door_meta->door_id;
building_ = building;
door_house_uniid_ = building_->GetUniId();
door_state0_ = door_meta->state0;
door_state1_ = door_meta->state1;
}
bool Obstacle::IsDoor()
{
return is_door_;
}
DoorState_e Obstacle::GetDoorState(Room *room)
{
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
return p->door_state;
}
void Obstacle::SetDoorState(Room* room, DoorState_e state)
{
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
p->door_state = state;
}
void Obstacle::SetBuilding(Building* building)
{
building_ = building;
}
Building* Obstacle::GetBuilding()
{
return building_;
}
const metatable::DoorObjJson* Obstacle::GetDoorState0()
{
return door_state0_;
}
const metatable::DoorObjJson* Obstacle::GetDoorState1()
{
return door_state1_;
}
void Obstacle::IncDoorOpenTimes(Room* room)
{
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
++(p->door_open_times);
}
float Obstacle::GetHealth(Room* room)
{
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
return p->health;
} else {
return health_;
}
}
void Obstacle::SetHealth(Room* room, float value)
{
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
p->health = value;
} else {
health_ = value;
}
}
void Obstacle::Die(Room* room)
{
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
p->health = 0;
p->dead = true;
p->dead_frameno = room->GetFrameNo();
} else {
health_ = 0;
dead_ = true;
dead_frameno_ = room->GetFrameNo();
}
}
bool Obstacle::IsPermanent()
{
if (is_permanent) {
if (GetUniId() >= FIXED_OBJECT_MAXID) {
abort();
}
} else {
if (GetUniId() < FIXED_OBJECT_MAXID) {
abort();
}
}
return is_permanent;
}
bool Obstacle::Attackable()
{
return meta->i->attack_type() == 1;
}
bool Obstacle::Throughable()
{
return meta->i->attack_type() == 2;
}
int Obstacle::GetTeamId(Room* room)
{
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
return p->team_id;
} else {
return team_id_;
}
}
void Obstacle::SetTeamId(Room* room, int team_id)
{
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
p->team_id = team_id;
} else {
team_id_ = team_id;
}
}
int Obstacle::GetMasterId(Room* room)
{
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
return p->master_id;
} else {
return master_id_;
}
}
void Obstacle::SetMasterId(Room* room, int master_id)
{
if (IsPermanent()) {
ObstacleData* p = room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
p->master_id = master_id;
} else {
master_id_ = master_id;
}
}
void Obstacle::OnBulletHit(Bullet* bullet)
{
if (!IsDead(bullet->room) &&
Attackable() &&
!IsTerminatorAirDropBox(bullet->room)) {
if (meta->receive_special_damage_type != 0 &&
((bullet->gun_meta->special_damage_type & meta->receive_special_damage_type) == 0)) {
return;
}
float dmg = bullet->GetAtk() * (1 + bullet->sender.Get()->GetAttrRate(kHAT_Atk)) +
bullet->sender.Get()->GetAttrAbs(kHAT_Atk);
float def = 0;
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
SetHealth(bullet->room, std::max(0.0f, GetHealth(bullet->room) - finaly_dmg));
if (GetHealth(bullet->room) <= 0.01f) {
Die(bullet->room);
}
if (IsDead(bullet->room)) {
if (meta->i->damage_dia() > 0.01f &&
meta->i->damage() > 0.01f) {
Explosion(bullet);
}
bullet->sender.Get()->DropItems(this);
}
BroadcastFullState(bullet->room);
#ifdef DEBUG
bullet->sender.Get()->SendDebugMsg
(
a8::Format("bullet weapon_id:%d atk:%f",
{
bullet->gun_meta->i->id(),
bullet->GetAtk()
})
);
#endif
}
}
bool Obstacle::CanThroughable(Creature* c)
{
return false;
}
bool Obstacle::CanThroughable(Bullet* bullet)
{
return false;
}
bool Obstacle::DoInteraction(Human* sender)
{
if (IsDoor()) {
if (GetDoorState(sender->room) == DoorStateClose) {
SetDoorState(sender->room, DoorStateOpen);
float x = GetBuilding()->GetX() + GetDoorState1()->x() - GetBuilding()->meta->i->tilewidth() / 2.0;
float y = GetBuilding()->GetY() + GetDoorState1()->y() - GetBuilding()->meta->i->tileheight() / 2.0;
SetPos(a8::Vec2(x, y));
} else {
SetDoorState(sender->room, DoorStateClose);
float x = GetBuilding()->GetX() + GetDoorState0()->x() - GetBuilding()->meta->i->tilewidth() / 2.0;
float y = GetBuilding()->GetY() + GetDoorState0()->y() - GetBuilding()->meta->i->tileheight() / 2.0;
SetPos(a8::Vec2(x, y));
}
IncDoorOpenTimes(sender->room);
RecalcSelfCollider();
sender->room->TraverseHumanList
(a8::XParams(),
[this] (Human* hum, a8::XParams& param) -> bool
{
hum->AddToNewObjects(this);
if (TestCollision(hum->room, hum)) {
hum->SetLastCollisionDoor(this);
} else if (hum->GetLastCollisionDoor() == this) {
hum->SetLastCollisionDoor(nullptr);
}
return true;
});
return true;
}
switch (meta->i->thing_type()) {
case kObstacleHideHouse:
{
DoHideHouseInteraction(sender);
}
break;
default:
{
}
break;
}
return false;
}
void Obstacle::OnCollisionTrigger(Creature* c, OptResult& opt_result)
{
opt_result = kOptPass;
switch (meta->i->thing_type()) {
case kObstacleSpring:
{
AddObstacleBuff(c);
a8::SetBitFlag(c->status, CS_Collisioning);
opt_result = kOptBreak;
}
break;
case kObstacleMine:
case kObstacleTrap:
{
if (c->team_id != GetTeamId(c->room)) {
AddObstacleBuff(c);
Explosion();
Die(c->room);
BroadcastFullState(c->room);
}
}
break;
default:
{
}
break;
}
}
void Obstacle::DoHideHouseInteraction(Human* sender)
{
ObstacleData* data = sender->room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(data->flags, kHealth)) {
data->health = health_;
a8::SetBitFlag(data->flags, kHealth);
}
auto p = GetInteractionData(sender);
if (p) {
ClearObstacleBuff(sender);
sender->SetPos(std::get<1>(*p));
data->interaction_humans->erase(sender->GetUniId());
} else {
a8::Vec2 old_pos = sender->GetPos();
sender->SetPos(GetPos());
AddObstacleBuff(sender);
if (!data->interaction_humans) {
data->interaction_humans = new std::map<int, std::tuple<long long, a8::Vec2>>();
}
(*data->interaction_humans)[sender->GetUniId()] = std::make_tuple
(sender->room->GetFrameNo(), old_pos);
}
sender->AddToNewObjects(this);
}
std::tuple<long long, a8::Vec2>* Obstacle::GetInteractionData(Human* sender)
{
if (IsPermanent()) {
ObstacleData* p = sender->room->GetPermanentObstacleData(GetUniId());
if (!a8::HasBitFlag(p->flags, kHealth)) {
p->health = health_;
a8::SetBitFlag(p->flags, kHealth);
}
if (!p->interaction_humans) {
return nullptr;
}
auto itr = p->interaction_humans->find(sender->GetUniId());
return itr != p->interaction_humans->end() ? &itr->second : nullptr;
} else {
return nullptr;
}
}
void Obstacle::AddObstacleBuff(Creature* c)
{
for (int buff_id : meta->buff_list) {
c->TryAddBuff(c, buff_id);
}
}
void Obstacle::ClearObstacleBuff(Creature* c)
{
for (int buff_id : meta->buff_list) {
c->RemoveBuffById(buff_id);
}
}