game2006/server/gameserver/roomobstacle.cc
aozhiwei 2594ab3610 1
2023-04-11 15:56:37 +08:00

904 lines
27 KiB
C++

#include "precompile.h"
#include <a8/collision.h>
#include "room.h"
#include "human.h"
#include "app.h"
#include "typeconvert.h"
#include "bullet.h"
#include "mapservice.h"
#include "roomobstacle.h"
#include "explosion.h"
#include "entityfactory.h"
#include "player.h"
#include "incubator.h"
#include "skillhelper.h"
#include "ability.h"
#include "netdata.h"
#include "collision.h"
#include "mt/MapThing.h"
#include "mt/Skill.h"
#include "mt/SkillNumber.h"
#include "mt/Buff.h"
RoomObstacle::RoomObstacle():Obstacle()
{
weak_ptr_chunk_.Set(this);
INIT_LIST_HEAD(&entry);
}
RoomObstacle::~RoomObstacle()
{
if (!detached_) {
if (master.Get()) {
master.Get()->SlaveOnRemove(this);
}
detached_ = true;
}
if (!grid_list_) {
A8_SAFE_DELETE(grid_list_);
}
if (!hit_objects_) {
A8_SAFE_DELETE(hit_objects_);
}
if (!temp_pass_objects_) {
A8_SAFE_DELETE(temp_pass_objects_);
}
if (meta->_sweep_tags != 0) {
room->mine_objects.erase(GetUniId());
}
}
void RoomObstacle::Initialize()
{
Obstacle::Initialize();
xtimer_attacher.SetOwner(&room->xtimer);
if (meta->_sweep_tags != 0) {
room->mine_objects[GetUniId()] = GetWeakPtrRef();
}
born_frameno = room->GetFrameNo();
#ifdef DEBUG
{
if (App::Instance()->debug_params.find(118) != App::Instance()->debug_params.end()) {
born_frameno += App::Instance()->debug_params[118] * 2;
}
}
#endif
}
void RoomObstacle::ActiveTimerFunc()
{
if (!master.Get()) {
room->xtimer.DeleteCurrentTimer();
return;
}
if (!grid_list_) {
grid_list_ = new std::set<GridCell*>();
room->grid_service->GetAllCellsByXy(room, GetPos().GetX(), GetPos().GetY(), *grid_list_);
}
bool has_hum = false;
room->grid_service->TraverseAllLayerHumanList
(room->GetRoomIdx(),
*grid_list_,
[this, &has_hum] (Human* hum, bool& stop)
{
if (master.Get()->team_id == hum->team_id) {
if (Collision::CheckCB(hum, this)) {
has_hum = true;
stop = true;
}
}
}
);
if (!has_hum) {
room->xtimer.DeleteCurrentTimer();
}
}
void RoomObstacle::UpdateTimerFunc()
{
if (!grid_list_) {
grid_list_ = new std::set<GridCell*>();
room->grid_service->GetAllCellsByXy(room, GetPos().GetX(), GetPos().GetY(), *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 &&
Collision::CheckCB(hum, this)) {
human_list.insert(hum);
}
}
);
if (!human_list.empty()) {
for (Human* hum : human_list) {
for (int buff_id : meta->_buff_list) {
const mt::Buff* buff_meta = mt::Buff::GetById(buff_id);
if (buff_meta) {
hum->AddBuff(master.Get(),
buff_meta
);
}
}
}
Die(room);
ProcDieExplosion(room);
BroadcastFullState(room);
room->xtimer.DeleteCurrentTimer();
}
} else {
room->xtimer.DeleteCurrentTimer();
}
}
void RoomObstacle::SpecExplosion(int delay_time)
{
++explosion_times_;
#if 1
{
#else
if (meta->damage_dia() > 0.01f) {
#endif
if (!grid_list_) {
grid_list_ = new std::set<GridCell*>();
room->grid_service->GetAllCellsByXy(room, GetPos().GetX(), GetPos().GetY(), *grid_list_);
}
Position bomb_born_offset;
if (pos_list && explosion_times_ <= pos_list->size()) {
bomb_born_offset = pos_list->at(explosion_times_ - 1);
} else {
// 999
#if 1
#else
bomb_born_offset.Rotate(a8::RandAngle());
bomb_born_offset = bomb_born_offset * a8::RandEx(1, std::max(2, meta->explosion_float()));
#endif
}
Position bomb_pos = GetPos();
bomb_pos.AddGlmVec3(bomb_born_offset.ToGlmVec3());
if (room->grid_service->CanAdd(bomb_pos.GetX(), bomb_pos.GetY())) {
std::shared_ptr<Explosion> explosion = EntityFactory::Instance()->MakeExplosion();
explosion->exclude_uniid = GetUniId();
if (delay_time > 0) {
explosion->SetDamageDelay(delay_time);
}
explosion->IndifferenceAttack(
room,
bomb_pos,
meta->damage_dia(),
meta->explosion_effect(),
meta->damage()
);
if (master.Get()) {
std::set<Creature*> target_list;
room->grid_service->TraverseCreatures
(room->GetRoomIdx(),
*grid_list_,
[this, &target_list, &bomb_pos] (Creature* c, bool& stop)
{
if (master.Get()->team_id != c->team_id &&
!c->dead &&
bomb_pos.Distance2D2(c->GetPos()) < meta->damage_dia()) {
target_list.insert(c);
}
}
);
if (!target_list.empty()) {
for (Creature* c : target_list) {
for (int buff_id : meta->_buff_list) {
#if DEBUG
a8::XPrintf("add thing buff:%d\n", {buff_id});
#endif
c->TryAddBuff(master.Get(),
buff_id
);
}
}
}
}
}
}
if (explosion_times_ >= total_explosion_times_) {
room->xtimer.DeleteCurrentTimer();
Die(room);
BroadcastFullState(room);
}
}
void RoomObstacle::Active()
{
switch (meta->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;
case kObstacleKeepRangeBuff:
{
ActiveKeepRangeBuff();
}
break;
case kObstacleShield:
{
#if 0
if (master.Get() && !GlmHelper::IsZero(master.Get()->GetMoveDir())) {
SetRotate(master.Get()->GetMoveDir().CalcAngleEx(GlmHelper::UP));
}
CalcTempPassObjects();
#endif
}
break;
case kObstacleStrengthenWall:
{
#if 0
if (master.Get() && !GlmHelper::IsZero(master.Get()->GetMoveDir())) {
SetRotate(master.Get()->GetMoveDir().CalcAngleEx(GlmHelper::UP));
}
ActiveStrengthenWall();
#endif
}
break;
case kObstacleMedicalStation:
{
ActiveMedicalStation();
}
break;
default:
{
}
break;
}
if (meta->life_time() > 0) {
life_time_timer = room->xtimer.SetTimeoutWpEx
(
meta->life_time() / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
DetachFromMaster();
}
},
&xtimer_attacher);
}
}
void RoomObstacle::ActiveSelfExplosion()
{
total_explosion_times_ = meta->explosion_times();
if (context_ability && context_ability->GetAttrAbs(kHAT_WeaponExplosionContinueTime) > 0.001f) {
total_explosion_times_ += context_ability->GetAttrAbs(kHAT_WeaponExplosionContinueTime) * 1000 /
meta->explosion_interval();
}
room->xtimer.SetTimeoutEx
(
meta->time() / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
InstallPreExplostionSummonTimer();
room->xtimer.SetIntervalEx
(
meta->explosion_interval() / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
SpecExplosion(meta->explosion_dmg_delay());
}
},
&xtimer_attacher);
}
},
&xtimer_attacher);
}
void RoomObstacle::ActiveMine()
{
room->xtimer.SetIntervalEx
(
1,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
UpdateTimerFunc();
}
},
&xtimer_attacher);
}
void RoomObstacle::ActiveTrap()
{
room->xtimer.SetIntervalEx
(
SERVER_FRAME_RATE,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
UpdateTimerFunc();
}
},
&xtimer_attacher);
}
void RoomObstacle::ActivePosionGas()
{
total_explosion_times_ = meta->explosion_times();
if (context_ability && context_ability->GetAttrAbs(kHAT_WeaponExplosionContinueTime) > 0.001f) {
total_explosion_times_ += context_ability->GetAttrAbs(kHAT_WeaponExplosionContinueTime) * 1000 /
meta->explosion_interval();
}
room->xtimer.SetTimeoutEx
(
meta->time() / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
room->xtimer.SetIntervalEx
(
meta->explosion_interval() / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
SpecExplosion();
}
},
&xtimer_attacher);
}
},
&xtimer_attacher);
}
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);
}
}
if (!list_empty(&entry)) {
list_del_init(&entry);
}
}
void RoomObstacle::Die(Room* room)
{
if (!IsDead(room)) {
Obstacle::Die(room);
DetachFromMaster();
if (meta->_sweep_tags != 0) {
room->mine_objects.erase(GetUniId());
}
}
}
void RoomObstacle::ActiveSpring()
{
}
void RoomObstacle::ActiveHideHouse()
{
}
void RoomObstacle::ActiveGully()
{
}
void RoomObstacle::ActiveAirDrop()
{
int delay_time = 0;
int appear_time = 0;
if (meta->_param2_list.size() >= 2) {
delay_time = meta->_param2_list[0];
appear_time = meta->_param2_list[1];
}
room->xtimer.SetTimeoutEx
(
(delay_time + appear_time) / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
SummonAirDropBox(meta->_int_param1);
Die(room);
BroadcastFullState(room);
}
},
&xtimer_attacher);
room->xtimer.SetTimeoutEx
(
delay_time / FRAME_RATE_MS,
[this, delay_time, appear_time]
(int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
Position born_pos = GetPos();
{
const mt::MapThing* thing_meta = meta;
if (thing_meta && thing_meta->summon_born_rad() > 0) {
glm::vec3 born_dir = GlmHelper::UP;
GlmHelper::RotateY(born_dir, GetUniId());
born_pos.AddGlmVec3(born_dir * (float)(thing_meta->summon_born_rad()));
}
}
room->frame_event.AddAirDrop(delay_time,
appear_time,
born_pos.ToGlmVec3());
}
},
&xtimer_attacher);
}
bool RoomObstacle::DoInteraction(Human* sender)
{
if (Obstacle::DoInteraction(sender)) {
return true;
}
return true;
}
void RoomObstacle::SummonAirDropBox(int box_id)
{
Position born_pos = GetPos();
const mt::MapThing* thing_meta = mt::MapThing::GetById(box_id);
if (thing_meta && thing_meta->summon_born_rad() > 0) {
glm::vec3 born_dir = GlmHelper::UP;
GlmHelper::RotateY(born_dir, GetUniId());
// 999
#if 1
#else
born_pos = born_pos + born_dir * thing_meta->summon_born_rad();
#endif
}
RoomObstacle* obstacle = room->CreateObstacle
(
box_id,
born_pos.GetX(),
born_pos.GetY(),
born_pos.GetZ()
);
if (obstacle) {
obstacle->PushCollisionObjects();
}
}
Entity* RoomObstacle::GetRealObject(Room* room)
{
return room->GetEntityByUniId(real_object_uniid);
}
void RoomObstacle::ActiveKeepRangeBuff()
{
auto check_cb =
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
ProcKeepRangeBuff();
}
};
room->xtimer.SetIntervalEx
(
1,
check_cb,
&xtimer_attacher);
room->xtimer.SetTimeoutEx
(
meta->time() / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
Destory();
}
},
&xtimer_attacher);
}
void RoomObstacle::ProcKeepRangeBuff()
{
if (!grid_list_) {
grid_list_ = new std::set<GridCell*>();
room->grid_service->GetAllCellsByXy(room, GetPos().GetX(), GetPos().GetY(), *grid_list_);
}
if (!hit_objects_) {
hit_objects_ = new std::map<int, CreatureWeakPtr>();
}
if (grid_list_ && master.Get() && !IsDead(room)) {
std::set<Creature*> target_list;
room->grid_service->TraverseCreatures
(room->GetRoomIdx(),
*grid_list_,
[this, &target_list] (Creature* hum, bool& stop)
{
if (master.Get()->team_id != hum->team_id &&
Collision::CheckCB(hum, this)) {
target_list.insert(hum);
}
}
);
for (Creature* hum : target_list) {
for (int buff_id : meta->_buff_list) {
const mt::Buff* buff_meta = mt::Buff::GetById(buff_id);
if (buff_meta && hum->GetBuffById(buff_id)) {
hum->AddBuff(master.Get(),
buff_meta
);
}
}
if (hit_objects_->find(hum->GetUniId()) == hit_objects_->end()) {
(*hit_objects_)[hum->GetUniId()] = hum->GetWeakPtrRef();
}
}
for (auto& pair : *hit_objects_) {
for (int buff_id : meta->_buff_list) {
if (pair.second.Get() && target_list.find(pair.second.Get()) == target_list.end()) {
pair.second.Get()->RemoveBuffById(buff_id);
}
}
}
} else {
room->xtimer.DeleteCurrentTimer();
}
}
RoomObstacleWeakPtr RoomObstacle::AllocWeakPtr()
{
RoomObstacleWeakPtr ptr;
ptr.Attach(this);
return ptr;
}
RoomObstacleWeakPtr& RoomObstacle::GetWeakPtrRef()
{
if (!weak_ptr_.Get()) {
weak_ptr_.Attach(this);
}
return weak_ptr_;
}
void RoomObstacle::OnBattleStart(Room* room)
{
if (master.Get()) {
Destory();
}
}
void RoomObstacle::Destory()
{
#if DEBUG
room->BroadcastDebugMsg(a8::Format("obstacle destory uniid:%d pos:%d,%d",
{
GetUniId(),
GetPos().GetX(),
GetPos().GetY()
}));
#endif
DetachFromMaster();
}
void RoomObstacle::PushCollisionObjects()
{
std::set<GridCell*> grid_list;
room->grid_service->GetAllCellsByXy(room, GetPos().GetX(), GetPos().GetY(), grid_list);
room->grid_service->TraverseCreatures
(
room->GetRoomIdx(),
grid_list,
[this] (Creature* c, bool& stop)
{
if (Collision::CheckCB(c, this)) {
c->room->xtimer.SetTimeoutEx
(1,
[c] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
c->MustBeAddBuff(c, kPullToWalkableBuffId);
}
},
&c->xtimer_attacher);
}
});
}
void RoomObstacle::InstallPreExplostionSummonTimer()
{
if (!meta->_preexplosion_summon.empty()) {
int base_time = 0;
pos_list = std::make_shared<std::vector<Position>>();
for (int i = 0; i < total_explosion_times_; ++i) {
Position bomb_born_offset;
{
// 999
#if 1
Position bomb_pos = GetPos();
#else
bomb_born_offset.Rotate(a8::RandAngle());
bomb_born_offset = bomb_born_offset * a8::RandEx(1, std::max(2, meta->explosion_float()));
Position bomb_pos = GetPos() + bomb_born_offset;
#endif
if (room->grid_service->CanAdd(bomb_pos.GetX(), bomb_pos.GetY())) {
pos_list->push_back(bomb_born_offset);
} else {
pos_list->push_back(Position());
}
}
for (auto& tuple : meta->_preexplosion_summon) {
int time = std::get<0>(tuple);
int obstacle_id = std::get<1>(tuple);
room->xtimer.SetTimeoutEx
(
(base_time + time) / FRAME_RATE_MS,
[this, obstacle_id, bomb_born_offset]
(int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
glm::vec3 pos = glm::vec3(
GetPos().GetX() + bomb_born_offset.GetX(),
GetPos().GetY(),
GetPos().GetZ() + bomb_born_offset.GetZ()
);
if (room->CanAddObstacle(pos, obstacle_id)) {
RoomObstacle* p = room->CreateObstacle
(
obstacle_id,
pos.x,
pos.y,
pos.z
);
if (p) {
p->Active();
}
}
}
},
&xtimer_attacher);
}//end for tuple
base_time += meta->explosion_interval();
}
}
}
void RoomObstacle::DestoryAt(int time)
{
room->xtimer.SetTimeoutEx
(
time / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
DetachFromMaster();
#ifdef DEBUG
room->BroadcastDebugMsg
(a8::Format("obstacle destory %d", {meta->thing_id()}));
#endif
}
},
&xtimer_attacher);
}
void RoomObstacle::CalcTempPassObjects()
{
if (!grid_list_) {
grid_list_ = new std::set<GridCell*>();
room->grid_service->GetAllCellsByXy(room, GetPos().GetX(), GetPos().GetY(), *grid_list_);
}
if (!temp_pass_objects_) {
temp_pass_objects_ = new std::map<int, CreatureWeakPtr>();
}
room->grid_service->TraverseCreatures
(room->GetRoomIdx(),
*grid_list_,
[this] (Creature* hum, bool& stop)
{
if (Collision::CheckCB(hum, this)) {
(*temp_pass_objects_)[hum->GetUniId()] = hum->GetWeakPtrRef();
}
}
);
if (!temp_pass_objects_->empty()) {
room->xtimer.SetTimeoutEx
(
200 / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
std::list<int> del_objs;
for (auto& pair : *temp_pass_objects_) {
if (!pair.second.Get() ||
!Collision::CheckCB(pair.second.Get(), this)) {
del_objs.push_back(pair.first);
}
}
for (int obj_uniid : del_objs) {
temp_pass_objects_->erase(obj_uniid);
}
if (temp_pass_objects_->empty()) {
room->xtimer.DeleteCurrentTimer();
}
}
},
&xtimer_attacher);
}
}
void RoomObstacle::ActiveStrengthenWall()
{
}
void RoomObstacle::ActiveMedicalStation()
{
ForceGridList();
if (!skill_meta || !skill_meta->_number_meta) {
return;
}
if (skill_meta->GetMagicId() != MAGIC_20301_XL) {
return;
}
scale = 1.0f;
if (skill_meta->_base_skill_meta) {
float base_range = SkillHelper::GetYlzRange(skill_meta->_base_skill_meta);
if (std::abs(base_range) > 0.0001f) {
scale = SkillHelper::GetYlzRange(skill_meta) / base_range;
}
}
room->xtimer.SetIntervalEx
(
SERVER_FRAME_RATE,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT != event) {
return;
}
if (!master.Get()) {
return;
}
std::set<Creature*> target_list;
room->grid_service->TraverseCreatures
(room->GetRoomIdx(),
*grid_list_,
[this, &target_list] (Creature* hum, bool& stop)
{
if (!hum->dead) {
if (Collision::CheckCB
(
hum,
hum->GetRadius(),
this,
SkillHelper::GetYlzRange(skill_meta)
)) {
target_list.insert(hum);
}
}
}
);
bool hit = false;
for (auto& c : target_list) {
if (c->team_id == master.Get()->team_id) {
float add_hp = SkillHelper::GetYlzRecoverHp(master.Get(),
c,
skill_meta);
float old_hp = c->GetHP();
c->AddHp(add_hp);
if (std::abs(c->GetHP() - old_hp) > 0.0001f && master.Get()) {
if (meta->_int_param2) {
c->TryAddBuff(master.Get(),
meta->_int_param2
);
}
hit = true;
#ifdef DEBUG
{
std::string dbg_msg = a8::Format
(
"skill_id:%d 医疗站 human.atk:%f target.extRecoverHp:%f add_hp:%f range2:%f",
{
skill_meta->skill_id(),
master.Get()->GetBattleContext()->GetHeroTotalAtk(),
master.Get()->GetBattleContext()->GetExtRecoverHp(),
add_hp,
skill_meta->_number_meta->_float_range2
});
master.Get()->SendDebugMsg(dbg_msg);
a8::XPrintf("%s\n", {dbg_msg});
}
#endif
}
} else if (!c->dead){
float damage = SkillHelper::GetYlzDamage(master.Get(),
c,
skill_meta);
damage = c->GetBattleContext()->CalcReceiveDmg(master.Get(), damage);
float dmg_out = 0.0f;
c->DecHP(damage,
master.Get()->GetUniId(),
master.Get()->GetName(),
VP_Buff,
master.Get()->GetUniId(),
master.Get()->GetName(),
dmg_out);
if (meta->_int_param1) {
c->TryAddBuff(master.Get(),
meta->_int_param1
);
}
hit = true;
}
if (hit) {
break;
}
}//end if
if (hit) {
DetachFromMaster();
}
},
&xtimer_attacher);
RemoveSameSkillObstacle();
}
void RoomObstacle::ForceGridList()
{
if (!grid_list_) {
grid_list_ = new std::set<GridCell*>();
room->grid_service->GetAllCellsByXy(room, GetPos().GetX(), GetPos().GetZ(), *grid_list_);
}
}
void RoomObstacle::RemoveSameSkillObstacle()
{
if (master.Get() && skill_meta) {
master.Get()->RemoveSkillObstacle(skill_meta);
}
}