aozhiwei 2a610760b1 1
2021-09-08 17:21:14 +08:00

667 lines
21 KiB
C++

#include "precompile.h"
#include <math.h>
#include <float.h>
#include "buff.h"
#include "metamgr.h"
#include "human.h"
#include "room.h"
#include "collider.h"
#include "skill.h"
#include "incubator.h"
#include "car.h"
Buff::Buff()
{
}
void Buff::Init()
{
INIT_LIST_HEAD(&effect_entry);
INIT_LIST_HEAD(&depend_entry);
INIT_LIST_HEAD(&cond_entry);
INIT_LIST_HEAD(&on_remove_contexts);
}
void Buff::UnInit()
{
list_del_init(&effect_entry);
if (!list_empty(&depend_entry)) {
list_del_init(&depend_entry);
}
if (!list_empty(&cond_entry)) {
list_del_init(&cond_entry);
}
while (!list_empty(&on_remove_contexts)) {
RemoveBuffCbConext* cb = list_last_entry(&on_remove_contexts, RemoveBuffCbConext, entry);
*cb->next = (*cb->next)->next;
list_del_init(&cb->entry);
}
}
int Buff::GetLeftTime()
{
int passed_ms = (owner->room->GetFrameNo() - add_frameno) * FRAME_RATE_MS;
return std::max(GetLastingTime() - passed_ms, 0);
}
int Buff::GetLastingTime()
{
return meta->i->duration_time() * 1000;
}
void Buff::FillMFBuff(cs::MFBuff* buff_pb)
{
buff_pb->set_buff_id(meta->i->buff_id());
buff_pb->set_left_time(GetLeftTime());
buff_pb->set_lasting_time(GetLastingTime());
buff_pb->set_buff_uniid(buff_uniid);
if (owner->IsHuman() &&
(meta->i->buff_effect() == kBET_Driver ||
meta->i->buff_effect() == kBET_Passenger)) {
buff_pb->add_params(((Human*)owner)->GetCar()->meta->i->id());
}
}
void Buff::ProcDelayAddBuff()
{
InternalTimerAddBuff();
}
void Buff::ProcIntervalAddBuff()
{
InternalTimerAddBuff();
}
void Buff::ProcBatchAddBuff()
{
std::shared_ptr<Ability> old_context_ability = owner->context_ability;
a8::Vec2 old_context_dir = owner->context_dir;
a8::Vec2 old_context_pos = owner->context_pos;
owner->context_dir = owner->GetAttackDir();
owner->context_pos = owner->GetPos();
for (auto& tuple : meta->batch_add_list) {
int rand_space = std::get<0>(tuple);
const auto& items = std::get<1>(tuple);
if (items.empty()) {
abort();
}
int rnd = rand();
if (rand_space == -1) {
//概率
rnd = rnd % 10000;
if (rnd < std::get<1>(items[0])){
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(std::get<0>(items[0]));
if (buff_meta) {
owner->AddBuff(caster_.Get(), buff_meta, 1, nullptr);
}
}
} else {
//权重
rnd = rnd % rand_space;
for (const auto& item : items) {
if (rnd <= std::get<1>(item)) {
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(std::get<0>(item));
if (buff_meta) {
owner->AddBuff(caster_.Get(), buff_meta, 1, nullptr);
}
break;
}
}
}
}
owner->context_dir = old_context_dir;
owner->context_pos = old_context_pos;
owner->context_ability = old_context_ability;
}
void Buff::InternalTimerAddBuff()
{
if (!caster_.Get()) {
return;
}
auto timer_func =
[] (const a8::XParams& param)
{
Creature* receiver = (Creature*)param.sender.GetUserData();
SkillCasterState* caster_state = (SkillCasterState*)param.param1.GetUserData();
if (caster_state->caster.Get()) {
std::shared_ptr<Ability> old_context_ability = receiver->context_ability;
a8::Vec2 old_context_dir = receiver->context_dir;
a8::Vec2 old_context_pos = receiver->context_pos;
receiver->context_dir = receiver->GetAttackDir();
receiver->context_pos = receiver->GetPos();
int buff_id = param.param2;
MetaData::Skill* skill = (MetaData::Skill*)param.param3.GetUserData();
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id);
if (buff_meta) {
SkillCasterState old_caster_state;
caster_state->caster.Get()->FillSkillCasterState(&old_caster_state);
caster_state->caster.Get()->RecoverSkillCasterState(caster_state);
receiver->AddBuff(caster_state->caster.Get(), buff_meta, 1, skill);
caster_state->caster.Get()->RecoverSkillCasterState(&old_caster_state);
}
receiver->context_dir = old_context_dir;
receiver->context_pos = old_context_pos;
receiver->context_ability = old_context_ability;
}
};
auto timer_after_func =
[] (const a8::XParams& param)
{
SkillCasterState* caster_state = (SkillCasterState*)param.param1.GetUserData();
delete caster_state;
};
SkillCasterState* caster_state = new SkillCasterState;
caster_.Get()->FillSkillCasterState(caster_state);
a8::XParams param;
param.SetSender(owner);
param.SetParam1(caster_state);
param.SetParam2(meta->i->buff_param2());
param.SetParam3(caster_.Get()->CurrentSkill() ? caster_.Get()->CurrentSkill()->meta : nullptr);
switch (meta->i->buff_effect() ) {
case kBET_DelayAddBuff:
{
owner->room->xtimer.AddDeadLineTimerAndAttach
(
meta->param1 * SERVER_FRAME_RATE,
param,
timer_func,
&xtimer_attacher.timer_list_,
timer_after_func);
}
break;
case kBET_IntervalAddBuff:
{
owner->room->xtimer.AddRepeatTimerAndAttach
(
meta->param1 / FRAME_RATE_MS,
param,
timer_func,
&xtimer_attacher.timer_list_,
timer_after_func);
}
break;
default:
{
}
break;
}
}
void Buff::ProcSummonHero()
{
if (!caster_.Get()->IsHuman()) {
return;
}
}
void Buff::ProcBeRecycle()
{
owner->room->xtimer.AddRepeatTimerAndAttach
(
SERVER_FRAME_RATE * 2,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Buff* buff = (Buff*)param.sender.GetUserData();
if (buff->owner->IsHuman()) {
buff->owner->room->GetIncubator()->RecycleAndroid((Human*)buff->owner);
}
},
&xtimer_attacher.timer_list_
);
}
bool Buff::NeedSync(Human* hum)
{
return !meta->i->only_server() || !(meta->i->only_self() && owner == hum);
}
bool Buff::FreezeOperate()
{
return meta->i->buff_effect() == kBET_Become && !hold_weapons_.empty();
}
void Buff::ProcBecome()
{
hold_curr_weapon_idx_ = caster_.Get()->GetCurrWeapon()->weapon_idx;
if (caster_.Get()->IsHuman() && meta->param2 > 0.01) {
std::vector<std::string> strings;
a8::Split(meta->i->buff_param2(), strings, ':');
for (size_t i = 0; i < strings.size(); ++i) {
int weapon_id = a8::XValue(strings[i]);
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(weapon_id);
if (weapon_meta && i < caster_.Get()->weapons.size()) {
int weapon_idx = weapon_meta->GetWeaponIdx();
if (weapon_idx >= 0 && weapon_idx < caster_.Get()->weapons.size()) {
Weapon* weapon = &caster_.Get()->weapons[weapon_idx];
hold_weapons_.push_back(*weapon);
weapon->weapon_id = weapon_meta->i->id();
weapon->weapon_lv = 1;
weapon->meta = weapon_meta;
weapon->Recalc();
weapon->ammo = weapon->GetClipVolume();
if (i == 0) {
caster_.Get()->SetCurrWeapon(weapon);
}
}
}
}
caster_.Get()->need_sync_active_player = true;
caster_.Get()->SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
}
void Buff::ProcRemoveBecome()
{
RecoverHoldWeapons();
}
void Buff::ProcDriver()
{
hold_curr_weapon_idx_ = caster_.Get()->GetCurrWeapon()->weapon_idx;
if (caster_.Get()->IsHuman()) {
Human* hum = (Human*)caster_.Get();
if (hum->GetCar() && hum->GetCar()->GetCurrWeapon()) {
hold_weapons_.push_back(hum->weapons[hum->GetCar()->GetCurrWeapon()->weapon_idx]);
hum->weapons[hum->GetCar()->GetCurrWeapon()->weapon_idx] = *hum->GetCar()->GetCurrWeapon();
hum->SetCurrWeapon(&hum->weapons[hum->GetCar()->GetCurrWeapon()->weapon_idx]);
if (hum->GetCar()->meta &&
hum->GetSeat() < hum->GetCar()->meta->shoot_offsets.size() &&
std::get<0>(hum->GetCar()->meta->shoot_offsets[hum->GetSeat()])
) {
hum->shoot_offset = std::get<1>(hum->GetCar()->meta->shoot_offsets[hum->GetSeat()]);
}
}
}
caster_.Get()->need_sync_active_player = true;
caster_.Get()->SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
void Buff::ProcRemoveDriver()
{
RecoverHoldWeapons();
caster_.Get()->shoot_offset = a8::Vec2();
}
void Buff::ProcPassenger()
{
hold_curr_weapon_idx_ = caster_.Get()->GetCurrWeapon()->weapon_idx;
CalcPassengerShotOffset();
caster_.Get()->need_sync_active_player = true;
caster_.Get()->SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
void Buff::ProcRemovePassenger()
{
RecoverHoldWeapons();
caster_.Get()->shoot_offset = a8::Vec2();
}
void Buff::RecoverHoldWeapons()
{
for (auto& weapon : hold_weapons_) {
if (weapon.weapon_idx >= 0 &&
weapon.weapon_idx < caster_.Get()->weapons.size()) {
caster_.Get()->weapons[weapon.weapon_idx] = weapon;
}
}
if (hold_curr_weapon_idx_ >=0 &&
hold_curr_weapon_idx_ <= caster_.Get()->weapons.size() &&
caster_.Get()->weapons[hold_curr_weapon_idx_].weapon_id != 0) {
Weapon* next_weapon = &caster_.Get()->weapons[hold_curr_weapon_idx_];
caster_.Get()->SetCurrWeapon(next_weapon);
} else {
if (!hold_weapons_.empty()) {
Weapon* next_weapon = caster_.Get()->AutoChgWeapon();
if (next_weapon) {
caster_.Get()->SetCurrWeapon(next_weapon);
}
}
}
hold_weapons_.clear();
caster_.Get()->need_sync_active_player = true;
caster_.Get()->SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
void Buff::ProcSprint()
{
if (caster_.Get()->IsPlayer()) {
owner->IncDisableMoveDirTimes();
{
int old_times = owner->GetDisableMoveDirTimes();
owner->SetDisableMoveDirTimes(0);
owner->SetMoveDir(owner->context_dir);
owner->SetAttackDir(owner->context_dir);
owner->SetDisableMoveDirTimes(old_times);
}
Player* hum = (Player*)caster_.Get();
std::set<int>* hited_objects = new std::set<int>;
owner->room->xtimer.AddRepeatTimerAndAttach
(
1,
a8::XParams()
.SetSender(this)
.SetParam1(hited_objects),
[] (const a8::XParams& param)
{
Buff* buff = (Buff*)param.sender.GetUserData();
std::set<int>* hited_objects = (std::set<int>*)param.param1.GetUserData();
std::set<Creature*> enemys;
buff->owner->GetHitEnemys(enemys);
for (auto& enemy : enemys) {
if (hited_objects->find(enemy->GetUniId()) != hited_objects->end()) {
hited_objects->insert(enemy->GetUniId());
for (int buff_id : buff->meta->param3_int_list) {
enemy->TryAddBuff(buff->owner, buff_id);
}
}
}
},
&xtimer_attacher.timer_list_,
[] (const a8::XParams& param)
{
std::set<int>* hited_objects = (std::set<int>*)param.param1.GetUserData();
delete hited_objects;
});
}
}
void Buff::ProcSeletTargetWithSelfPos()
{
std::vector<Creature*> targets;
owner->TraverseCreatures
(
[this, &targets] (Creature* c, bool& stop)
{
if (owner->GetPos().Distance(c->GetPos()) < meta->int_param3) {
switch (meta->int_param1) {
case kBST_All:
{
targets.push_back(c);
}
break;
case kBST_Self:
{
if (c == owner) {
targets.push_back(c);
}
}
break;
case kBST_FriendlyIncludeSelf:
{
if (c->team_id == owner->team_id) {
targets.push_back(c);
}
}
break;
case kBST_FriendlyExcludeSelf:
{
if (c->team_id == owner->team_id && c != owner) {
targets.push_back(c);
}
}
break;
case kBST_Enemy:
{
if (c->team_id != owner->team_id) {
targets.push_back(c);
}
}
break;
case kBST_EnemyAndSelf:
{
if (c->team_id != owner->team_id || c == owner) {
targets.push_back(c);
}
}
break;
default:
{
}
break;
}
}
}
);
for (auto& target : targets) {
for (int buff_id : meta->param2_int_list) {
target->TryAddBuff(caster_.Get(), buff_id);
}
}
}
void Buff::ProcTurnOver()
{
Skill* skill = owner->CurrentSkill();
if (!skill) {
return;
}
MetaData::SkillPhase* phase = owner->GetCurrSkillPhase();
if (!phase) {
return;
}
#if 0
if (phase->time_offset < skill->GetPassedTime()) {
return;
}
#endif
a8::Vec2 old_pos = owner->GetPos();
float distance =
owner->HasBuffEffect(kBET_Car) ? phase->param1.GetDouble() * 1.5 : phase->param1.GetDouble();
owner->_UpdateMove(distance);
int moved_distance = (int)owner->GetPos().Distance(old_pos);
moved_distance = std::min(moved_distance, 200);
if (!meta->param1_int_list.empty() && moved_distance > 2) {
std::set<Creature*> target_list;
owner->TraverseCreatures
(
[this, &target_list] (Creature* c, bool& stop)
{
if (owner->IsProperTarget(c) && owner->GetPos().Distance(c->GetPos()) < 300) {
target_list.insert(c);
}
});
a8::Vec2 curr_pos = owner->GetPos();
a8::Vec2 dir = owner->GetPos() - old_pos;
dir.Normalize();
for (int i = 5; i < moved_distance; i += 5) {
owner->SetPos(old_pos + dir * i);
std::list<Creature*> hit_objects;
for (auto& target : target_list) {
if (owner->TestCollision(owner->room, target)) {
hit_objects.push_back(target);
}
}
for (auto& target : hit_objects) {
target_list.erase(target);
target->room->xtimer.AddDeadLineTimerAndAttach
(
meta->int_param4 / FRAME_RATE_MS * (i / moved_distance),
a8::XParams()
.SetSender(target)
.SetParam1(meta)
.SetParam2(owner->GetUniId()),
[] (const a8::XParams& param)
{
Creature* c = (Creature*)param.sender.GetUserData();
MetaData::Buff* buff_meta = (MetaData::Buff*)param.param1.GetUserData();
Entity* caster = c->room->GetEntityByUniId(param.param2);
if (caster && caster->IsCreature(c->room)) {
for (int buff_id : buff_meta->param1_int_list) {
c->TryAddBuff((Creature*)caster, buff_id);
}
}
},
&target->xtimer_attacher.timer_list_);
}
}
owner->SetPos(curr_pos);
}
if (!meta->param2_int_list.empty()) {
owner->room->xtimer.AddDeadLineTimerAndAttach
(
meta->int_param4 / FRAME_RATE_MS,
a8::XParams()
.SetSender(owner)
.SetParam1(meta),
[] (const a8::XParams& param)
{
Creature* c = (Creature*)param.sender.GetUserData();
MetaData::Buff* buff_meta = (MetaData::Buff*)param.param1.GetUserData();
c->TraverseCreatures
(
[c, buff_meta] (Creature* target, bool& stop)
{
if (c->GetPos().Distance(target->GetPos()) < buff_meta->int_param3) {
for (int buff_id : buff_meta->param2_int_list) {
target->TryAddBuffWithTarget(c, buff_id);
}
}
});
},
&owner->xtimer_attacher.timer_list_
);
}
}
CreatureWeakPtr& Buff::GetCaster()
{
return caster_;
}
void Buff::SetCaster(Creature* caster)
{
if (caster) {
caster_.Attach(caster);
} else {
caster_.Reset();
}
}
void Buff::ProcPullToWalkable()
{
if (!owner->CollisonDetection()) {
return;
}
a8::Vec2 move_dir = owner->GetMoveDir();
if (std::abs(move_dir.x) > FLT_EPSILON ||
std::abs(move_dir.y) > FLT_EPSILON
) {
a8::Vec2 old_pos = owner->GetPos();
for (int i = 1; i < 2000; i += 5) {
owner->SetPos(old_pos + move_dir * i);
if (!owner->CollisonDetection()) {
owner->room->grid_service->MoveCreature(owner);
return;
}
}
owner->FindLocation();
} else {
owner->FindLocation();
}
}
void Buff::ProcOnceChgAttr()
{
}
void Buff::ProcAutoShot()
{
owner->room->xtimer.AddRepeatTimerAndAttach
(
1,
a8::XParams()
.SetSender(this),
[] (const a8::XParams& param)
{
Buff* buff = (Buff*)param.sender.GetUserData();
if (buff->owner->IsHuman()) {
#if 0
buff->owner->AsHuman()->shot_start = true;
#endif
buff->owner->AsHuman()->shot_hold = true;
buff->owner->AsHuman()->series_shot_frames = 0;
}
},
&xtimer_attacher.timer_list_,
[] (const a8::XParams& param)
{
Buff* buff = (Buff*)param.sender.GetUserData();
if (buff->owner->IsHuman()) {
#if 0
buff->owner->AsHuman()->shot_start = false;
#endif
buff->owner->AsHuman()->shot_hold = false;
buff->owner->AsHuman()->series_shot_frames = 0;
}
});
}
void Buff::ProcBeatBack()
{
if (caster_.Get()) {
if (std::abs(caster_.Get()->context_dir.x) > FLT_EPSILON ||
std::abs(caster_.Get()->context_dir.x) > FLT_EPSILON) {
if (std::abs(meta->int_param1) > 0) {
a8::Vec2 old_move_dir = owner->GetMoveDir();
owner->SetMoveDir(caster_.Get()->context_dir);
owner->_UpdateMove(meta->param1);
owner->SetMoveDir(old_move_dir);
}
}
}
}
void Buff::ProcDisperse()
{
std::vector<int> del_buffs;
owner->TraverseBuff
(
[this, &del_buffs] (Buff* buff, bool& stop)
{
for (int tag : meta->param1_int_set) {
if (buff->meta->tags.find(tag) != buff->meta->tags.end()) {
del_buffs.push_back(buff->buff_uniid);
break;
}
}
});
for (int buff_uniid : del_buffs) {
owner->RemoveBuffByUniId(buff_uniid);
}
}
void Buff::CalcPassengerShotOffset()
{
if (meta->i->buff_effect() == kBET_Passenger && caster_.Get()->IsHuman()) {
Human* hum = (Human*)caster_.Get();
if (hum->GetCar() && hum->GetCar()->GetCurrWeapon()) {
hold_weapons_.push_back(hum->weapons[hum->GetCar()->GetCurrWeapon()->weapon_idx]);
hum->weapons[hum->GetCar()->GetCurrWeapon()->weapon_idx] = *hum->GetCar()->GetCurrWeapon();
hum->SetCurrWeapon(&hum->weapons[hum->GetCar()->GetCurrWeapon()->weapon_idx]);
if (hum->GetCar()->meta &&
hum->GetSeat() < hum->GetCar()->meta->shoot_offsets.size() &&
std::get<0>(hum->GetCar()->meta->shoot_offsets[hum->GetSeat()])
) {
hum->shoot_offset = std::get<1>(hum->GetCar()->meta->shoot_offsets[hum->GetSeat()]);
}
}
}
}