2024-04-28 13:55:19 +00:00
|
|
|
|
using ColdMint.scripts.camp;
|
|
|
|
|
using ColdMint.scripts.character;
|
|
|
|
|
|
|
|
|
|
namespace ColdMint.scripts.behaviorTree.ai;
|
|
|
|
|
|
2024-06-06 13:05:51 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// <para>AI attack node</para>
|
|
|
|
|
/// <para>AI的攻击节点</para>
|
|
|
|
|
/// </summary>
|
2024-05-08 10:22:04 +00:00
|
|
|
|
public class AiAttackNode : BehaviorTreeNodeTemplate
|
2024-04-28 13:55:19 +00:00
|
|
|
|
{
|
2024-05-08 10:22:04 +00:00
|
|
|
|
public AiCharacter? Character { get; set; }
|
2024-04-28 13:55:19 +00:00
|
|
|
|
|
|
|
|
|
public override int Execute(bool isPhysicsProcess, double delta)
|
|
|
|
|
{
|
|
|
|
|
if (Character == null)
|
|
|
|
|
{
|
|
|
|
|
return Config.BehaviorTreeResult.Failure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var nodesInTheAttackRange = Character.NodesInTheAttackRange;
|
|
|
|
|
if (nodesInTheAttackRange.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
//No nodes are in range of the attack
|
|
|
|
|
//没有节点在攻击范围内
|
|
|
|
|
return Config.BehaviorTreeResult.Failure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Save the nearest enemy
|
|
|
|
|
//保存最近的敌人
|
2024-05-08 10:22:04 +00:00
|
|
|
|
CharacterTemplate? closestEnemy = null;
|
2024-04-28 13:55:19 +00:00
|
|
|
|
var closestDistance = float.MaxValue;
|
|
|
|
|
var selfCamp = CampManager.GetCamp(Character.CampId);
|
|
|
|
|
foreach (var node in nodesInTheAttackRange)
|
|
|
|
|
{
|
2024-06-06 13:05:51 +00:00
|
|
|
|
if (node is not CharacterTemplate characterTemplate)
|
2024-04-28 13:55:19 +00:00
|
|
|
|
{
|
2024-06-06 13:05:51 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node == Character)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-04-28 13:55:19 +00:00
|
|
|
|
|
2024-06-06 13:05:51 +00:00
|
|
|
|
var characterCamp = CampManager.GetCamp(characterTemplate.CampId);
|
|
|
|
|
var canCause = CampManager.CanCauseHarm(selfCamp, characterCamp);
|
|
|
|
|
if (!canCause)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-05-08 10:22:04 +00:00
|
|
|
|
|
2024-06-06 13:05:51 +00:00
|
|
|
|
if (selfCamp == null || characterCamp == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selfCamp.Id == characterCamp.Id)
|
|
|
|
|
{
|
|
|
|
|
//If it is the same side, do not attack, if allowed friend damage, this code will prevent the AI from actively attacking the player.
|
|
|
|
|
//如果是同一阵营,不攻击,如果允许友伤,这段代码会阻止AI主动攻击玩家。
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-04-28 13:55:19 +00:00
|
|
|
|
|
2024-06-06 13:05:51 +00:00
|
|
|
|
var distance = characterTemplate.GlobalPosition.DistanceTo(Character.GlobalPosition);
|
|
|
|
|
if (distance < closestDistance)
|
|
|
|
|
{
|
|
|
|
|
closestDistance = distance;
|
|
|
|
|
closestEnemy = characterTemplate;
|
2024-04-28 13:55:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 10:22:04 +00:00
|
|
|
|
if (closestEnemy != null && Character.AttackObstacleDetection != null)
|
2024-04-28 13:55:19 +00:00
|
|
|
|
{
|
2024-06-06 13:05:51 +00:00
|
|
|
|
//With the nearest enemy and no obstacles
|
|
|
|
|
//有距离最近的敌人,且没有障碍物
|
|
|
|
|
var distanceVector2 = closestEnemy.GlobalPosition - Character.GlobalPosition;
|
|
|
|
|
Character.AttackObstacleDetection.TargetPosition = distanceVector2;
|
2024-04-28 13:55:19 +00:00
|
|
|
|
if (Character.AttackObstacleDetection.GetCollider() == null)
|
|
|
|
|
{
|
|
|
|
|
Character.StopMoving();
|
|
|
|
|
Character.AimTheCurrentItemAtAPoint(closestEnemy.GlobalPosition);
|
|
|
|
|
Character.UseItem(closestEnemy.GlobalPosition);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Config.BehaviorTreeResult.Success;
|
|
|
|
|
}
|
|
|
|
|
}
|