game2006/server/gameserver/roomobstacle.cc
aozhiwei 0f60fa5631 1
2021-06-15 22:47:56 +08:00

569 lines
17 KiB
C++

#include "precompile.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 "mapservice.h"
#include "roomobstacle.h"
RoomObstacle::RoomObstacle():Obstacle()
{
}
RoomObstacle::~RoomObstacle()
{
for (auto& itr : colliders_) {
ColliderComponent* collider = itr;
#ifdef DEBUG
#if 0
a8::UdpLog::Instance()->Debug("OnRemoveCollider %d %d %d",
{
room->GetRoomIdx(),
GetUniId(),
(long long)collider
});
#endif
#endif
room->map_service->RemoveCollider(collider);
}
if (!detached_) {
if (master.Get()) {
master.Get()->SlaveOnRemove(this);
}
detached_ = true;
}
if (!grid_list_) {
A8_SAFE_DELETE(grid_list_);
}
}
void RoomObstacle::Initialize()
{
Obstacle::Initialize();
xtimer_attacher.xtimer = &room->xtimer;
}
void RoomObstacle::RecalcSelfCollider()
{
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;
room->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);
room->map_service->AddCollider(self_collider2_);
}
break;
}
}
if (!self_collider_) {
#ifdef DEBUG
#if 0
a8::UdpLog::Instance()->Debug("OnAddCollider %d %d %d",
{
room->GetRoomIdx(),
GetUniId(),
(long long)self_collider_
});
#endif
#endif
}
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 RoomObstacle::ActiveTimerFunc()
{
if (!master.Get()) {
room->xtimer.DeleteTimer(room->xtimer.GetRunningTimer());
return;
}
if (!grid_list_) {
temp_through_ = true;
grid_list_ = new std::set<GridCell*>();
room->grid_service->GetAllCellsByXy(room, GetPos().x, GetPos().y, *grid_list_);
}
bool has_hum = false;
room->grid_service->TraverseAllLayerHumanList
(room->GetRoomIdx(),
*grid_list_,
[this, &has_hum] (Human* hum, bool& stop)
{
bool old_temp_through = temp_through_;
temp_through_ = false;
if (master.Get()->team_id == hum->team_id) {
if (TestCollision(room, hum)) {
has_hum = true;
stop = true;
}
}
temp_through_ = old_temp_through;
}
);
if (!has_hum) {
temp_through_ = false;
room->xtimer.DeleteTimer(room->xtimer.GetRunningTimer());
}
}
void RoomObstacle::UpdateTimerFunc()
{
if (!grid_list_) {
grid_list_ = new std::set<GridCell*>();
room->grid_service->GetAllCellsByXy(room, GetPos().x, GetPos().y, *grid_list_);
}
if (grid_list_ && master.Get() && !IsDead(room)) {
std::set<Human*> human_list;
room->grid_service->TraverseAllLayerHumanList
(room->GetRoomIdx(),
*grid_list_,
[this, &human_list] (Human* hum, bool& stop)
{
if (master.Get()->team_id != hum->team_id && TestCollision(room, hum)) {
human_list.insert(hum);
}
}
);
if (!human_list.empty()) {
for (Human* hum : human_list) {
for (int buff_id : meta->buff_list) {
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id);
if (buff_meta) {
hum->AddBuff(master.Get(),
buff_meta,
1);
}
}
}
Explosion();
Die(room);
BroadcastFullState(room);
room->xtimer.DeleteTimer(room->xtimer.GetRunningTimer());
}
} else {
room->xtimer.DeleteTimer(room->xtimer.GetRunningTimer());
}
}
void RoomObstacle::Explosion()
{
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;
room->grid_service->TraverseAllLayerHumanList
(
room->GetRoomIdx(),
*grid_list_,
[this, &objects] (Human* hum, bool& stop)
{
if (master.Get()->team_id != hum->team_id && TestCollision(room, hum)) {
objects.insert(hum);
}
});
room->grid_service->TraverseAllLayerEntityList
(
room->GetRoomIdx(),
*grid_list_,
[this, &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();
room->frame_event.AddExplosionEx(master,
meta->i->thing_id(),
bomb_pos,
meta->i->explosion_effect());
for (auto& target : objects) {
switch (target->GetEntityType()) {
case ET_Player:
{
Human* hum = (Human*)target;
if (!hum->dead) {
float dmg = meta->i->damage();
float def = hum->ability.def;
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
if (master.Get()) {
hum->DecHP(finaly_dmg, master.Get()->GetUniId(), master.Get()->GetName(), 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->GetHealth(room) - finaly_dmg));
if (obstacle->GetHealth(room) <= 0.01f) {
obstacle->Die(room);
}
if (obstacle->IsDead(room)) {
#if 0
bullet->player->DropItems(obstacle);
#endif
}
obstacle->BroadcastFullState(room);
}
}
break;
default:
break;
}
}
}
if (self_collider_) {
self_collider_->rad = old_rad;
}
}
void RoomObstacle::SpecExplosion()
{
++explosion_times_;
if (meta->i->damage_dia() > 0.01f &&
meta->i->damage() > 0.01f) {
if (!grid_list_) {
grid_list_ = new std::set<GridCell*>();
room->grid_service->GetAllCellsByXy(room, GetPos().x, GetPos().y, *grid_list_);
}
a8::Vec2 bomb_born_offset = a8::Vec2::UP;
bomb_born_offset.Rotate(a8::RandAngle());
bomb_born_offset = bomb_born_offset * a8::RandEx(1, std::max(2, meta->i->explosion_float()));
a8::Vec2 bomb_pos = GetPos() + bomb_born_offset;
std::set<Entity*> objects;
room->grid_service->TraverseAllLayerHumanList
(
room->GetRoomIdx(),
*grid_list_,
[this, &objects, &bomb_pos] (Human* hum, bool& stop)
{
float distance = (hum->GetPos() - bomb_pos).Norm();
if (master.Get()->team_id != hum->team_id && distance < meta->i->damage_dia()) {
objects.insert(hum);
}
});
room->frame_event.AddExplosionEx(master,
meta->i->thing_id(),
bomb_pos,
meta->i->explosion_effect());
for (auto& target : objects) {
switch (target->GetEntityType()) {
case ET_Player:
{
Human* hum = (Human*)target;
if (!hum->dead) {
float dmg = meta->i->damage();
float def = hum->ability.def;
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
hum->DecHP(finaly_dmg, VP_Mine, TEXT("battle_server_killer_mine", "地雷"), VW_Mine);
for (int buff_id : meta->buff_list) {
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id);
if (buff_meta) {
hum->AddBuff(master.Get(),
buff_meta,
1);
}
}
}
}
break;
default:
{
}
break;
}
}
}
if (explosion_times_ >= meta->i->explosion_times()) {
Die(room);
BroadcastFullState(room);
room->xtimer.DeleteTimer(room->xtimer.GetRunningTimer());
}
}
void RoomObstacle::Active()
{
switch (meta->i->thing_type()) {
case kObstacleSelfExplosion:
{
ActiveSelfExplosion();
}
break;
case kObstacleMine:
{
ActiveMine();
}
break;
case kObstacleTrap:
{
ActiveTrap();
}
break;
case kObstaclePosionGas:
{
ActivePosionGas();
}
break;
case kObstacleSpring:
{
ActiveSpring();
}
break;
case kObstacleHideHouse:
{
ActiveHideHouse();
}
break;
case kObstacleGully:
{
ActiveGully();
}
break;
case kObstacleAirDropBox:
{
ActiveAirDrop();
}
break;
default:
{
}
break;
}
}
void RoomObstacle::ActiveSelfExplosion()
{
room->xtimer.AddDeadLineTimerAndAttach
(
meta->i->time() / FRAME_RATE_MS,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
RoomObstacle* obstacle = (RoomObstacle*)param.sender.GetUserData();
obstacle->room->xtimer.AddRepeatTimerAndAttach
(
obstacle->meta->i->explosion_interval() / FRAME_RATE_MS,
a8::XParams()
.SetSender(obstacle),
[] (const a8::XParams& param)
{
RoomObstacle* obstacle = (RoomObstacle*)param.sender.GetUserData();
obstacle->SpecExplosion();
},
&obstacle->xtimer_attacher.timer_list_
);
},
&xtimer_attacher.timer_list_
);
}
void RoomObstacle::ActiveMine()
{
room->xtimer.AddRepeatTimerAndAttach
(
1,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
RoomObstacle* obstacle = (RoomObstacle*)param.sender.GetUserData();
obstacle->UpdateTimerFunc();
},
&xtimer_attacher.timer_list_
);
}
void RoomObstacle::ActiveTrap()
{
room->xtimer.AddRepeatTimerAndAttach
(
SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
RoomObstacle* obstacle = (RoomObstacle*)param.sender.GetUserData();
obstacle->UpdateTimerFunc();
},
&xtimer_attacher.timer_list_
);
}
void RoomObstacle::ActivePosionGas()
{
room->xtimer.AddDeadLineTimerAndAttach
(
meta->i->time() / FRAME_RATE_MS,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
RoomObstacle* obstacle = (RoomObstacle*)param.sender.GetUserData();
obstacle->room->xtimer.AddRepeatTimerAndAttach
(
obstacle->meta->i->explosion_interval() / FRAME_RATE_MS,
a8::XParams()
.SetSender(obstacle),
[] (const a8::XParams& param)
{
RoomObstacle* obstacle = (RoomObstacle*)param.sender.GetUserData();
obstacle->SpecExplosion();
},
&obstacle->xtimer_attacher.timer_list_
);
},
&xtimer_attacher.timer_list_
);
}
void RoomObstacle::DetachFromMaster()
{
if (!detached_) {
detached_ = true;
if (master.Get()) {
xtimer_attacher.ClearTimerList();
master.Get()->SlaveOnRemove(this);
room->grid_service->DelRoomEntity(room, this);
if (!IsDead(room)) {
Die(room);
BroadcastFullState(room);
}
room->RemoveObjectLater(this);
}
}
}
void RoomObstacle::Die(Room* room)
{
if (!IsDead(room)) {
Obstacle::Die(room);
DetachFromMaster();
}
}
bool RoomObstacle::CanThroughable(Creature* c)
{
if (master.Get()) {
return master.Get()->team_id == c->team_id && temp_through_;
} else {
return temp_through_;
}
}
void RoomObstacle::ActiveSpring()
{
}
void RoomObstacle::ActiveHideHouse()
{
}
void RoomObstacle::ActiveGully()
{
}
void RoomObstacle::ActiveAirDrop()
{
room->xtimer.AddDeadLineTimerAndAttach
(
meta->int_param2 / FRAME_RATE_MS,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
RoomObstacle* obstacle = (RoomObstacle*)param.sender.GetUserData();
obstacle->SummonAirDropBox(obstacle->meta->int_param1);
},
&xtimer_attacher.timer_list_
);
room->xtimer.AddDeadLineTimerAndAttach
(
meta->int_param2 / FRAME_RATE_MS * 1,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
RoomObstacle* obstacle = (RoomObstacle*)param.sender.GetUserData();
obstacle->room->frame_event.AddAirDrop(obstacle->meta->int_param2 / 1000,
obstacle->meta->int_param1,
obstacle->GetPos());
},
&xtimer_attacher.timer_list_
);
}
bool RoomObstacle::DoInteraction(Human* sender)
{
if (Obstacle::DoInteraction(sender)) {
return true;
}
return true;
}
void RoomObstacle::SummonAirDropBox(int box_id)
{
RoomObstacle* obstacle = room->CreateObstacle
(
box_id,
GetPos().x,
GetPos().y
);
}