game2005/server/gameserver/android.ai.cc
aozhiwei ff6d93dfa3 1
2021-06-18 15:01:02 +08:00

634 lines
17 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 "android.ai.h"
#include "android.h"
#include "room.h"
#include "metamgr.h"
#include "player.h"
#include "app.h"
const int SHUA_RANGE = 580;
enum AndroidState_e : int
{
AS_thinking,
AS_moving,
AS_attack
};
enum AndroidStateEx_e : int
{
ASE_Idle = 0,
ASE_Thinking = 1,
ASE_Attack = 2,
ASE_RandomWalk = 3,
ASE_Pursuit = 4
};
class Human;
class AINode
{
public:
AndroidStateEx_e main_state = ASE_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;
};
struct OldAiData
{
AndroidState_e state = AS_thinking;
int state_elapsed_time = 0;
CreatureWeakPtr last_target;
long long last_attack_frameno = 0;
long long last_findenemy_frameno = 0;
long long series_attack_frames = 0;
};
/*
nn目标ai可切换降级/升级(根据系统当前负载)
ai级别
1: 木桩(不射击)
2: 站桩间隔开枪
3: 站桩描边射击
4: 站桩扫射(连续射击)
5: 跑动间隔开枪
6: 跑动描边射击
7: 跑动扫射
8: 跑动射击
*/
AndroidAI::AndroidAI()
{
old_ai_data_ = new OldAiData();
node_ = new AINode();
}
AndroidAI::~AndroidAI()
{
A8_SAFE_DELETE(old_ai_data_);
A8_SAFE_DELETE(node_);
}
void AndroidAI::Update(int delta_time)
{
old_ai_data_->state_elapsed_time += delta_time;
Human* hum = (Human*)owner;
if (hum->poisoning) {
hum->poisoning_time += delta_time;
}
if (hum->poisoning) {
hum->UpdatePoisoning();
}
if (hum->dead) {
return;
}
if (hum->room->GetGasData().gas_mode == GasInactive) {
DefaultAi();
return;
}
UpdateNewAI();
}
float AndroidAI::GetAttackRate()
{
if (!ai_meta) {
return 1;
} else {
return ai_meta->i->attack_rate();
}
}
void AndroidAI::DefaultAi()
{
switch (old_ai_data_->state) {
case AS_thinking:
{
if (old_ai_data_->state_elapsed_time > 1500 + rand() % 3000) {
int rnd = rand();
if (rnd % 100 < 30) {
ChangeToStateOldAI(AS_moving);
} else if (rnd % 100 < 50) {
ChangeToStateOldAI(AS_attack);
}
}
}
break;
case AS_moving:
{
if (old_ai_data_->state_elapsed_time < 1000 + rand() % 2000) {
DoMoveOldAI();
} else {
int rnd = rand();
if (rnd % 100 < 30) {
ChangeToStateOldAI(AS_thinking);
} else if (rnd % 100 < 50) {
ChangeToStateOldAI(AS_attack);
}
}
}
break;
case AS_attack:
{
if ((old_ai_data_->state_elapsed_time < 3000 && old_ai_data_->last_target.Get()) ||
(old_ai_data_->state_elapsed_time < 1100)) {
DoAttackOldAI();
} else {
int rnd = rand();
if (rnd % 100 < 30) {
ChangeToStateOldAI(AS_moving);
} else if (rnd % 100 < 50) {
ChangeToStateOldAI(AS_thinking);
}
}
}
break;
}
}
void AndroidAI::ChangeToStateOldAI(AndroidState_e to_state)
{
old_ai_data_->state = to_state;
old_ai_data_->state_elapsed_time = 0;
switch (old_ai_data_->state) {
case AS_moving:
{
Human* hum = (Human*)owner;
a8::Vec2 move_dir = a8::Vec2(1.0f, 0);
move_dir.Rotate(a8::RandAngle());
move_dir.Normalize();
hum->SetMoveDir(move_dir);
hum->SetAttackDir(hum->GetMoveDir());
}
break;
default:
break;
}
}
void AndroidAI::DoMoveOldAI()
{
Human* hum = (Human*)owner;
if (hum->room->IsWaitingStart()) {
return;
}
#if 0
if (hum->HasBuffEffect(kBET_Jump)) {
return;
}
#endif
if (owner->UpdatedTimes() % 2 == 0) {
Human* hum = (Human*)owner;
int speed = std::max(1, (int)hum->GetSpeed());
for (int i = 0; i < speed; ++i) {
a8::Vec2 old_pos = hum->GetPos();
hum->SetPos(hum->GetPos() + hum->GetMoveDir());
if (hum->IsCollisionInMapService()) {
hum->SetPos(old_pos);
if (i == 0) {
hum->FindPathInMapService();
}
break;
}
hum->room->grid_service->MoveCreature(hum);
}
}
}
void AndroidAI::DoAttackOldAI()
{
Human* hum = (Human*)owner;
if (hum->room->IsWaitingStart()) {
return;
}
if (hum->HasBuffEffect(kBET_Jump)) {
return;
}
if (hum->room->GetGasData().gas_mode == GasInactive) {
return;
}
if (owner->UpdatedTimes() % 10 == 0) {
Human* enemy = owner->room->FindEnemy((Human*)owner);
if (enemy) {
Human* sender = (Human*)owner;
a8::Vec2 shot_dir = enemy->GetPos() - sender->GetPos();
if (std::abs(shot_dir.x) > FLT_EPSILON ||
std::abs(shot_dir.y) > FLT_EPSILON) {
shot_dir.Normalize();
shot_dir.Rotate((rand() % 10) / 180.0f);
sender->SetAttackDir(shot_dir);
bool shot_ok = false;
sender->Shot(shot_dir, shot_ok, DEFAULT_FLY_DISTANCE);
}
old_ai_data_->last_target.Attach(enemy);
}
}
}
void AndroidAI::UpdateNewAI()
{
Human* hum = (Human*)owner;
if (a8::HasBitFlag(hum->status, CS_Disable)) {
return;
}
if (!ai_meta && GetAiLevel() != 0) {
ai_meta = MetaMgr::Instance()->GetAI(GetAiLevel(), 0);
if (!ai_meta) {
abort();
}
}
if (hum->playing_skill) {
hum->UpdateSkill();
}
++node_->exec_frame_num;
hum->shot_hold = false;
switch (node_->main_state) {
case ASE_Idle:
{
UpdateIdle();
}
break;
case ASE_Thinking:
{
UpdateThinking();
}
break;
case ASE_Attack:
{
UpdateAttack();
}
break;
case ASE_RandomWalk:
{
UpdateRandomWalk();
}
break;
case ASE_Pursuit:
{
UpdatePursuit();
}
break;
default:
{
abort();
}
break;
}
if (moving_) {
DoMoveNewAI();
}
}
void AndroidAI::UpdateIdle()
{
Human* hum = (Human*)owner;
if (hum->room->GetFrameNo() > node_->frameno + node_->param1) {
ChangeToStateNewAI(ASE_Thinking);
}
}
void AndroidAI::UpdateThinking()
{
Human* hum = (Human*)owner;
if (hum->room->GetGasData().gas_mode == GasInactive ||
hum->room->IsWaitingStart() ||
hum->HasBuffEffect(kBET_Jump) ||
a8::HasBitFlag(hum->status, CS_DisableAttack)) {
if (hum->room->IsWaitingStart()) {
ChangeToStateNewAI(ASE_Idle);
} else {
ChangeToStateNewAI(ASE_RandomWalk);
}
} else {
Creature* target = GetTarget();
if (target) {
node_->target.Attach(target);
ChangeToStateNewAI(ASE_Attack);
} else {
if (hum->room->GetFrameNo() >= node_->next_random_move_frameno) {
if ((rand() % 7) < 4) {
ChangeToStateNewAI(ASE_Idle);
} else {
ChangeToStateNewAI(ASE_RandomWalk);
}
} else {
ChangeToStateNewAI(ASE_Idle);
node_->param1 = node_->next_random_move_frameno - hum->room->GetFrameNo();
}
}
}
}
void AndroidAI::UpdateAttack()
{
Human* myself = (Human*)owner;
if (!node_->target.Get() || node_->target.Get()->dead) {
ChangeToStateNewAI(ASE_Thinking);
return;
}
if (node_->exec_frame_num > SERVER_FRAME_RATE * 8) {
ChangeToStateNewAI(ASE_Thinking);
return;
}
#ifdef DEBUG
if (App::Instance()->HasFlag(20)) {
ChangeToStateNewAI(ASE_Thinking);
return;
}
#endif
if (myself->HasBuffEffect(kBET_Vertigo)) {
return;
}
float distance = myself->GetPos().Distance(node_->target.Get()->GetPos());
if (distance > GetAttackRange()) {
if (ai_meta->i->pursuit_radius() <= 0) {
//站桩
ChangeToStateNewAI(ASE_Thinking);
} else {
if (distance < ai_meta->i->pursuit_radius()) {
//追击
ChangeToStateNewAI(ASE_Pursuit);
} else {
ChangeToStateNewAI(ASE_Thinking);
}
}
return;
}
//攻击逻辑
switch (ai_meta->i->attack_type()) {
case kShotClick:
{
if (ai_meta->i->attack_interval() > 0) {
if (node_->shot_times < GetAttackTimes()) {
DoShotNewAI();
} else {
ChangeToStateNewAI(ASE_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;
DoShotNewAI();
}
}
break;
case kShotHold:
{
myself->shot_hold = true;
DoShotNewAI();
}
break;
default:
{
ChangeToStateNewAI(ASE_Idle);
}
break;
}
}
void AndroidAI::UpdateRandomWalk()
{
Human* hum = (Human*)owner;
if (hum->room->GetFrameNo() > node_->frameno + node_->param1) {
ChangeToStateNewAI(ASE_Thinking);
}
}
void AndroidAI::UpdatePursuit()
{
Human* myself = (Human*)owner;
if (node_->target.Get()) {
float distance = myself->GetPos().Distance(node_->target.Get()->GetPos());
if (!myself->HasBuffEffect(kBET_Jump) &&
!a8::HasBitFlag(myself->status, CS_DisableAttack) &&
distance < GetAttackRange()) {
ChangeToStateNewAI(ASE_Attack);
} else {
if (node_->exec_frame_num > 100 * 2) {
ChangeToStateNewAI(ASE_RandomWalk);
}
}
} else {
if (node_->exec_frame_num > 100 * 2) {
ChangeToStateNewAI(ASE_RandomWalk);
}
}
}
void AndroidAI::DoMoveNewAI()
{
Human* hum = (Human*)owner;
if (hum->HasBuffEffect(kBET_Vertigo)) {
return;
}
if (std::abs(hum->GetMoveDir().x) > FLT_EPSILON ||
std::abs(hum->GetMoveDir().y) > FLT_EPSILON) {
hum->on_move_collision =
[this] () {
ChangeToStateNewAI(ASE_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 != ASE_Pursuit &&
hum->GetPos().ManhattanDistance(node_->nearest_human.Get()->GetPos()) < 200) {
ChangeToStateNewAI(ASE_Thinking);
} else if (hum->GetPos().ManhattanDistance(node_->nearest_human.Get()->GetPos()) > 800) {
GetTarget();
}
}
}
}
void AndroidAI::ChangeToStateNewAI(AndroidStateEx_e to_state)
{
Human* hum = (Human*)owner;
switch (to_state) {
case ASE_Idle:
{
node_->target.Reset();
node_->param1 = 0;
node_->start_shot_frameno = 0;
node_->shot_times = 0;
moving_ = false;
if (hum->room->GetGasData().gas_mode == GasInactive ||
hum->room->IsWaitingStart() ||
hum->HasBuffEffect(kBET_Jump)) {
node_->param1 = rand() % (3 * SERVER_FRAME_RATE);
} else {
node_->param1 = rand() % (2 * SERVER_FRAME_RATE);
}
}
break;
case ASE_Thinking:
{
node_->target.Reset();
node_->param1 = 0;
node_->start_shot_frameno = 0;
node_->shot_times = 0;
moving_ = false;
}
break;
case ASE_Attack:
{
node_->param1 = 0;
node_->start_shot_frameno = 0;
node_->shot_times = 0;
moving_ = false;
node_->shot_times = 0;
}
break;
case ASE_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 = hum->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();
hum->SetMoveDir(move_dir);
hum->SetAttackDir(hum->GetMoveDir());
if (node_->param1 <= 1) {
moving_ = false;
}
}
break;
case ASE_Pursuit:
{
moving_ = true;
if (node_->target.Get()) {
a8::Vec2 move_dir = node_->target.Get()->GetPos() - hum->GetPos();
move_dir.Normalize();
hum->SetMoveDir(move_dir);
hum->SetAttackDir(hum->GetMoveDir());
}
}
break;
}
node_->main_state = to_state;
node_->frameno = hum->room->GetFrameNo();
node_->exec_frame_num = 0;
}
Creature* AndroidAI::GetTarget()
{
if (GetAiLevel() <= 1) {
return nullptr;
}
Human* myself = (Human*)owner;
if (myself->room->GetGasData().gas_mode == GasInactive) {
return nullptr;
}
Creature* target = nullptr;
myself->TraverseProperTargets
(
[myself, &target] (Creature* hum, bool& stop)
{
if (target->HasBuffEffect(kBET_Camouflage)) {
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 AndroidAI::GetAttackRange()
{
float attack_range = 0;
Human* myself = (Human*)owner;
if (myself->GetCurrWeapon() && myself->GetCurrWeapon()->meta) {
attack_range = myself->GetCurrWeapon()->meta->i->range();
}
attack_range = std::min(ai_meta->i->attack_range(), (int)attack_range);
return attack_range;
}
void AndroidAI::DoShotNewAI()
{
Human* myself = (Human*)owner;
if (!node_->target.Get()) {
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);
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 AndroidAI::GetAttackTimes()
{
Human* myself = (Human*)owner;
if (myself->GetCurrWeapon()) {
return std::min(ai_meta->i->attack_times(), myself->GetCurrWeapon()->GetClipVolume());
} else {
return ai_meta->i->attack_times();
}
}