From 714342d4ce0d7e25f83c0c1794e485bac836408e Mon Sep 17 00:00:00 2001 From: aozhiwei Date: Mon, 20 Jul 2020 11:00:38 +0800 Subject: [PATCH] add zombie.ai.* --- server/gameserver/android.ai.cc | 1 - server/gameserver/zombie.ai.cc | 435 ++++++++++++++++++++++++++++++++ server/gameserver/zombie.ai.h | 67 +++++ 3 files changed, 502 insertions(+), 1 deletion(-) create mode 100644 server/gameserver/zombie.ai.cc create mode 100644 server/gameserver/zombie.ai.h diff --git a/server/gameserver/android.ai.cc b/server/gameserver/android.ai.cc index 7aa6d03..65b4a7f 100644 --- a/server/gameserver/android.ai.cc +++ b/server/gameserver/android.ai.cc @@ -86,7 +86,6 @@ float AndroidNewAI::GetAttackRate() void AndroidNewAI::DefaultAi() { - Human* hum = (Human*)owner; switch (old_ai_data_.state) { case AS_thinking: { diff --git a/server/gameserver/zombie.ai.cc b/server/gameserver/zombie.ai.cc new file mode 100644 index 0000000..1ef4fc5 --- /dev/null +++ b/server/gameserver/zombie.ai.cc @@ -0,0 +1,435 @@ +#include "precompile.h" + +#include + +#include "zombie.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: 跑动射击 +*/ + +ZombieAI::~ZombieAI() +{ +} + +void ZombieAI::Update(int 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) { + return; + } + UpdateAI(); +} + +float ZombieAI::GetAttackRate() +{ + if (!ai_meta) { + return 1; + } else { + return ai_meta->i->attack_rate(); + } +} + +void ZombieAI::UpdateAI() +{ + 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 ZSE_Idle: + { + UpdateIdle(); + } + break; + case ZSE_Thinking: + { + UpdateThinking(); + } + break; + case ZSE_Attack: + { + UpdateAttack(); + } + break; + case ZSE_RandomWalk: + { + UpdateRandomWalk(); + } + break; + case ZSE_Pursuit: + { + UpdatePursuit(); + } + break; + default: + { + abort(); + } + break; + } + if (moving_) { + DoMove(); + } +} + +void ZombieAI::UpdateIdle() +{ + Human* hum = (Human*)owner; + if (hum->room->GetFrameNo() > node_.frameno + node_.param1) { + ChangeToState(ZSE_Thinking); + } +} + +void ZombieAI::UpdateThinking() +{ + Human* hum = (Human*)owner; + if (hum->room->GetGasData().gas_mode == GasInactive || + hum->room->IsWaitingStart()) { + if (hum->room->IsWaitingStart()) { + ChangeToState(ZSE_Idle); + } else { + ChangeToState(ZSE_RandomWalk); + } + } else { + Human* target = GetTarget(); + if (target) { + node_.target = target; + ChangeToState(ZSE_Attack); + } else { + if (hum->room->GetFrameNo() >= node_.next_random_move_frameno) { + if ((rand() % 7) < 4) { + ChangeToState(ZSE_Idle); + } else { + ChangeToState(ZSE_RandomWalk); + } + } else { + ChangeToState(ZSE_Idle); + node_.param1 = node_.next_random_move_frameno - hum->room->GetFrameNo(); + } + } + } +} + +void ZombieAI::UpdateAttack() +{ + Human* myself = (Human*)owner; + if (!node_.target || node_.target->dead) { + ChangeToState(ZSE_Thinking); + return; + } + if (node_.exec_frame_num > SERVER_FRAME_RATE * 8) { + ChangeToState(ZSE_Thinking); + return; + } + float distance = myself->GetPos().Distance(node_.target->GetPos()); + if (distance > GetAttackRange()) { + if (ai_meta->i->pursuit_radius() <= 0) { + //站桩 + ChangeToState(ZSE_Thinking); + } else { + if (distance < ai_meta->i->pursuit_radius()) { + //追击 + ChangeToState(ZSE_Pursuit); + } else { + ChangeToState(ZSE_Thinking); + } + } + return; + } + //攻击逻辑 + switch (ai_meta->i->attack_type()) { + case kShotClick: + { + if (ai_meta->i->attack_interval() > 0) { + if (node_.shot_times < GetAttackTimes()) { + DoShot(); + } else { + ChangeToState(ZSE_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; + DoShot(); + } + } + break; + case kShotHold: + { + myself->shot_hold = true; + DoShot(); + } + break; + default: + { + ChangeToState(ZSE_Idle); + } + break; + } +} + +void ZombieAI::UpdateRandomWalk() +{ + Human* hum = (Human*)owner; + if (hum->room->GetFrameNo() > node_.frameno + node_.param1) { + ChangeToState(ZSE_Thinking); + } +} + +void ZombieAI::UpdatePursuit() +{ + Human* myself = (Human*)owner; + float distance = myself->GetPos().Distance(node_.target->GetPos()); + if (distance < GetAttackRange()) { + ChangeToState(ZSE_Attack); + } else { + if (node_.exec_frame_num > 100 * 2) { + ChangeToState(ZSE_RandomWalk); + } + } +} + +void ZombieAI::DoMove() +{ + 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] () { + ChangeToState(ZSE_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 != ZSE_Pursuit && + hum->GetPos().ManhattanDistance(node_.nearest_human->GetPos()) < 200) { + ChangeToState(ZSE_Thinking); + } else if (hum->GetPos().ManhattanDistance(node_.nearest_human->GetPos()) > 800) { + GetTarget(); + } + } + } +} + +void ZombieAI::ChangeToState(ZombieState_e to_state) +{ + Human* hum = (Human*)owner; + switch (to_state) { + case ZSE_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 ZSE_Thinking: + { + node_.target = nullptr; + node_.param1 = 0; + node_.start_shot_frameno = 0; + node_.shot_times = 0; + moving_ = false; + } + break; + case ZSE_Attack: + { + node_.param1 = 0; + node_.start_shot_frameno = 0; + node_.shot_times = 0; + moving_ = false; + node_.shot_times = 0; + } + break; + case ZSE_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 ZSE_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* ZombieAI::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 ZombieAI::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 ZombieAI::DoShot() +{ + 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 ZombieAI::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(); + } +} diff --git a/server/gameserver/zombie.ai.h b/server/gameserver/zombie.ai.h new file mode 100644 index 0000000..4df64fe --- /dev/null +++ b/server/gameserver/zombie.ai.h @@ -0,0 +1,67 @@ +#pragma once + +#include "aicomponent.h" + +enum ZombieState_e +{ + ZSE_Idle = 0, + ZSE_Thinking = 1, + ZSE_Attack = 2, + ZSE_RandomWalk = 3, + ZSE_Pursuit = 4 +}; + +class Human; +class ZombieAINode +{ +public: + ZombieState_e main_state = ZSE_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; + Human* target = nullptr; + Human* nearest_human = nullptr; + long long last_check_nearest_human_frameno = 0; + a8::Vec2 shot_dir; +}; + +namespace MetaData +{ + class AI; +} + +class Human; +class ZombieAI : public AIComponent +{ +public: + + virtual ~ZombieAI() override; + virtual void Update(int delta_time) override; + float GetAttackRate(); + +private: + void UpdateAI(); + void UpdateIdle(); + void UpdateThinking(); + void UpdateAttack(); + void UpdateRandomWalk(); + void UpdatePursuit(); + void DoMove(); + void ChangeToState(ZombieState_e to_state); + void DoShot(); + + Human* GetTarget(); + float GetAttackRange(); + int GetAttackTimes(); + +private: + MetaData::AI* ai_meta = nullptr; + ZombieAINode node_; + bool moving_ = false; +};