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; private VisibleOnScreenEnabler2D? _screenEnabler2D; /// /// 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; /// /// The initial weapons scene /// 初始的武器场景 /// [Export] public string? InitWeaponRes; public override void _Ready() { base._Ready(); _enemyInTheAttackRange = new List(); _enemyInTheScoutRange = new List(); _weaponInTheScoutRange = new List(); _screenEnabler2D = GetNode("VisibleOnScreenEnabler2D"); _screenEnabler2D.ScreenEntered += () => { //When the character enters the screen. //当角色进入屏幕。 ProcessMode = ProcessModeEnum.Disabled; }; _screenEnabler2D.ScreenExited += () => { //When the character leaves the screen. //当角色离开屏幕。 ProcessMode = ProcessModeEnum.Inherit; }; _bubbleMarker = GetNode("BubbleMarker"); if (_bubbleMarker != null) { using var plaintScene = GD.Load("res://prefab/ui/plaint.tscn"); var plaint = NodeUtils.InstantiatePackedScene(plaintScene); if (plaint != null) { _bubbleMarker.AddBubble(PlaintBubbleId, plaint); } using var queryScene = GD.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(); var itemSlotNode = universalItemContainer.AddItemSlot(this); itemSlotNode?.Hide(); ProtectedItemContainer = universalItemContainer; //Add initial weapon //添加初始武器 AddInitialWeapon(InitWeaponRes); } /// /// Adds an initial weapon to the character /// 为角色添加初始的武器 /// private void AddInitialWeapon(string? initWeaponRes) { if (initWeaponRes == null) { return; } //Set the resource path of the initial weapon and try to create the object of the initial weapon. //设置了初始武器的资源路径,尝试创建初始武器的对象。 var packedScene = GD.Load(initWeaponRes); var weaponTemplate = NodeUtils.InstantiatePackedScene(packedScene); if (weaponTemplate == null) { return; } NodeUtils.CallDeferredAddChild(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 = (nextPathPosition - GlobalPosition).Normalized(); 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(); } } }