aozhiwei c5161158d7 1
2021-03-19 16:36:24 +08:00

444 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "precompile.h"
#include <float.h>
#include "hero.ai.h"
#include "hero.h"
#include "room.h"
#include "metamgr.h"
#include "player.h"
const int SHUA_RANGE = 580;
enum ShotType_e
{
kShotNone = 0,
kShotClick = 1,
kShotHold = 2,
};
/*
nn目标ai可切换降级/升级(根据系统当前负载)
ai级别
1: 木桩(不射击)
2: 站桩间隔开枪
3: 站桩描边射击
4: 站桩扫射(连续射击)
5: 跑动间隔开枪
6: 跑动描边射击
7: 跑动扫射
8: 跑动射击
*/
HeroAI::~HeroAI()
{
}
void HeroAI::Update(int delta_time)
{
Hero* hero = (Hero*)owner;
#if 0
if (hum->poisoning) {
hum->poisoning_time += delta_time;
}
if (hum->poisoning) {
hum->UpdatePoisoning();
}
#endif
if (hero->dead) {
return;
}
UpdateAI();
}
float HeroAI::GetAttackRate()
{
if (!ai_meta) {
return 1;
} else {
return ai_meta->i->attack_rate();
}
}
void HeroAI::UpdateAI()
{
Hero* hero = (Hero*)owner;
if (!ai_meta && GetAiLevel() != 0) {
ai_meta = MetaMgr::Instance()->GetAI(GetAiLevel(), 0);
if (!ai_meta) {
abort();
}
}
++node_.exec_frame_num;
hero->shot_hold = false;
switch (node_.main_state) {
case HSE_Idle:
{
UpdateIdle();
}
break;
case HSE_Thinking:
{
UpdateThinking();
}
break;
case HSE_Attack:
{
UpdateAttack();
}
break;
case HSE_RandomWalk:
{
UpdateRandomWalk();
}
break;
case HSE_Pursuit:
{
UpdatePursuit();
}
break;
default:
{
abort();
}
break;
}
if (moving_) {
DoMoveAI();
}
}
void HeroAI::UpdateIdle()
{
Hero* hero = (Hero*)owner;
if (hero->room->GetFrameNo() > node_.frameno + node_.param1) {
ChangeToStateAI(HSE_Thinking);
}
}
void HeroAI::UpdateThinking()
{
#if 1
ChangeToStateAI(HSE_RandomWalk);
return ;
#endif
Hero* hero = (Hero*)owner;
if (hero->room->GetGasData().gas_mode == GasInactive ||
hero->room->IsWaitingStart() ||
hero->HasBuffEffect(kBET_Jump)
) {
if (hero->room->IsWaitingStart()) {
ChangeToStateAI(HSE_Idle);
} else {
ChangeToStateAI(HSE_RandomWalk);
}
} else {
Human* target = GetTarget();
if (target) {
node_.target = target;
ChangeToStateAI(HSE_Attack);
} else {
if (hero->room->GetFrameNo() >= node_.next_random_move_frameno) {
if ((rand() % 7) < 4) {
ChangeToStateAI(HSE_Idle);
} else {
ChangeToStateAI(HSE_RandomWalk);
}
} else {
ChangeToStateAI(HSE_Idle);
node_.param1 = node_.next_random_move_frameno - hero->room->GetFrameNo();
}
}
}
}
void HeroAI::UpdateAttack()
{
Hero* myself = (Hero*)owner;
if (!node_.target || node_.target->dead) {
ChangeToStateAI(HSE_Thinking);
return;
}
if (node_.exec_frame_num > SERVER_FRAME_RATE * 8) {
ChangeToStateAI(HSE_Thinking);
return;
}
float distance = myself->GetPos().Distance(node_.target->GetPos());
if (distance > GetAttackRange()) {
if (ai_meta->i->pursuit_radius() <= 0) {
//站桩
ChangeToStateAI(HSE_Thinking);
} else {
if (distance < ai_meta->i->pursuit_radius()) {
//追击
ChangeToStateAI(HSE_Pursuit);
} else {
ChangeToStateAI(HSE_Thinking);
}
}
return;
}
//攻击逻辑
switch (ai_meta->i->attack_type()) {
case kShotClick:
{
if (ai_meta->i->attack_interval() > 0) {
if (node_.shot_times < GetAttackTimes()) {
DoShotAI();
} else {
ChangeToStateAI(HSE_Idle);
node_.next_total_shot_times = node_.total_shot_times;
node_.param1 = ai_meta->i->attack_interval() / 1000 * SERVER_FRAME_RATE;
}
} else {
myself->shot_hold = true;
DoShotAI();
}
}
break;
case kShotHold:
{
myself->shot_hold = true;
DoShotAI();
}
break;
default:
{
ChangeToStateAI(HSE_Idle);
}
break;
}
}
void HeroAI::UpdateRandomWalk()
{
Hero* hero = (Hero*)owner;
if (hero->room->GetFrameNo() > node_.frameno + node_.param1) {
ChangeToStateAI(HSE_Thinking);
}
}
void HeroAI::UpdatePursuit()
{
Hero* myself = (Hero*)owner;
float distance = myself->GetPos().Distance(node_.target->GetPos());
if (!myself->HasBuffEffect(kBET_Jump) &&
distance < GetAttackRange()) {
ChangeToStateAI(HSE_Attack);
} else {
if (node_.exec_frame_num > 100 * 2) {
ChangeToStateAI(HSE_RandomWalk);
}
}
}
void HeroAI::DoMoveAI()
{
Hero* hero = (Hero*)owner;
if (std::abs(hero->move_dir.x) > FLT_EPSILON ||
std::abs(hero->move_dir.y) > FLT_EPSILON) {
hero->on_move_collision =
[this] () {
ChangeToStateAI(HSE_RandomWalk);
return false;
};
int speed = std::max(1, (int)hero->GetSpeed()) * 1;
hero->_UpdateMove(speed);
hero->on_move_collision = nullptr;
}
}
void HeroAI::ChangeToStateAI(HeroState_e to_state)
{
Hero* hero = (Hero*)owner;
switch (to_state) {
case HSE_Idle:
{
node_.target = nullptr;
node_.param1 = 0;
node_.start_shot_frameno = 0;
node_.shot_times = 0;
moving_ = false;
if (hero->room->GetGasData().gas_mode == GasInactive ||
hero->room->IsWaitingStart() ||
hero->HasBuffEffect(kBET_Jump)) {
node_.param1 = rand() % (3 * SERVER_FRAME_RATE);
} else {
node_.param1 = rand() % (2 * SERVER_FRAME_RATE);
}
}
break;
case HSE_Thinking:
{
node_.target = nullptr;
node_.param1 = 0;
node_.start_shot_frameno = 0;
node_.shot_times = 0;
moving_ = false;
}
break;
case HSE_Attack:
{
node_.param1 = 0;
node_.start_shot_frameno = 0;
node_.shot_times = 0;
moving_ = false;
node_.shot_times = 0;
}
break;
case HSE_RandomWalk:
{
moving_ = true;
node_.target = nullptr;
#if 1
node_.param1 = SERVER_FRAME_RATE * ai_meta->GetMoveTime();
#else
node_.param1 = SERVER_FRAME_RATE * 5 + rand() % (SERVER_FRAME_RATE * 3);
#endif
node_.start_shot_frameno = 0;
node_.shot_times = 0;
node_.next_random_move_frameno = hero->room->GetFrameNo() +
SERVER_FRAME_RATE * ai_meta->GetMoveIdleTime();
hero->move_dir = a8::Vec2(1.0f, 0);
hero->move_dir.Rotate(a8::RandAngle());
hero->move_dir.Normalize();
#if 0
hero->attack_dir = hero->move_dir;
#endif
if (node_.param1 <= 1) {
moving_ = false;
}
}
break;
case HSE_Pursuit:
{
moving_ = true;
if (node_.target) {
hero->move_dir = node_.target->GetPos() - hero->GetPos();
hero->move_dir.Normalize();
#if 0
hero->attack_dir = hero->move_dir;
#endif
}
}
break;
}
node_.main_state = to_state;
node_.frameno = hero->room->GetFrameNo();
node_.exec_frame_num = 0;
}
Human* HeroAI::GetTarget()
{
if (GetAiLevel() <= 1) {
return nullptr;
}
Hero* myself = (Hero*)owner;
if (myself->room->GetGasData().gas_mode == GasInactive) {
return nullptr;
}
Human* target = nullptr;
myself->TouchAllLayerHumanList
(
[myself, &target] (Human* hum, bool& stop)
{
if (hum->dead) {
return;
}
if (a8::HasBitFlag(hum->status, HS_Disable)) {
return;
}
if (myself->team_id == hum->team_id) {
return;
}
if (target) {
if (myself->GetPos().ManhattanDistance(target->GetPos()) >
myself->GetPos().ManhattanDistance(hum->GetPos())) {
target = hum;
}
} else {
target = hum;
}
});
if (target) {
node_.nearest_human = target;
node_.last_check_nearest_human_frameno = myself->room->GetFrameNo();
float distance = myself->GetPos().Distance(target->GetPos());
if (distance > GetAttackRange()) {
target = nullptr;
}
}
return target;
}
float HeroAI::GetAttackRange()
{
float attack_range = 0;
Hero* myself = (Hero*)owner;
#if 0
if (myself->curr_weapon && myself->curr_weapon->meta) {
attack_range = myself->curr_weapon->meta->i->range();
}
#endif
attack_range = std::min(ai_meta->i->attack_range(), (int)attack_range);
return attack_range;
}
void HeroAI::DoShotAI()
{
Hero* myself = (Hero*)owner;
if (!node_.target) {
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->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 (ai_meta->i->shot_offset_angle() > 0) {
int shot_offset_angle = a8::RandEx(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;
#if 0
myself->Shot(shot_dir, shot_ok);
#endif
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;
}
}
}
int HeroAI::GetAttackTimes()
{
Hero* myself = (Hero*)owner;
#if 0
if (myself->curr_weapon) {
return std::min(ai_meta->i->attack_times(), myself->curr_weapon->GetClipVolume());
} else {
return ai_meta->i->attack_times();
}
#endif
return 0;
}