2024-07-07 15:10:25 +00:00
using System ;
2024-05-08 10:22:04 +00:00
using System.Collections.Generic ;
2024-07-07 15:10:25 +00:00
using ColdMint.scripts.bubble ;
2024-07-06 14:55:07 +00:00
using ColdMint.scripts.camp ;
2024-07-28 03:12:53 +00:00
using ColdMint.scripts.debug ;
2024-07-10 15:23:04 +00:00
using ColdMint.scripts.inventory ;
2024-06-30 23:55:58 +00:00
using ColdMint.scripts.stateMachine ;
2024-07-07 15:10:25 +00:00
using ColdMint.scripts.utils ;
2024-07-10 15:23:04 +00:00
using ColdMint.scripts.weapon ;
2024-05-08 10:22:04 +00:00
using Godot ;
namespace ColdMint.scripts.character ;
/// <summary>
/// <para>The role played by computers</para>
/// <para>由电脑扮演的角色</para>
/// </summary>
2024-06-06 13:05:51 +00:00
public sealed partial class AiCharacter : CharacterTemplate
2024-05-08 10:22:04 +00:00
{
//Used to detect rays on walls
//用于检测墙壁的射线
private RayCast2D ? _wallDetection ;
private Vector2 _wallDetectionOrigin ;
private Area2D ? _attackArea ;
2024-07-07 15:10:25 +00:00
/// <summary>
/// <para>Reconnaissance area</para>
/// <para>侦察区域</para>
/// </summary>
/// <remarks>
///<para>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.</para>
///<para>大多数情况下,当敌人进入侦察区域后,角色会发出“疑问(问号)”,并尝试向事件点缓慢移动。</para>
/// </remarks>
private Area2D ? _scoutArea ;
2024-05-08 10:22:04 +00:00
/// <summary>
2024-07-06 14:55:07 +00:00
/// <para>All enemies within striking distance</para>
/// <para>在攻击范围内的所有敌人</para>
2024-05-08 10:22:04 +00:00
/// </summary>
2024-07-06 14:55:07 +00:00
private List < CharacterTemplate > ? _enemyInTheAttackRange ;
2024-05-08 10:22:04 +00:00
2024-07-07 15:10:25 +00:00
/// <summary>
/// <para>Scout all enemies within range</para>
/// <para>在侦察范围内所有的敌人</para>
/// </summary>
private List < CharacterTemplate > ? _enemyInTheScoutRange ;
2024-07-17 14:54:42 +00:00
/// <summary>
/// <para>Every weapon in the recon area</para>
/// <para>在侦察范围内所有的武器</para>
/// </summary>
private List < WeaponTemplate > ? _weaponInTheScoutRange ;
2024-05-08 10:22:04 +00:00
/// <summary>
/// <para>Obstacle detection ray during attack</para>
/// <para>攻击时的障碍物检测射线</para>
/// </summary>
/// <remarks>
///<para></para>
///<para>检测与目标点直接是否间隔墙壁</para>
/// </remarks>
private RayCast2D ? _attackObstacleDetection ;
2024-06-30 23:55:58 +00:00
2024-07-07 15:10:25 +00:00
private VisibleOnScreenEnabler2D ? _screenEnabler2D ;
2024-06-30 23:55:58 +00:00
/// <summary>
/// <para>Navigation agent</para>
/// <para>导航代理</para>
/// </summary>
2024-07-25 12:32:59 +00:00
private NavigationAgent2D ? NavigationAgent2D { get ; set ; }
2024-06-30 23:55:58 +00:00
2024-07-25 12:32:59 +00:00
/// <summary>
/// <para>State machine</para>
/// <para>状态机</para>
/// </summary>
private IStateMachine ? StateMachine { get ; set ; }
2024-06-30 23:55:58 +00:00
2024-05-08 10:22:04 +00:00
2024-07-25 12:32:59 +00:00
/// <summary>
/// <para>Attack obstacle detection</para>
/// <para>攻击障碍物检测</para>
/// </summary>
private RayCast2D ? AttackObstacleDetection = > _attackObstacleDetection ;
2024-05-08 10:22:04 +00:00
2024-07-07 15:10:25 +00:00
/// <summary>
2024-07-19 15:18:53 +00:00
/// <para>Exclamation bubble id</para>
2024-07-07 15:10:25 +00:00
/// <para>感叹气泡Id</para>
/// </summary>
2024-07-19 15:18:53 +00:00
private const int PlaintBubbleId = 0 ;
2024-07-07 15:10:25 +00:00
/// <summary>
2024-07-19 15:18:53 +00:00
/// <para>Query bubble id</para>
2024-07-07 15:10:25 +00:00
/// <para>疑问气泡Id</para>
/// </summary>
2024-07-19 15:18:53 +00:00
private const int QueryBubbleId = 1 ;
2024-07-07 15:10:25 +00:00
/// <summary>
/// <para>BubbleMarker</para>
/// <para>气泡标记</para>
/// </summary>
/// <remarks>
///<para>Subsequent production of dialogue bubbles can be put into the parent class for players to use.</para>
///<para>后续制作对话泡时可进其放到父类,供玩家使用。</para>
/// </remarks>
private BubbleMarker ? _bubbleMarker ;
2024-07-10 15:23:04 +00:00
/// <summary>
/// <para>The initial weapons scene</para>
/// <para>初始的武器场景</para>
/// </summary>
[Export] public string? InitWeaponRes ;
2024-05-08 10:22:04 +00:00
public override void _Ready ( )
{
base . _Ready ( ) ;
2024-07-07 15:10:25 +00:00
2024-07-06 14:55:07 +00:00
_enemyInTheAttackRange = new List < CharacterTemplate > ( ) ;
2024-07-07 15:10:25 +00:00
_enemyInTheScoutRange = new List < CharacterTemplate > ( ) ;
2024-07-17 14:54:42 +00:00
_weaponInTheScoutRange = new List < WeaponTemplate > ( ) ;
2024-07-07 15:10:25 +00:00
_screenEnabler2D = GetNode < VisibleOnScreenEnabler2D > ( "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 > ( "BubbleMarker" ) ;
if ( _bubbleMarker ! = null )
{
using var plaintScene = GD . Load < PackedScene > ( "res://prefab/ui/plaint.tscn" ) ;
2024-07-10 15:23:04 +00:00
var plaint = NodeUtils . InstantiatePackedScene < Control > ( plaintScene ) ;
2024-07-07 15:10:25 +00:00
if ( plaint ! = null )
{
2024-07-19 15:18:53 +00:00
_bubbleMarker . AddBubble ( PlaintBubbleId , plaint ) ;
2024-07-07 15:10:25 +00:00
}
using var queryScene = GD . Load < PackedScene > ( "res://prefab/ui/query.tscn" ) ;
2024-07-10 15:23:04 +00:00
var query = NodeUtils . InstantiatePackedScene < Control > ( queryScene ) ;
2024-07-07 15:10:25 +00:00
if ( query ! = null )
{
2024-07-19 15:18:53 +00:00
_bubbleMarker . AddBubble ( QueryBubbleId , query ) ;
2024-07-07 15:10:25 +00:00
}
}
2024-05-08 10:22:04 +00:00
_wallDetection = GetNode < RayCast2D > ( "WallDetection" ) ;
_attackArea = GetNode < Area2D > ( "AttackArea2D" ) ;
2024-07-07 15:10:25 +00:00
_scoutArea = GetNode < Area2D > ( "ScoutArea2D" ) ;
2024-06-30 23:55:58 +00:00
NavigationAgent2D = GetNode < NavigationAgent2D > ( "NavigationAgent2D" ) ;
2024-05-08 10:22:04 +00:00
if ( ItemMarker2D ! = null )
{
_attackObstacleDetection = ItemMarker2D . GetNode < RayCast2D > ( "AttackObstacleDetection" ) ;
}
if ( _attackArea ! = null )
{
2024-06-06 13:05:51 +00:00
//If true, the zone will detect objects or areas entering and leaving the zone.
2024-05-08 10:22:04 +00:00
//如果为true, 该区域将检测进出该区域的物体或区域。
_attackArea . Monitoring = true ;
2024-06-06 13:05:51 +00:00
//Other areas can't detect our attack zone
//其他区域不能检测到我们的攻击区域
2024-05-08 10:22:04 +00:00
_attackArea . Monitorable = false ;
_attackArea . BodyEntered + = EnterTheAttackArea ;
_attackArea . BodyExited + = ExitTheAttackArea ;
}
2024-07-07 15:10:25 +00:00
if ( _scoutArea ! = null )
{
_scoutArea . Monitoring = true ;
_scoutArea . Monitorable = false ;
_scoutArea . BodyEntered + = EnterTheScoutArea ;
_scoutArea . BodyExited + = ExitTheScoutArea ;
}
2024-05-08 10:22:04 +00:00
_wallDetectionOrigin = _wallDetection . TargetPosition ;
2024-06-30 23:55:58 +00:00
StateMachine = new PatrolStateMachine ( ) ;
StateMachine . Context = new StateContext
{
CurrentState = State . Patrol ,
Owner = this
} ;
if ( StateMachine ! = null )
{
StateMachine . Start ( ) ;
}
2024-07-10 15:23:04 +00:00
//You must create an item container for the character before you can pick up the item.
//必须为角色创建物品容器后才能拾起物品。
var universalItemContainer = new UniversalItemContainer ( ) ;
2024-07-17 14:54:42 +00:00
var itemSlotNode = universalItemContainer . AddItemSlot ( this ) ;
2024-07-10 15:23:04 +00:00
itemSlotNode ? . Hide ( ) ;
ProtectedItemContainer = universalItemContainer ;
//Add initial weapon
//添加初始武器
AddInitialWeapon ( InitWeaponRes ) ;
}
/// <summary>
/// <para>Adds an initial weapon to the character</para>
/// <para>为角色添加初始的武器</para>
/// </summary>
private void AddInitialWeapon ( string? initWeaponRes )
{
2024-07-28 03:12:53 +00:00
if ( string . IsNullOrEmpty ( initWeaponRes ) )
2024-07-10 15:23:04 +00:00
{
return ;
}
//Set the resource path of the initial weapon and try to create the object of the initial weapon.
//设置了初始武器的资源路径,尝试创建初始武器的对象。
var packedScene = GD . Load < PackedScene > ( initWeaponRes ) ;
var weaponTemplate = NodeUtils . InstantiatePackedScene < WeaponTemplate > ( packedScene ) ;
if ( weaponTemplate = = null )
{
return ;
}
2024-07-17 14:54:42 +00:00
2024-07-10 15:23:04 +00:00
NodeUtils . CallDeferredAddChild ( this , weaponTemplate ) ;
PickItem ( weaponTemplate ) ;
2024-06-30 23:55:58 +00:00
}
2024-07-10 15:23:04 +00:00
2024-07-06 14:55:07 +00:00
/// <summary>
2024-07-07 15:10:25 +00:00
/// <para>Display exclamation marks</para>
/// <para>显示感叹号</para>
/// </summary>
public void DispladyPlaint ( )
{
2024-07-19 15:18:53 +00:00
_bubbleMarker ? . ShowBubble ( PlaintBubbleId ) ;
2024-07-07 15:10:25 +00:00
}
public void HidePlaint ( )
{
2024-07-19 15:18:53 +00:00
_bubbleMarker ? . HideBubble ( PlaintBubbleId ) ;
2024-07-07 15:10:25 +00:00
}
/// <summary>
/// <para>Displady Query</para>
/// <para>显示疑问</para>
/// </summary>
public void DispladyQuery ( )
{
2024-07-19 15:18:53 +00:00
_bubbleMarker ? . ShowBubble ( QueryBubbleId ) ;
2024-07-07 15:10:25 +00:00
}
2024-07-10 15:23:04 +00:00
public void HideQuery ( )
2024-07-07 15:10:25 +00:00
{
2024-07-19 15:18:53 +00:00
_bubbleMarker ? . HideBubble ( QueryBubbleId ) ;
2024-07-07 15:10:25 +00:00
}
/// <summary>
/// <para>Whether the enemy has been detected in the reconnaissance area</para>
/// <para>侦察范围是否发现敌人</para>
2024-07-06 14:55:07 +00:00
/// </summary>
/// <returns>
///<para>Have you spotted the enemy?</para>
///<para>是否发现敌人</para>
/// </returns>
2024-07-07 15:10:25 +00:00
public bool ScoutEnemyDetected ( )
2024-07-06 14:55:07 +00:00
{
2024-07-07 15:10:25 +00:00
if ( _enemyInTheScoutRange = = null )
2024-07-06 14:55:07 +00:00
{
return false ;
}
2024-07-07 15:10:25 +00:00
return _enemyInTheScoutRange . Count > 0 ;
}
2024-07-17 14:54:42 +00:00
/// <summary>
/// <para>Any weapons found in the recon area</para>
/// <para>侦察范围内是否发现武器</para>
/// </summary>
/// <returns></returns>
public bool ScoutWeaponDetected ( )
{
if ( _weaponInTheScoutRange = = null )
{
return false ;
}
return _weaponInTheScoutRange . Count > 0 ;
}
/// <summary>
/// <para>Get weapons in the recon area</para>
/// <para>获取侦察范围内的武器</para>
/// </summary>
/// <returns></returns>
public WeaponTemplate [ ] ? GetWeaponInScoutArea ( )
{
if ( _weaponInTheScoutRange = = null )
{
return null ;
}
return _weaponInTheScoutRange . ToArray ( ) ;
}
2024-07-07 15:10:25 +00:00
/// <summary>
/// <para>Get the first enemy in range</para>
/// <para>获取第一个进入侦察范围的敌人</para>
/// </summary>
/// <returns></returns>
public CharacterTemplate ? GetFirstEnemyInScoutArea ( )
{
if ( _enemyInTheScoutRange = = null | | _enemyInTheScoutRange . Count = = 0 )
{
return null ;
}
return _enemyInTheScoutRange [ 0 ] ;
2024-07-06 14:55:07 +00:00
}
/// <summary>
2024-07-07 15:10:25 +00:00
/// <para>Get the first enemy within striking range</para>
/// <para>获取第一个进入攻击范围的敌人</para>
2024-07-06 14:55:07 +00:00
/// </summary>
/// <returns></returns>
2024-07-07 15:10:25 +00:00
public CharacterTemplate ? GetFirstEnemyInAttackArea ( )
2024-07-06 14:55:07 +00:00
{
if ( _enemyInTheAttackRange = = null | | _enemyInTheAttackRange . Count = = 0 )
{
return null ;
}
2024-07-07 15:10:25 +00:00
2024-07-06 14:55:07 +00:00
return _enemyInTheAttackRange [ 0 ] ;
}
2024-06-30 23:55:58 +00:00
protected override void HookPhysicsProcess ( ref Vector2 velocity , double delta )
{
StateMachine ? . Execute ( ) ;
2024-07-02 15:16:04 +00:00
if ( NavigationAgent2D ! = null & & IsOnFloor ( ) )
{
var nextPathPosition = NavigationAgent2D . GetNextPathPosition ( ) ;
var direction = ( nextPathPosition - GlobalPosition ) . Normalized ( ) ;
2024-07-07 15:10:25 +00:00
velocity = direction * Config . CellSize * Speed * ProtectedSpeedScale ;
2024-07-02 15:16:04 +00:00
}
2024-05-08 10:22:04 +00:00
}
2024-07-07 15:10:25 +00:00
/// <summary>
/// <para>When the node enters the reconnaissance area</para>
/// <para>当节点进入侦察区域后</para>
/// </summary>
/// <param name="node"></param>
private void EnterTheScoutArea ( Node node )
{
2024-07-17 14:54:42 +00:00
if ( node is WeaponTemplate weaponTemplate )
{
2024-07-18 14:45:39 +00:00
if ( CanPickItem ( weaponTemplate ) )
{
_weaponInTheScoutRange ? . Add ( weaponTemplate ) ;
}
2024-07-17 14:54:42 +00:00
}
2024-07-07 15:10:25 +00:00
CanCauseHarmNode ( node , ( canCause , characterTemplate ) = >
{
if ( canCause & & characterTemplate ! = null )
{
_enemyInTheScoutRange ? . Add ( characterTemplate ) ;
}
} ) ;
}
/// <summary>
/// <para>When the node exits the reconnaissance area</para>
/// <para>当节点退出侦察区域后</para>
/// </summary>
/// <param name="node"></param>
private void ExitTheScoutArea ( Node node )
2024-05-08 10:22:04 +00:00
{
2024-07-06 14:55:07 +00:00
if ( node = = this )
{
return ;
}
2024-07-17 14:54:42 +00:00
if ( node is WeaponTemplate weaponTemplate )
{
_weaponInTheScoutRange ? . Remove ( weaponTemplate ) ;
}
2024-07-06 14:55:07 +00:00
if ( node is CharacterTemplate characterTemplate )
{
2024-07-07 15:10:25 +00:00
_enemyInTheScoutRange ? . Remove ( characterTemplate ) ;
}
}
/// <summary>
/// <para>When a node enters the attack zone</para>
/// <para>当节点进入攻击区域后</para>
/// </summary>
/// <param name="node"></param>
private void EnterTheAttackArea ( Node node )
{
CanCauseHarmNode ( node , ( canCause , characterTemplate ) = >
{
if ( canCause & & characterTemplate ! = null )
2024-07-06 14:55:07 +00:00
{
2024-07-07 15:10:25 +00:00
_enemyInTheAttackRange ? . Add ( characterTemplate ) ;
2024-07-06 14:55:07 +00:00
}
2024-07-07 15:10:25 +00:00
} ) ;
}
/// <summary>
/// <para>CanCauseHarmNode</para>
/// <para>是否可伤害某个节点</para>
/// </summary>
/// <param name="node"></param>
/// <param name="action"></param>
private void CanCauseHarmNode ( Node node , Action < bool , CharacterTemplate ? > action )
{
if ( node = = this )
{
//The target can't be yourself.
//攻击目标不能是自己。
action . Invoke ( false , null ) ;
return ;
2024-07-06 14:55:07 +00:00
}
2024-07-07 15:10:25 +00:00
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 ) ;
2024-05-08 10:22:04 +00:00
}
2024-06-06 13:05:51 +00:00
private void ExitTheAttackArea ( Node node )
2024-05-08 10:22:04 +00:00
{
2024-07-06 14:55:07 +00:00
if ( node = = this )
{
return ;
}
if ( node is CharacterTemplate characterTemplate )
{
_enemyInTheAttackRange ? . Remove ( characterTemplate ) ;
}
2024-05-08 10:22:04 +00:00
}
/// <summary>
2024-07-02 15:16:04 +00:00
/// <para>Set target location</para>
/// <para>设置目标位置</para>
2024-05-08 10:22:04 +00:00
/// </summary>
2024-07-02 15:16:04 +00:00
/// <param name="targetPosition"></param>
public void SetTargetPosition ( Vector2 targetPosition )
2024-05-08 10:22:04 +00:00
{
2024-07-02 15:16:04 +00:00
if ( NavigationAgent2D = = null )
2024-06-30 23:55:58 +00:00
{
return ;
}
2024-07-02 15:16:04 +00:00
NavigationAgent2D . TargetPosition = targetPosition ;
2024-05-08 10:22:04 +00:00
}
2024-06-30 23:55:58 +00:00
public override void _ExitTree ( )
{
base . _ExitTree ( ) ;
if ( _attackArea ! = null )
{
_attackArea . BodyEntered - = EnterTheAttackArea ;
_attackArea . BodyExited - = ExitTheAttackArea ;
}
2024-07-07 15:10:25 +00:00
if ( _scoutArea ! = null )
{
_scoutArea . BodyEntered - = EnterTheScoutArea ;
_scoutArea . BodyExited - = ExitTheScoutArea ;
}
2024-06-30 23:55:58 +00:00
if ( StateMachine ! = null )
{
StateMachine . Stop ( ) ;
}
2024-05-08 10:22:04 +00:00
}
2024-07-26 14:38:18 +00:00
}