using System;
using System.Collections.Generic;
using ColdMint.scripts.bubble;
using ColdMint.scripts.camp;
using ColdMint.scripts.inventory;
using ColdMint.scripts.stateMachine;
using ColdMint.scripts.utils;
using ColdMint.scripts.weapon;
using Godot;
namespace ColdMint.scripts.character;
///
/// The role played by computers
/// 由电脑扮演的角色
///
public sealed partial class AiCharacter : CharacterTemplate
{
//Used to detect rays on walls
//用于检测墙壁的射线
private RayCast2D? _wallDetection;
private Vector2 _wallDetectionOrigin;
private Area2D? _attackArea;
///
/// Reconnaissance area
/// 侦察区域
///
///
///Most of the time, when the enemy enters the reconnaissance area, the character will issue a "question mark" and try to move slowly towards the event point.
///大多数情况下,当敌人进入侦察区域后,角色会发出“疑问(问号)”,并尝试向事件点缓慢移动。
///
private Area2D? _scoutArea;
///
/// All enemies within striking distance
/// 在攻击范围内的所有敌人
///
private List? _enemyInTheAttackRange;
///
/// Scout all enemies within range
/// 在侦察范围内所有的敌人
///
private List? _enemyInTheScoutRange;
///
/// Every weapon in the recon area
/// 在侦察范围内所有的武器
///
private List? _weaponInTheScoutRange;
///
/// Obstacle detection ray during attack
/// 攻击时的障碍物检测射线
///
///
///
///检测与目标点直接是否间隔墙壁
///
private RayCast2D? _attackObstacleDetection;
///
/// Navigation agent
/// 导航代理
///
private NavigationAgent2D? NavigationAgent2D { get; set; }
///
/// State machine
/// 状态机
///
private IStateMachine? StateMachine { get; set; }
///
/// Attack obstacle detection
/// 攻击障碍物检测
///
private RayCast2D? AttackObstacleDetection => _attackObstacleDetection;
///
/// Exclamation bubble id
/// 感叹气泡Id
///
private const int PlaintBubbleId = 0;
///
/// Query bubble id
/// 疑问气泡Id
///
private const int QueryBubbleId = 1;
///
/// BubbleMarker
/// 气泡标记
///
///
///Subsequent production of dialogue bubbles can be put into the parent class for players to use.
///后续制作对话泡时可进其放到父类,供玩家使用。
///
private BubbleMarker? _bubbleMarker;
///
/// Initial weapon ID
/// 初始的武器ID
///
[Export] public string? InitWeaponId;
public override void _Ready()
{
base._Ready();
_enemyInTheAttackRange = [];
_enemyInTheScoutRange = [];
_weaponInTheScoutRange = [];
_bubbleMarker = GetNode("BubbleMarker");
if (_bubbleMarker != null)
{
using var plaintScene = ResourceLoader.Load("res://prefab/ui/plaint.tscn");
var plaint = NodeUtils.InstantiatePackedScene(plaintScene);
if (plaint != null)
{
_bubbleMarker.AddBubble(PlaintBubbleId, plaint);
}
using var queryScene = ResourceLoader.Load("res://prefab/ui/query.tscn");
var query = NodeUtils.InstantiatePackedScene(queryScene);
if (query != null)
{
_bubbleMarker.AddBubble(QueryBubbleId, query);
}
}
_wallDetection = GetNode("WallDetection");
_attackArea = GetNode("AttackArea2D");
_scoutArea = GetNode("ScoutArea2D");
NavigationAgent2D = GetNode("NavigationAgent2D");
if (ItemMarker2D != null)
{
_attackObstacleDetection = ItemMarker2D.GetNode("AttackObstacleDetection");
}
if (_attackArea != null)
{
//If true, the zone will detect objects or areas entering and leaving the zone.
//如果为true,该区域将检测进出该区域的物体或区域。
_attackArea.Monitoring = true;
//Other areas can't detect our attack zone
//其他区域不能检测到我们的攻击区域
_attackArea.Monitorable = false;
_attackArea.BodyEntered += EnterTheAttackArea;
_attackArea.BodyExited += ExitTheAttackArea;
}
if (_scoutArea != null)
{
_scoutArea.Monitoring = true;
_scoutArea.Monitorable = false;
_scoutArea.BodyEntered += EnterTheScoutArea;
_scoutArea.BodyExited += ExitTheScoutArea;
}
_wallDetectionOrigin = _wallDetection.TargetPosition;
StateMachine = new PatrolStateMachine();
StateMachine.Context = new StateContext
{
CurrentState = State.Patrol,
Owner = this
};
if (StateMachine != null)
{
StateMachine.Start();
}
//You must create an item container for the character before you can pick up the item.
//必须为角色创建物品容器后才能拾起物品。
var universalItemContainer = new UniversalItemContainer(1);
universalItemContainer.AllowAddingItemByType(Config.ItemType.ProjectileWeapon);
ProtectedItemContainer = universalItemContainer;
//Add initial weapon
//添加初始武器
AddInitialWeapon(InitWeaponId);
}
///
/// Adds an initial weapon to the character
/// 为角色添加初始的武器
///
private void AddInitialWeapon(string? initWeaponId)
{
if (string.IsNullOrEmpty(initWeaponId))
{
return;
}
var item = ItemTypeManager.CreateItem(initWeaponId, this);
if (item is not WeaponTemplate weaponTemplate)
{
return;
}
NodeUtils.CallDeferredReparent(this, weaponTemplate);
PickItem(weaponTemplate);
}
///
/// Display exclamation marks
/// 显示感叹号
///
public void DispladyPlaint()
{
_bubbleMarker?.ShowBubble(PlaintBubbleId);
}
public void HidePlaint()
{
_bubbleMarker?.HideBubble(PlaintBubbleId);
}
///
/// Displady Query
/// 显示疑问
///
public void DispladyQuery()
{
_bubbleMarker?.ShowBubble(QueryBubbleId);
}
public void HideQuery()
{
_bubbleMarker?.HideBubble(QueryBubbleId);
}
///
/// Whether the enemy has been detected in the reconnaissance area
/// 侦察范围是否发现敌人
///
///
///Have you spotted the enemy?
///是否发现敌人
///
public bool ScoutEnemyDetected()
{
if (_enemyInTheScoutRange == null)
{
return false;
}
return _enemyInTheScoutRange.Count > 0;
}
///
/// Any weapons found in the recon area
/// 侦察范围内是否发现武器
///
///
public bool ScoutWeaponDetected()
{
if (_weaponInTheScoutRange == null)
{
return false;
}
return _weaponInTheScoutRange.Count > 0;
}
///
/// Get weapons in the recon area
/// 获取侦察范围内的武器
///
///
public WeaponTemplate[]? GetWeaponInScoutArea()
{
if (_weaponInTheScoutRange == null)
{
return null;
}
return _weaponInTheScoutRange.ToArray();
}
///
/// Get the first enemy in range
/// 获取第一个进入侦察范围的敌人
///
///
public CharacterTemplate? GetFirstEnemyInScoutArea()
{
if (_enemyInTheScoutRange == null || _enemyInTheScoutRange.Count == 0)
{
return null;
}
return _enemyInTheScoutRange[0];
}
///
/// Get the first enemy within striking range
/// 获取第一个进入攻击范围的敌人
///
///
public CharacterTemplate? GetFirstEnemyInAttackArea()
{
if (_enemyInTheAttackRange == null || _enemyInTheAttackRange.Count == 0)
{
return null;
}
return _enemyInTheAttackRange[0];
}
protected override void HookPhysicsProcess(ref Vector2 velocity, double delta)
{
StateMachine?.Execute();
if (NavigationAgent2D != null && IsOnFloor())
{
var nextPathPosition = NavigationAgent2D.GetNextPathPosition();
var direction = GlobalPosition.DirectionTo(nextPathPosition);
velocity = direction * Config.CellSize * Speed * ProtectedSpeedScale;
}
}
///
/// When the node enters the reconnaissance area
/// 当节点进入侦察区域后
///
///
private void EnterTheScoutArea(Node node)
{
if (node is WeaponTemplate weaponTemplate)
{
if (CanPickItem(weaponTemplate))
{
_weaponInTheScoutRange?.Add(weaponTemplate);
}
}
CanCauseHarmNode(node, (canCause, characterTemplate) =>
{
if (canCause && characterTemplate != null)
{
_enemyInTheScoutRange?.Add(characterTemplate);
}
});
}
///
/// When the node exits the reconnaissance area
/// 当节点退出侦察区域后
///
///
private void ExitTheScoutArea(Node node)
{
if (node == this)
{
return;
}
if (node is WeaponTemplate weaponTemplate)
{
_weaponInTheScoutRange?.Remove(weaponTemplate);
}
if (node is CharacterTemplate characterTemplate)
{
_enemyInTheScoutRange?.Remove(characterTemplate);
}
}
///
/// When a node enters the attack zone
/// 当节点进入攻击区域后
///
///
private void EnterTheAttackArea(Node node)
{
CanCauseHarmNode(node, (canCause, characterTemplate) =>
{
if (canCause && characterTemplate != null)
{
_enemyInTheAttackRange?.Add(characterTemplate);
}
});
}
///
/// CanCauseHarmNode
/// 是否可伤害某个节点
///
///
///
private void CanCauseHarmNode(Node node, Action action)
{
if (node == this)
{
//The target can't be yourself.
//攻击目标不能是自己。
action.Invoke(false, null);
return;
}
if (node is not CharacterTemplate characterTemplate)
{
action.Invoke(false, null);
return;
}
//Determine if damage can be done between factions
//判断阵营间是否可造成伤害
var camp = CampManager.GetCamp(CampId);
var enemyCamp = CampManager.GetCamp(characterTemplate.CampId);
if (enemyCamp != null && camp != null)
{
action.Invoke(CampManager.CanCauseHarm(camp, enemyCamp), characterTemplate);
return;
}
action.Invoke(false, characterTemplate);
}
private void ExitTheAttackArea(Node node)
{
if (node == this)
{
return;
}
if (node is CharacterTemplate characterTemplate)
{
_enemyInTheAttackRange?.Remove(characterTemplate);
}
}
///
/// Set target location
/// 设置目标位置
///
///
public void SetTargetPosition(Vector2 targetPosition)
{
if (NavigationAgent2D == null)
{
return;
}
NavigationAgent2D.TargetPosition = targetPosition;
}
public override void _ExitTree()
{
base._ExitTree();
if (_attackArea != null)
{
_attackArea.BodyEntered -= EnterTheAttackArea;
_attackArea.BodyExited -= ExitTheAttackArea;
}
if (_scoutArea != null)
{
_scoutArea.BodyEntered -= EnterTheScoutArea;
_scoutArea.BodyExited -= ExitTheScoutArea;
}
if (StateMachine != null)
{
StateMachine.Stop();
}
}
}