aozhiwei b5840bd56e 1
2021-08-06 08:32:50 +00:00

604 lines
17 KiB
C++

#include "precompile.h"
#include <float.h>
#include "hero.ai.h"
#include "hero.h"
#include "room.h"
#include "metamgr.h"
#include "player.h"
#include "roomobstacle.h"
#include "car.h"
#include "app.h"
HeroAI::HeroAI()
{
node_ = new HeroAINode();
}
HeroAI::~HeroAI()
{
A8_SAFE_DELETE(node_);
}
void HeroAI::Update(int delta_time)
{
Hero* hero = (Hero*)owner;
if (hero->poisoning) {
hero->poisoning_time += delta_time;
}
if (hero->poisoning) {
hero->UpdatePoisoning();
}
if (hero->dead) {
return;
}
UpdateAI();
}
float HeroAI::GetAttackRate()
{
return ai_meta->i->attack_rate();
}
void HeroAI::UpdateAI()
{
Hero* hero = (Hero*)owner;
++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;
case HSE_FollowMaster:
{
UpdateFollowMaster();
}
break;
case HSE_SweepMine:
{
UpdateSweepMine();
}
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()
{
Hero* hero = (Hero*)owner;
if (hero->room->GetGasData().gas_mode == GasInactive ||
hero->room->IsWaitingStart() ||
hero->HasBuffEffect(kBET_PeaceMode) ||
hero->HasBuffEffect(kBET_Jump)
) {
if (hero->room->IsWaitingStart()) {
ChangeToStateAI(HSE_Idle);
} else {
ChangeToStateAI(HSE_RandomWalk);
}
} else {
if (hero->HasBuffEffect(kBET_FollowMaster) &&
hero->master.Get() &&
hero->master.Get()->GetPos().ManhattanDistance(hero->GetPos()) > 200) {
ChangeToStateAI(HSE_FollowMaster);
return;
}
if (ai_meta->i->ai_kind() == kAI_MineSweeper) {
RoomObstacle* target = FindObstacleTarget();
if (target) {
node_->target_obstacle.Attach(target);
ChangeToStateAI(HSE_SweepMine);
return;
}
}
Creature* target = GetTarget();
if (target) {
node_->target.Attach(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.Get() || node_->target.Get()->dead) {
ChangeToStateAI(HSE_Thinking);
return;
}
if (node_->exec_frame_num > SERVER_FRAME_RATE * 8) {
ChangeToStateAI(HSE_Thinking);
return;
}
if (myself->HasBuffEffect(kBET_Vertigo)) {
return;
}
#ifdef DEBUG
if (App::Instance()->HasFlag(20)) {
ChangeToStateAI(HSE_Thinking);
return;
}
#endif
float distance = myself->GetPos().Distance(node_->target.Get()->GetPos());
if (distance > GetShotRange()) {
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;
if (node_->target.Get()) {
float distance = myself->GetPos().Distance(node_->target.Get()->GetPos());
if (!myself->HasBuffEffect(kBET_Jump) &&
!myself->HasBuffEffect(kBET_PeaceMode) &&
distance < GetShotRange()) {
ChangeToStateAI(HSE_Attack);
} else {
if (node_->exec_frame_num % SERVER_FRAME_RATE == 0) {
a8::Vec2 move_dir = node_->target.Get()->GetPos() - myself->GetPos();
if (distance > 10) {
move_dir.Normalize();
myself->SetMoveDir(move_dir);
myself->SetAttackDir(myself->GetMoveDir());
}
}
if (node_->exec_frame_num > 100 * 2) {
ChangeToStateAI(HSE_RandomWalk);
}
}
} else {
ChangeToStateAI(HSE_RandomWalk);
}
}
void HeroAI::UpdateFollowMaster()
{
Hero* myself = (Hero*)owner;
if (myself->master.Get()) {
float mdistance = myself->GetPos().ManhattanDistance(node_->target_pos);
if (mdistance < 10) {
ChangeToStateAI(HSE_Thinking);
}
}
}
void HeroAI::DoMoveAI()
{
Hero* hero = (Hero*)owner;
if (hero->HasBuffEffect(kBET_Vertigo)) {
return;
}
if (std::abs(hero->GetMoveDir().x) > FLT_EPSILON ||
std::abs(hero->GetMoveDir().y) > FLT_EPSILON) {
auto old_on_move_collision_func = hero->on_move_collision;
hero->on_move_collision =
[this] () {
if (node_->main_state == HSE_FollowMaster) {
ChangeToStateAI(HSE_FollowMaster);
} else {
ChangeToStateAI(HSE_RandomWalk);
}
return false;
};
int speed = std::max(1, (int)hero->GetSpeed()) * 1;
hero->_UpdateMove(speed);
hero->on_move_collision = old_on_move_collision_func;
}
}
void HeroAI::ChangeToStateAI(HeroState_e to_state)
{
Hero* hero = (Hero*)owner;
switch (to_state) {
case HSE_Idle:
{
node_->target.Reset();
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.Reset();
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.Reset();
#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();
a8::Vec2 move_dir = a8::Vec2(1.0f, 0);
move_dir.Rotate(a8::RandAngle());
move_dir.Normalize();
hero->SetMoveDir(move_dir);
hero->SetAttackDir(hero->GetMoveDir());
if (node_->param1 <= 1) {
moving_ = false;
}
}
break;
case HSE_Pursuit:
{
moving_ = true;
if (node_->target.Get()) {
a8::Vec2 move_dir = node_->target.Get()->GetPos() - hero->GetPos();
move_dir.Normalize();
hero->SetMoveDir(move_dir);
hero->SetAttackDir(hero->GetMoveDir());
}
}
break;
case HSE_FollowMaster:
{
moving_ = true;
if (hero->master.Get()) {
a8::Vec2 target_pos = hero->master.Get()->GetPos();
a8::Vec2 target_dir = a8::Vec2::UP;
target_dir.Rotate(a8::RandAngle());
target_pos = target_pos + target_dir * 80;
a8::Vec2 move_dir = target_pos - hero->GetPos();
move_dir.Normalize();
hero->SetMoveDir(move_dir);
hero->SetAttackDir(hero->GetMoveDir());
node_->target_pos = target_pos;
}
}
break;
case HSE_SweepMine:
{
moving_ = true;
a8::Vec2 move_dir = node_->target_obstacle.Get()->GetPos() - hero->GetPos();
move_dir.Normalize();
hero->SetMoveDir(move_dir);
hero->SetAttackDir(hero->GetMoveDir());
}
break;
default:
{
}
break;
}
node_->main_state = to_state;
node_->frameno = hero->room->GetFrameNo();
node_->exec_frame_num = 0;
node_->last_collision_times = hero->GetCollisionTimes();
}
Creature* HeroAI::GetTarget()
{
if (ai_meta->i->attack_range() <= 0) {
return nullptr;
}
Hero* myself = (Hero*)owner;
if (myself->room->GetGasData().gas_mode == GasInactive) {
return nullptr;
}
Creature* target = nullptr;
myself->TraverseProperTargets
(
[myself, &target] (Creature* hum, bool& stop)
{
if (hum->HasBuffEffect(kBET_Camouflage)) {
return;
}
if (hum->HasBuffEffect(kBET_Jump)) {
return;
}
if (hum->IsCar() && !hum->AsCar()->HasPassenter()) {
return;
}
if (target) {
if (myself->GetPos().ManhattanDistance(target->GetPos()) >
myself->GetPos().ManhattanDistance(hum->GetPos())) {
target = hum;
}
} else {
target = hum;
}
});
if (target) {
node_->nearest_human.Attach(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;
attack_range = std::max(ai_meta->i->attack_range(), 1);
return attack_range;
}
float HeroAI::GetShotRange()
{
float shot_range = 0;
Hero* myself = (Hero*)owner;
if (myself->GetCurrWeapon() && myself->GetCurrWeapon()->meta) {
shot_range = myself->GetCurrWeapon()->meta->i->range();
shot_range += myself->GetCurrWeapon()->meta->i->bullet_rad();
}
shot_range = std::max(shot_range - 1, 1.0f);
return shot_range;
}
void HeroAI::DoShotAI()
{
Hero* myself = (Hero*)owner;
if (!node_->target.Get()) {
return;
}
if (!myself->GetCurrWeapon()) {
return;
}
bool shot_ok = false;
a8::Vec2 shot_dir = myself->GetAttackDir();
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);
shot_dir.Normalize();
myself->SetAttackDir(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->GetAttackDir();
myself->SetAttackDir(shot_dir);
myself->Shot(shot_dir, shot_ok, DEFAULT_FLY_DISTANCE);
myself->SetAttackDir(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 (myself->GetCurrWeapon()) {
return std::min(ai_meta->i->attack_times(), myself->GetCurrWeapon()->GetClipVolume());
} else {
return ai_meta->i->attack_times();
}
}
void HeroAI::UpdateSweepMine()
{
Hero* myself = (Hero*)owner;
if (!node_->target_obstacle.Get() || node_->target_obstacle.Get()->IsDead(myself->room)) {
ChangeToStateAI(HSE_Thinking);
return;
}
if (node_->exec_frame_num > SERVER_FRAME_RATE * 8) {
ChangeToStateAI(HSE_Thinking);
return;
}
if (myself->HasBuffEffect(kBET_Vertigo)) {
return;
}
if (node_->exec_frame_num % SERVER_FRAME_RATE == 0) {
a8::Vec2 move_dir = node_->target_obstacle.Get()->GetPos() - myself->GetPos();
move_dir.Normalize();
myself->SetMoveDir(move_dir);
myself->SetAttackDir(myself->GetMoveDir());
}
float distance = myself->GetPos().Distance(node_->target_obstacle.Get()->GetPos());
if (distance < myself->GetSpeed() + 2) {
myself->_UpdateMove(distance);
distance = myself->GetPos().Distance(node_->target_obstacle.Get()->GetPos());
}
if (!node_->target_obstacle.Get() || node_->target_obstacle.Get()->IsDead(myself->room)) {
ChangeToStateAI(HSE_Thinking);
} else {
if (myself->GetCollisionTimes() > node_->last_collision_times) {
ChangeToStateAI(HSE_Thinking);
} else {
if (distance < 3) {
ChangeToStateAI(HSE_Idle);
}
}
}
}
RoomObstacle* HeroAI::FindObstacleTarget()
{
Hero* myself = (Hero*)owner;
RoomObstacle* target = nullptr;
if (!myself->master.Get()) {
return nullptr;
}
for (auto& pair : myself->room->mine_objects) {
if (pair.second.Get() &&
!pair.second.Get()->sweep_lock &&
!pair.second.Get()->IsDead(myself->room) &&
(pair.second.Get()->meta->sweep_tags & ai_meta->bits_param2) != 0) {
if (pair.second.Get()->GetTeamId(myself->room) == myself->master.Get()->team_id) {
continue;
}
if (target) {
if (myself->GetPos().ManhattanDistance(target->GetPos()) >
myself->GetPos().ManhattanDistance(pair.second.Get()->GetPos())) {
target = pair.second.Get();
}
} else {
target = pair.second.Get();
}
}
}
if (target && target->GetPos().Distance(myself->GetPos()) > ai_meta->int_param1) {
target = nullptr;
}
if (target) {
target->sweep_lock = true;
myself->room->xtimer.AddDeadLineTimerAndAttach
(
SERVER_FRAME_RATE * 2,
a8::XParams(),
[] (const a8::XParams& param)
{
RoomObstacle* obstacle = (RoomObstacle*)param.sender.GetUserData();
obstacle->sweep_lock = false;
},
&target->xtimer_attacher.timer_list_);
}
return target;
}
void HeroAI::ForceRandomWalk(int time, int idle_time)
{
Hero* myself = (Hero*)owner;
ChangeToStateAI(HSE_RandomWalk);
node_->param1 = time / FRAME_RATE_MS;
node_->next_random_move_frameno = myself->room->GetFrameNo() +
idle_time / FRAME_RATE_MS;
}