#include "precompile.h" #include #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 (hero->poisoning) { hero->poisoning_time += delta_time; } if (hero->poisoning) { hero->UpdatePoisoning(); } 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() { 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 { 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; } float distance = myself->GetPos().Distance(node_.target.Get()->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.Get()->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->GetMoveDir().x) > FLT_EPSILON || std::abs(hero->GetMoveDir().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.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->attack_dir = 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->attack_dir = hero->GetMoveDir(); } } break; } node_.main_state = to_state; node_.frameno = hero->room->GetFrameNo(); node_.exec_frame_num = 0; } Creature* HeroAI::GetTarget() { if (GetAiLevel() <= 1) { return nullptr; } Hero* myself = (Hero*)owner; if (myself->room->GetGasData().gas_mode == GasInactive) { return nullptr; } Creature* target = nullptr; myself->TouchProperTargets ( [myself, &target] (Creature* hum, bool& stop) { 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; Hero* myself = (Hero*)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 HeroAI::DoShotAI() { Hero* myself = (Hero*)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 (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 HeroAI::GetAttackTimes() { Hero* myself = (Hero*)owner; if (myself->curr_weapon) { return std::min(ai_meta->i->attack_times(), myself->curr_weapon->GetClipVolume()); } else { return ai_meta->i->attack_times(); } }