game2005/server/gameserver/zombiemode.ai.cc
aozhiwei d707b56f54 1
2021-03-30 11:54:45 +08:00

488 lines
13 KiB
C++

#include "precompile.h"
#include <float.h>
#include "zombiemode.ai.h"
#include "android.h"
#include "room.h"
#include "metamgr.h"
#include "player.h"
#include "skill.h"
const int SHUA_RANGE = 580;
enum ShotType_e
{
kShotNone = 0,
kShotClick = 1,
kShotHold = 2,
};
class ZombieAINode
{
public:
ZombieState_e main_state = ZSE_Idle;
long long frameno = 0;
long long exec_frame_num = 0;
long long start_shot_frameno = 0;
long long next_random_move_frameno = 0;
int shot_times = 0;
int total_shot_times = 0;
int next_total_shot_times = 0;
long long param1 = 0;
CreatureWeakPtr target;
CreatureWeakPtr nearest_human;
long long last_check_nearest_human_frameno = 0;
a8::Vec2 shot_dir;
bool moving = false;
MetaData::AI* ai_meta = nullptr;
};
ZombieModeAI::ZombieModeAI()
{
node_ = new ZombieAINode();
}
ZombieModeAI::~ZombieModeAI()
{
A8_SAFE_DELETE(node_);
}
void ZombieModeAI::Update(int delta_time)
{
Human* hum = (Human*)owner;
if (hum->poisoning) {
hum->poisoning_time += delta_time;
}
if (hum->poisoning) {
hum->UpdatePoisoning();
}
if (hum->dead) {
return;
}
UpdateAI();
}
void ZombieModeAI::Reset()
{
ChangeToState(ZSE_Idle);
node_->param1 = 2;
node_->ai_meta = nullptr;
}
float ZombieModeAI::GetAttackRate()
{
if (!node_->ai_meta) {
return 1;
} else {
return node_->ai_meta->i->attack_rate();
}
}
void ZombieModeAI::UpdateAI()
{
Human* hum = (Human*)owner;
if (a8::HasBitFlag(hum->status, HS_Disable)) {
return;
}
if (!node_->ai_meta && GetAiLevel() != 0) {
node_->ai_meta = MetaMgr::Instance()->GetAI(GetAiLevel(), GetAiMode());
if (!node_->ai_meta) {
abort();
}
}
++node_->exec_frame_num;
hum->shot_hold = false;
switch (node_->main_state) {
case ZSE_Idle:
{
UpdateIdle();
}
break;
case ZSE_Thinking:
{
UpdateThinking();
}
break;
case ZSE_Attack:
{
UpdateAttack();
}
break;
case ZSE_RandomWalk:
{
UpdateRandomWalk();
}
break;
case ZSE_Pursuit:
{
UpdatePursuit();
}
break;
default:
{
abort();
}
break;
}
if (hum->HasSpecMove()) {
hum->_UpdateSpecMove();
} else {
if (node_->moving) {
DoMove();
}
}
}
void ZombieModeAI::UpdateIdle()
{
Human* hum = (Human*)owner;
if (hum->room->GetFrameNo() > node_->frameno + node_->param1) {
ChangeToState(ZSE_Thinking);
}
}
void ZombieModeAI::UpdateThinking()
{
Human* hum = (Human*)owner;
if (hum->room->GetGasData().gas_mode == GasInactive ||
hum->room->IsWaitingStart()) {
if (hum->room->IsWaitingStart()) {
ChangeToState(ZSE_Idle);
} else {
ChangeToState(ZSE_RandomWalk);
}
} else {
Creature* target = GetTarget();
if (target) {
node_->target.Attach(target);
ChangeToState(ZSE_Attack);
} else {
if (hum->room->GetFrameNo() >= node_->next_random_move_frameno) {
if ((rand() % 7) < 4) {
ChangeToState(ZSE_Idle);
} else {
ChangeToState(ZSE_RandomWalk);
}
} else {
ChangeToState(ZSE_Idle);
node_->param1 = node_->next_random_move_frameno - hum->room->GetFrameNo();
}
}
}
}
void ZombieModeAI::UpdateAttack()
{
Human* myself = (Human*)owner;
if (myself->HasBuffEffect(kBET_Vertigo)) {
return;
}
if (!node_->target.Get() || node_->target.Get()->dead) {
ChangeToState(ZSE_Thinking);
return;
}
if (node_->exec_frame_num > SERVER_FRAME_RATE * 8 ||
myself->GetRace() == node_->target.Get()->GetRace()) {
ChangeToState(ZSE_Thinking);
return;
}
float distance = myself->GetPos().Distance(node_->target.Get()->GetPos());
if (distance > GetAttackRange()) {
Skill* skill = myself->SelectSkill();
if (myself->CanUseSkill(skill->meta->i->skill_id()) &&
skill->meta->i->skill_id() != TURN_OVER_SKILL_ID &&
distance < skill->meta->i->skill_distance()) {
DoSkill(skill->meta->i->skill_id());
} else if (node_->ai_meta->i->pursuit_radius() <= 0) {
//站桩
ChangeToState(ZSE_Thinking);
} else {
#if 1
ChangeToState(ZSE_Pursuit);
#else
if (distance < node_->ai_meta->i->pursuit_radius()) {
//追击
ChangeToState(ZSE_Pursuit);
} else {
ChangeToState(ZSE_Thinking);
}
#endif
}
return;
}
#if 0
if (myself->GetRace() == kHumanRace) {
return;
}
#endif
//攻击逻辑
switch (node_->ai_meta->i->attack_type()) {
case kShotClick:
{
if (node_->ai_meta->i->attack_interval() > 0) {
if (node_->shot_times < GetAttackTimes()) {
DoShot();
} else {
ChangeToState(ZSE_Idle);
node_->next_total_shot_times = node_->total_shot_times;
node_->param1 = node_->ai_meta->i->attack_interval() / 1000 * SERVER_FRAME_RATE;
}
} else {
myself->shot_hold = true;
DoShot();
}
}
break;
case kShotHold:
{
myself->shot_hold = true;
DoShot();
}
break;
default:
{
ChangeToState(ZSE_Idle);
}
break;
}
}
void ZombieModeAI::UpdateRandomWalk()
{
Human* hum = (Human*)owner;
if (hum->room->GetFrameNo() > node_->frameno + node_->param1) {
ChangeToState(ZSE_Thinking);
}
}
void ZombieModeAI::UpdatePursuit()
{
Human* myself = (Human*)owner;
float distance = myself->GetPos().Distance(node_->target.Get()->GetPos());
if (distance < GetAttackRange()) {
ChangeToState(ZSE_Attack);
} else {
if (node_->exec_frame_num > SERVER_FRAME_RATE * 3) {
ChangeToState(ZSE_RandomWalk);
}
}
}
void ZombieModeAI::DoMove()
{
Human* hum = (Human*)owner;
if (hum->HasBuffEffect(kBET_Vertigo)) {
return;
}
if (std::abs(hum->move_dir.x) > FLT_EPSILON ||
std::abs(hum->move_dir.y) > FLT_EPSILON) {
hum->on_move_collision =
[this] () {
ChangeToState(ZSE_RandomWalk);
return false;
};
int speed = std::max(1, (int)hum->GetSpeed()) * 1;
hum->_UpdateMove(speed);
hum->on_move_collision = nullptr;
if (node_->nearest_human.Get()) {
if (node_->main_state != ZSE_Pursuit &&
hum->GetPos().ManhattanDistance(node_->nearest_human.Get()->GetPos()) < 200) {
ChangeToState(ZSE_Thinking);
} else if (hum->GetPos().ManhattanDistance(node_->nearest_human.Get()->GetPos()) > 800) {
GetTarget();
}
}
}
}
void ZombieModeAI::ChangeToState(ZombieState_e to_state)
{
Human* hum = (Human*)owner;
switch (to_state) {
case ZSE_Idle:
{
node_->target.Reset();
node_->param1 = 0;
node_->start_shot_frameno = 0;
node_->shot_times = 0;
node_->moving = false;
if (hum->room->GetGasData().gas_mode == GasInactive ||
hum->room->IsWaitingStart()) {
node_->param1 = rand() % (3 * SERVER_FRAME_RATE);
} else {
node_->param1 = rand() % (2 * SERVER_FRAME_RATE);
}
}
break;
case ZSE_Thinking:
{
node_->target.Reset();
node_->param1 = 0;
node_->start_shot_frameno = 0;
node_->shot_times = 0;
node_->moving = false;
}
break;
case ZSE_Attack:
{
node_->param1 = 0;
node_->start_shot_frameno = 0;
node_->shot_times = 0;
node_->moving = false;
node_->shot_times = 0;
}
break;
case ZSE_RandomWalk:
{
node_->moving = true;
node_->target.Reset();
node_->param1 = SERVER_FRAME_RATE * node_->ai_meta->GetMoveTime();
node_->start_shot_frameno = 0;
node_->shot_times = 0;
node_->next_random_move_frameno = hum->room->GetFrameNo() +
SERVER_FRAME_RATE * node_->ai_meta->GetMoveIdleTime();
hum->move_dir = a8::Vec2(1.0f, 0);
hum->move_dir.Rotate(a8::RandAngle());
hum->move_dir.Normalize();
hum->attack_dir = hum->move_dir;
if (node_->param1 <= 1) {
node_->moving = false;
}
}
break;
case ZSE_Pursuit:
{
node_->moving = true;
if (node_->target.Get()) {
hum->move_dir = node_->target.Get()->GetPos() - hum->GetPos();
hum->move_dir.Normalize();
hum->attack_dir = hum->move_dir;
}
}
break;
}
node_->main_state = to_state;
node_->frameno = hum->room->GetFrameNo();
node_->exec_frame_num = 0;
}
Creature* ZombieModeAI::GetTarget()
{
if (GetAiLevel() <= 1) {
return nullptr;
}
Human* myself = (Human*)owner;
if (myself->room->GetGasData().gas_mode == GasInactive) {
return nullptr;
}
Creature* target = nullptr;
myself->TouchProperTargets
(
[myself, &target] (Creature* c, bool& stop)
{
if (target) {
if (myself->GetPos().ManhattanDistance(target->GetPos()) >
myself->GetPos().ManhattanDistance(c->GetPos())) {
target = c;
}
} else {
target = c;
}
});
if (target) {
node_->nearest_human.Attach(target);
node_->last_check_nearest_human_frameno = myself->room->GetFrameNo();
float distance = myself->GetPos().Distance(target->GetPos());
#if 0
if (distance > GetAttackRange()) {
target = nullptr;
}
#endif
#if 0
if (target->IsPlayer()) {
target = nullptr;
}
#endif
}
return target;
}
float ZombieModeAI::GetAttackRange()
{
float attack_range = 0;
Human* myself = (Human*)owner;
if (myself->curr_weapon && myself->curr_weapon->meta) {
attack_range = myself->curr_weapon->meta->i->range() + 10;
} else {
abort();
}
attack_range = std::min(node_->ai_meta->i->attack_range(), (int)attack_range);
return attack_range;
}
void ZombieModeAI::DoShot()
{
Human* myself = (Human*)owner;
if (!node_->target.Get()) {
return;
}
bool shot_ok = false;
a8::Vec2 shot_dir = myself->attack_dir;
if (node_->total_shot_times >= node_->next_total_shot_times) {
shot_dir = node_->target.Get()->GetPos() - myself->GetPos();
node_->next_total_shot_times += 7 + (rand() % 6);
myself->attack_dir = shot_dir;
}
if (std::abs(shot_dir.x) > FLT_EPSILON ||
std::abs(shot_dir.y) > FLT_EPSILON) {
shot_dir.Normalize();
if (node_->ai_meta->i->shot_offset_angle() > 0) {
int shot_offset_angle = a8::RandEx(node_->ai_meta->i->shot_offset_angle(),
1);
if (rand() % 10 < 3) {
shot_dir.Rotate(shot_offset_angle / 180.0f);
} else {
shot_dir.Rotate(shot_offset_angle / -180.0f);
}
}
a8::Vec2 old_attack_dir = myself->attack_dir;
myself->attack_dir = shot_dir;
myself->Shot(shot_dir, shot_ok);
myself->attack_dir = old_attack_dir;
if (shot_ok) {
if (node_->shot_times <= 0) {
node_->start_shot_frameno = myself->room->GetFrameNo();
}
++node_->shot_times;
++node_->total_shot_times;
}
}
}
void ZombieModeAI::DoSkill(int skill_id)
{
Human* myself = (Human*)owner;
myself->DoSkill(skill_id, node_->target.Get()->GetEntityUniId(), a8::Vec2(), node_->target.Get()->GetPos());
}
int ZombieModeAI::GetAttackTimes()
{
Human* myself = (Human*)owner;
int attack_times = 0;
if (myself->curr_weapon) {
attack_times = std::min(node_->ai_meta->i->attack_times(), myself->curr_weapon->GetClipVolume());
} else {
attack_times = node_->ai_meta->i->attack_times();
}
if (attack_times <= 0) {
return node_->ai_meta->i->attack_times();
} else {
return 0;
}
}