game2004/server/gameserver/android.ai.cc
2020-07-20 11:00:38 +08:00

777 lines
24 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"
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: 跑动射击
*/
AndroidNewAI::~AndroidNewAI()
{
}
void AndroidNewAI::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 1
{
if (a8::HasBitFlag(hum->status, HS_NewBieGuideAndroid)) {
UpdateNewBieNpc();
return;
}
if (a8::HasBitFlag(hum->status, HS_LastAndroid)) {
UpdateLastNpc();
return;
}
if ((hum->room->GetRoomType() == RT_NewBrid ||
hum->room->GetRoomType() == RT_MidBrid) &&
hum->room->GetGasData().gas_mode != GasInactive &&
hum->team_uuid.empty()
) {
UpdateNewBieRoomLogic();
return;
}
}
#endif
if (hum->room->GetGasData().gas_mode == GasInactive) {
DefaultAi();
return;
}
UpdateNewAI();
}
float AndroidNewAI::GetAttackRate()
{
if (!ai_meta) {
return 1;
} else {
return ai_meta->i->attack_rate();
}
}
void AndroidNewAI::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) ||
(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 AndroidNewAI::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;
hum->move_dir = a8::Vec2(1.0f, 0);
hum->move_dir.Rotate(a8::RandAngle());
hum->move_dir.Normalize();
hum->attack_dir = hum->move_dir;
}
break;
default:
break;
}
}
void AndroidNewAI::DoMoveOldAI()
{
Human* hum = (Human*)owner;
if (hum->room->IsWaitingStart()) {
return;
}
if (owner->UpdatedTimes() % 2 == 0) {
Human* hum = (Human*)owner;
int speed = std::max(1, (int)hum->GetSpeed());
if (a8::HasBitFlag(hum->status, HS_NewBieGuideAndroid) &&
hum->room->GetFrameNo() - hum->enable_frameno < SERVER_FRAME_RATE * 8) {
hum->move_dir = hum->room->GetFirstNewBie()->GetPos() - hum->GetPos();
hum->move_dir.Normalize();
}
for (int i = 0; i < speed; ++i) {
a8::Vec2 old_pos = hum->GetPos();
hum->SetPos(hum->GetPos() + hum->move_dir);
if (hum->IsCollisionInMapService()) {
hum->SetPos(old_pos);
if (i == 0) {
hum->FindPathInMapService();
}
break;
}
hum->room->grid_service->MoveHuman(hum);
}
}
}
void AndroidNewAI::DoAttackOldAI()
{
Human* hum = (Human*)owner;
if (hum->room->IsWaitingStart()) {
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->attack_dir = shot_dir;
bool shot_ok = false;
sender->Shot(shot_dir, shot_ok);
}
}
old_ai_data_.last_target = enemy;
}
}
void AndroidNewAI::UpdateNewBieNpc()
{
Human* hum = (Human*)owner;
if (hum->room->GetFrameNo() - hum->enable_frameno < 2) {
if (hum->GetPos().ManhattanDistance(hum->room->GetFirstNewBie()->GetPos()) > 100) {
hum->move_dir = hum->room->GetFirstNewBie()->GetPos() - hum->GetPos();
hum->move_dir.Normalize();
}
hum->attack_dir = hum->move_dir;
if (hum->curr_weapon->weapon_idx != 0) {
hum->curr_weapon->ammo = MetaMgr::Instance()->newbie_first_robot_ammo;
}
} else if (hum->room->GetFrameNo() - hum->enable_frameno < SERVER_FRAME_RATE * 1.5) {
int speed = std::max(1, (int)hum->GetSpeed());
for (int i = 0; i < speed; ++i) {
hum->SetPos(hum->GetPos() + hum->move_dir);
hum->room->grid_service->MoveHuman(hum);
}
} else if (hum->room->GetFrameNo() - hum->enable_frameno < SERVER_FRAME_RATE * 3) {
Human* enemy = hum->room->GetFirstNewBie();
Human* sender = hum;
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->attack_dir = shot_dir;
bool shot_ok = false;
sender->Shot(shot_dir, shot_ok);
}
} else {
a8::UnSetBitFlag(hum->status, HS_NewBieGuideAndroid);
}
}
void AndroidNewAI::UpdateLastNpc()
{
Human* hum = (Human*)owner;
if (hum->room->GetFrameNo() - hum->enable_frameno < 2) {
if (hum->GetPos().ManhattanDistance(hum->last_human_target->GetPos()) > 100) {
hum->move_dir = hum->last_human_target->GetPos() - hum->GetPos();
hum->move_dir.Normalize();
}
hum->attack_dir = hum->move_dir;
if (hum->curr_weapon->weapon_idx != 0) {
hum->curr_weapon->ammo = MetaMgr::Instance()->newbie_first_robot_ammo * 2;
}
} else if (hum->room->GetFrameNo() - hum->enable_frameno < SERVER_FRAME_RATE * 1.5) {
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->move_dir);
if (!hum->room->OverBorder(hum->GetPos(), hum->meta->i->radius())) {
hum->room->grid_service->MoveHuman(hum);
} else {
hum->SetPos(old_pos);
break;
}
}
} else if (hum->room->GetFrameNo() - hum->enable_frameno < SERVER_FRAME_RATE * 3) {
Human* enemy = hum->last_human_target;
Human* sender = hum;
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->attack_dir = shot_dir;
bool shot_ok = false;
sender->Shot(shot_dir, shot_ok);
}
} else {
a8::UnSetBitFlag(hum->status, HS_LastAndroid);
}
}
void AndroidNewAI::UpdateNewBieRoomLogic()
{
Human* hum = (Human*)owner;
Human* old_last_target = old_ai_data_.last_target;
Human* target = old_ai_data_.last_target;
if ((old_ai_data_.last_target && old_ai_data_.last_target->real_dead) || !old_ai_data_.last_target) {
if (hum->last_human_target && !hum->last_human_target->dead) {
target = hum->last_human_target;
} else {
if (rand() % 100 < 70) {
hum->room->TouchPlayerList
(
a8::XParams(),
[hum, &target] (Player* player, a8::XParams& param)
{
if (!player->dead && hum->team_id != player->team_id) {
if (!target) {
target = player;
} else {
if (hum->GetPos().ManhattanDistance(player->GetPos()) <
hum->GetPos().ManhattanDistance(target->GetPos())) {
target = player;
}
}
}
});
} else {
hum->room->TouchHumanList
(
a8::XParams(),
[hum, &target] (Human* huma, a8::XParams& param)
{
if (!huma->dead &&
huma->IsAndroid() &&
hum->team_id != huma->team_id &&
!a8::HasBitFlag(huma->status, HS_Disable)) {
if (!target) {
target = huma;
} else {
if (hum->GetPos().ManhattanDistance(huma->GetPos()) <
hum->GetPos().ManhattanDistance(target->GetPos())) {
target = huma;
}
}
}
return true;
});
}
}
old_ai_data_.last_target = target;
}
if (!target) {
return;
}
if (target->IsPlayer()) {
if (hum->room->AliveCount() < 15) {
if (hum->GetPos().ManhattanDistance(target->GetPos()) > 1000) {
a8::Vec2 pos = target->GetPos();
a8::Vec2 dir = target->move_dir;
dir = a8::Vec2::UP;
if (rand() % 100 < 1) {
dir.Rotate(a8::RandAngle() / 2.0f);
} else {
dir.Rotate(a8::RandAngle());
}
pos = pos + dir * SHUA_RANGE;
if (hum->room->OverBorder(pos, hum->GetRadius())) {
pos.x = target->GetPos().x;
if (hum->room->OverBorder(pos, hum->GetRadius())) {
pos = target->GetPos();
}
}
hum->SetPos(pos);
hum->room->grid_service->MoveHuman(hum);
hum->FindLocation();
hum->RefreshView();
}
}
}
if (hum->GetPos().ManhattanDistance(target->GetPos()) > 180) {
if (hum->GetPos().ManhattanDistance(target->GetPos()) > 650 &&
hum->room->GetFrameNo() - old_ai_data_.last_findenemy_frameno > SERVER_FRAME_RATE * 3) {
old_ai_data_.last_findenemy_frameno = hum->room->GetFrameNo();
old_ai_data_.last_target = nullptr;
} else {
int speed = std::max(1, (int)hum->GetSpeed());
hum->move_dir = target->GetPos() - hum->GetPos();
hum->move_dir.Normalize();
hum->attack_dir = hum->move_dir;
speed *= 0.7;
for (int i = 0; i < speed; ++i) {
hum->SetPos(hum->GetPos() + hum->move_dir);
hum->room->grid_service->MoveHuman(hum);
}
}
} else {
if (hum->room->GetFrameNo() - old_ai_data_.last_attack_frameno > SERVER_FRAME_RATE + (rand() % 15)) {
old_ai_data_.last_attack_frameno = hum->room->GetFrameNo();
Human* sender = (Human*)owner;
a8::Vec2 shot_dir = target->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->attack_dir = shot_dir;
bool shot_ok = false;
sender->Shot(shot_dir, shot_ok);
}
}
if (old_last_target && old_ai_data_.last_target && old_last_target == old_ai_data_.last_target) {
++old_ai_data_.series_attack_frames;
} else {
old_ai_data_.series_attack_frames = 0;
}
if (old_ai_data_.series_attack_frames > SERVER_FRAME_RATE * 10 &&
old_ai_data_.last_target && old_ai_data_.last_target->IsAndroid()) {
old_ai_data_.last_target = nullptr;
}
}
}
void AndroidNewAI::UpdateNewAI()
{
Human* hum = (Human*)owner;
if (a8::HasBitFlag(hum->status, HS_Disable)) {
return;
}
if (!ai_meta && GetAiLevel() != 0) {
ai_meta = MetaMgr::Instance()->GetAI(GetAiLevel());
if (!ai_meta) {
abort();
}
}
++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 AndroidNewAI::UpdateIdle()
{
Human* hum = (Human*)owner;
if (hum->room->GetFrameNo() > node_.frameno + node_.param1) {
ChangeToStateNewAI(ASE_Thinking);
}
}
void AndroidNewAI::UpdateThinking()
{
Human* hum = (Human*)owner;
if (hum->room->GetGasData().gas_mode == GasInactive ||
hum->room->IsWaitingStart()) {
if (hum->room->IsWaitingStart()) {
ChangeToStateNewAI(ASE_Idle);
} else {
ChangeToStateNewAI(ASE_RandomWalk);
}
} else {
Human* target = GetTarget();
if (target) {
node_.target = 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 AndroidNewAI::UpdateAttack()
{
Human* myself = (Human*)owner;
if (!node_.target || node_.target->dead) {
ChangeToStateNewAI(ASE_Thinking);
return;
}
if (node_.exec_frame_num > SERVER_FRAME_RATE * 8) {
ChangeToStateNewAI(ASE_Thinking);
return;
}
float distance = myself->GetPos().Distance(node_.target->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 AndroidNewAI::UpdateRandomWalk()
{
Human* hum = (Human*)owner;
if (hum->room->GetFrameNo() > node_.frameno + node_.param1) {
ChangeToStateNewAI(ASE_Thinking);
}
}
void AndroidNewAI::UpdatePursuit()
{
Human* myself = (Human*)owner;
float distance = myself->GetPos().Distance(node_.target->GetPos());
if (distance < GetAttackRange()) {
ChangeToStateNewAI(ASE_Attack);
} else {
if (node_.exec_frame_num > 100 * 2) {
ChangeToStateNewAI(ASE_RandomWalk);
}
}
}
void AndroidNewAI::DoMoveNewAI()
{
Human* hum = (Human*)owner;
if (std::abs(hum->move_dir.x) > FLT_EPSILON ||
std::abs(hum->move_dir.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) {
if (node_.main_state != ASE_Pursuit &&
hum->GetPos().ManhattanDistance(node_.nearest_human->GetPos()) < 200) {
ChangeToStateNewAI(ASE_Thinking);
} else if (hum->GetPos().ManhattanDistance(node_.nearest_human->GetPos()) > 800) {
GetTarget();
}
}
}
}
void AndroidNewAI::ChangeToStateNewAI(AndroidStateEx_e to_state)
{
Human* hum = (Human*)owner;
switch (to_state) {
case ASE_Idle:
{
node_.target = nullptr;
node_.param1 = 0;
node_.start_shot_frameno = 0;
node_.shot_times = 0;
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 ASE_Thinking:
{
node_.target = nullptr;
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 = 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 = hum->room->GetFrameNo() +
SERVER_FRAME_RATE * 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) {
moving_ = false;
}
}
break;
case ASE_Pursuit:
{
moving_ = true;
if (node_.target) {
hum->move_dir = node_.target->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;
}
Human* AndroidNewAI::GetTarget()
{
if (GetAiLevel() <= 1) {
return nullptr;
}
Human* myself = (Human*)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 AndroidNewAI::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();
}
attack_range = std::min(ai_meta->i->attack_range(), (int)attack_range);
return attack_range;
}
void AndroidNewAI::DoShotNewAI()
{
Human* myself = (Human*)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;
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;
}
}
}
int AndroidNewAI::GetAttackTimes()
{
Human* myself = (Human*)owner;
if (myself->curr_weapon) {
return std::min(ai_meta->i->attack_times(), myself->curr_weapon->GetClipVolume());
} else {
return ai_meta->i->attack_times();
}
}