diff --git a/locals/Log.csv b/locals/Log.csv index ec89ed7..744e25f 100644 --- a/locals/Log.csv +++ b/locals/Log.csv @@ -83,4 +83,8 @@ log_weapon_detected,检测到武器。,Weapon detected.,武器が検出されま log_no_weapon_detected,没有检测到武器。,No weapon detected.,武器が検出されません。 log_weapon_lost,武器丢失。,Weapon lost.,武器が失われました。 log_nearest_node_is_null,最近的节点为空。,The nearest node is null.,最も近いノードが空です。 -log_node_is_not_WeaponTemplate,节点不是WeaponTemplate。,The node is not a WeaponTemplate.,ノードはWeaponTemplateではありません。 \ No newline at end of file +log_node_is_not_WeaponTemplate,节点不是WeaponTemplate。,The node is not a WeaponTemplate.,ノードはWeaponTemplateではありません。 +log_weapon_not_in_pickup_range,武器不在拾取范围内。,The weapon is not within the pickup range.,武器は拾い取り範囲内にありません。 +log_weapon_picked_up,武器被拾取。,Weapon picked up.,武器が拾い取られました。 +log_weapon_pickup_failed,武器拾取失败。,Weapon pickup failed.,武器の拾い取りに失敗しました。 +log_enter_the_picking_range_body,进入拾取范围。,Enter the picking range.,拾い取り範囲に入ります。 \ No newline at end of file diff --git a/prefab/entitys/Character.tscn b/prefab/entitys/Character.tscn index 463593b..0837ada 100644 --- a/prefab/entitys/Character.tscn +++ b/prefab/entitys/Character.tscn @@ -26,7 +26,6 @@ animations = [{ collision_layer = 4 collision_mask = 34 script = ExtResource("1_1dlls") -LootListId = null metadata/CampId = "Default" metadata/MaxHp = 32 diff --git a/prefab/entitys/DelivererOfDarkMagic.tscn b/prefab/entitys/DelivererOfDarkMagic.tscn index feedaf1..ca8b6a6 100644 --- a/prefab/entitys/DelivererOfDarkMagic.tscn +++ b/prefab/entitys/DelivererOfDarkMagic.tscn @@ -11,7 +11,7 @@ radius = 20.0 height = 52.0 [sub_resource type="CircleShape2D" id="CircleShape2D_vmqbt"] -radius = 34.5398 +radius = 65.3758 [sub_resource type="SpriteFrames" id="SpriteFrames_qumby"] animations = [{ diff --git a/scripts/Config.cs b/scripts/Config.cs index d626cbb..1278cff 100644 --- a/scripts/Config.cs +++ b/scripts/Config.cs @@ -8,19 +8,6 @@ namespace ColdMint.scripts; public static class Config { - /// - /// ID of the behavior tree - /// 行为树的ID - /// - public static class BehaviorTreeId - { - /// - /// 巡逻 - /// Patrol - /// - public const string Patrol = "Patrol"; - } - /// /// Loot table ID /// 战利品表ID @@ -34,31 +21,6 @@ public static class Config public const string Test = "test"; } - /// - /// BehaviorTreeResult - /// 行为树的结果 - /// - public static class BehaviorTreeResult - { - /// - /// Running - /// 运行中 - /// - public const int Running = 0; - - /// - /// Success - /// 成功 - /// - public const int Success = 1; - - /// - /// Failure - /// 失败 - /// - public const int Failure = 2; - } - /// /// Camp ID diff --git a/scripts/character/AiCharacter.cs b/scripts/character/AiCharacter.cs index c63eab4..dfb077e 100644 --- a/scripts/character/AiCharacter.cs +++ b/scripts/character/AiCharacter.cs @@ -16,472 +16,475 @@ namespace ColdMint.scripts.character; /// public sealed partial class AiCharacter : CharacterTemplate { - //Used to detect rays on walls - //用于检测墙壁的射线 - private RayCast2D? _wallDetection; - - public RayCast2D? WallDetection => _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 - /// 导航代理 - /// - public NavigationAgent2D? NavigationAgent2D { get; set; } - - - public IStateMachine? StateMachine { get; set; } - - - public 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) - { - _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(); - } - } -} \ No newline at end of file + //Used to detect rays on walls + //用于检测墙壁的射线 + private RayCast2D? _wallDetection; + + public RayCast2D? WallDetection => _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 + /// 导航代理 + /// + public NavigationAgent2D? NavigationAgent2D { get; set; } + + + public IStateMachine? StateMachine { get; set; } + + + public 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(); + } + } +} diff --git a/scripts/character/CharacterTemplate.cs b/scripts/character/CharacterTemplate.cs index baf6e6f..b0f7f89 100644 --- a/scripts/character/CharacterTemplate.cs +++ b/scripts/character/CharacterTemplate.cs @@ -240,14 +240,12 @@ public partial class CharacterTemplate : CharacterBody2D foreach (var pickingRangeBody in PickingRangeBodies) { if (pickingRangeBody is not WeaponTemplate weaponTemplate) continue; - if (weaponTemplate.Owner != null) + if (weaponTemplate.Picked) { continue; } - weaponTemplates.Add(weaponTemplate); } - return weaponTemplates.ToArray(); } @@ -308,7 +306,7 @@ public partial class CharacterTemplate : CharacterBody2D /// /// /// - private bool CanPickItem(Node node) + protected bool CanPickItem(Node node) { if (_currentItem != null && node == _currentItem) { @@ -336,7 +334,7 @@ public partial class CharacterTemplate : CharacterBody2D ///Whether successfully picked up ///是否成功拾起 /// - protected bool PickItem(Node2D? pickAbleItemNode2D) + public bool PickItem(Node2D? pickAbleItemNode2D) { //Empty reference checking is implicitly performed here. //此处隐式的执行了空引用检查。 @@ -585,12 +583,12 @@ public partial class CharacterTemplate : CharacterBody2D if (damageTemplate.Attacker is CharacterTemplate characterTemplate && !string.IsNullOrEmpty(characterTemplate.CharacterName)) { - LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat,CharacterName, + LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat, CharacterName, characterTemplate.CharacterName); } else { - LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat,CharacterName, + LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat, CharacterName, damageTemplate.Attacker.Name); } } @@ -612,6 +610,7 @@ public partial class CharacterTemplate : CharacterBody2D return; } + LogCat.Log("enter_the_picking_range_body"); PickingRangeBodiesList?.Add(node); } diff --git a/scripts/character/Player.cs b/scripts/character/Player.cs index 384161b..20d3cb5 100644 --- a/scripts/character/Player.cs +++ b/scripts/character/Player.cs @@ -17,498 +17,498 @@ namespace ColdMint.scripts.character; /// public partial class Player : CharacterTemplate { - private Control? _floatLabel; - - //Empty object projectile - //空的物品抛射线 - private readonly Vector2[] _emptyVector2Array = [Vector2.Zero]; - - //抛物线 - private Line2D? _parabola; - - //用于检测玩家是否站在平台上的射线 - private RayCast2D? _platformDetectionRayCast2D; - - - private const float PromptTextDistance = 50; - - - //抛出物品的飞行速度 - private float _throwingVelocity = Config.CellSize * 13; - - //射线是否与平台碰撞 - private bool _collidingWithPlatform; - - //How long does it take for the character to recover from a collision with the platform after jumping off the platform (in seconds) - //角色从平台上跳下后,多少时间后恢复与平台的碰撞(单位:秒) - private double _platformCollisionRecoveryTime = 0.2f; - - - public override void _Ready() - { - base._Ready(); - CharacterName = TranslationServerUtils.Translate("default_player_name"); - LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, LogCat.UploadFormat,ReadOnlyCharacterName, - GlobalPosition); - var floatLabelPackedScene = GD.Load("res://prefab/ui/FloatLabel.tscn"); - //Initializes the float label. - //初始化悬浮标签。 - _floatLabel = NodeUtils.InstantiatePackedScene(floatLabelPackedScene); - if (_floatLabel == null) - { - throw new NullReferenceException(TranslationServer.Translate("float_label_instantiate_failed")); - } - - _floatLabel.Hide(); - NodeUtils.CallDeferredAddChild(this, _floatLabel); - _parabola = GetNode("Parabola"); - _platformDetectionRayCast2D = GetNode("PlatformDetectionRayCast"); - UpdateOperationTip(); - var healthBarUi = GameSceneNodeHolder.HealthBarUi; - if (healthBarUi != null) - { - healthBarUi.MaxHp = MaxHp; - healthBarUi.CurrentHp = CurrentHp; - } - } - - protected override void WhenBindItemContainer(IItemContainer? itemContainer) - { - if (itemContainer == null) - { - return; - } - - //Subscribe to events when the item container is bound to the player. - //在物品容器与玩家绑定时订阅事件。 - itemContainer.SelectedItemSlotChangeEvent += SelectedItemSlotChangeEvent; - } - - public override void _ExitTree() - { - base._ExitTree(); - if (ProtectedItemContainer != null) - { - //Unsubscribe to events when this object is destroyed. - //此节点被销毁时,取消订阅事件。 - ProtectedItemContainer.SelectedItemSlotChangeEvent -= SelectedItemSlotChangeEvent; - } - } - - private void SelectedItemSlotChangeEvent(SelectedItemSlotChangeEvent selectedItemSlotChangeEvent) - { - var item = selectedItemSlotChangeEvent.NewItemSlotNode?.GetItem(); - GameSceneNodeHolder.HideBackpackUiContainerIfVisible(); - if (item is Node2D node2D) - { - CurrentItem = node2D; - } - else - { - CurrentItem = null; - } - } - - /// - /// Update operation prompt - /// 更新操作提示 - /// - private void UpdateOperationTip() - { - var operationTipLabel = GameSceneNodeHolder.OperationTipLabel; - if (operationTipLabel == null) - { - return; - } - - var operationTipBuilder = new StringBuilder(); - - operationTipBuilder.Append("[color="); - operationTipBuilder.Append(Config.OperationTipActionColor); - operationTipBuilder.Append(']'); - operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_left")[0].AsText())); - operationTipBuilder.Append("[/color]"); - operationTipBuilder.Append(TranslationServerUtils.Translate("action_move_left")); - operationTipBuilder.Append(' '); - operationTipBuilder.Append("[color="); - operationTipBuilder.Append(Config.OperationTipActionColor); - operationTipBuilder.Append(']'); - operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_right")[0].AsText())); - operationTipBuilder.Append("[/color]"); - operationTipBuilder.Append(TranslationServerUtils.Translate("action_move_right")); - operationTipBuilder.Append(' '); - operationTipBuilder.Append("[color="); - operationTipBuilder.Append(Config.OperationTipActionColor); - operationTipBuilder.Append(']'); - operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_up")[0].AsText())); - operationTipBuilder.Append("[/color]"); - operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump")); - if (_collidingWithPlatform) - { - operationTipBuilder.Append(' '); - operationTipBuilder.Append("[color="); - operationTipBuilder.Append(Config.OperationTipActionColor); - operationTipBuilder.Append(']'); - operationTipBuilder.Append( - TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_down")[0].AsText())); - operationTipBuilder.Append("[/color]"); - operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump_down")); - } - - var nearestItem = FindTheNearestItem(); - if (nearestItem != null) - { - operationTipBuilder.Append(' '); - operationTipBuilder.Append("[color="); - operationTipBuilder.Append(Config.OperationTipActionColor); - operationTipBuilder.Append(']'); - operationTipBuilder.Append( - TranslationServerUtils.Translate(InputMap.ActionGetEvents("pick_up")[0].AsText())); - operationTipBuilder.Append("[/color]"); - operationTipBuilder.Append(TranslationServerUtils.Translate("action_pick_up")); - if (nearestItem is IItem item) - { - operationTipBuilder.Append(item.Name); - } - - operationTipLabel.Text = operationTipBuilder.ToString(); - } - - if (CurrentItem != null) - { - operationTipBuilder.Append(' '); - operationTipBuilder.Append("[color="); - operationTipBuilder.Append(Config.OperationTipActionColor); - operationTipBuilder.Append(']'); - operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("throw")[0].AsText())); - operationTipBuilder.Append("[/color]"); - operationTipBuilder.Append(TranslationServerUtils.Translate("action_throw")); - if (CurrentItem is IItem item) - { - operationTipBuilder.Append(TranslationServerUtils.Translate(item.Name)); - operationTipBuilder.Append(' '); - operationTipBuilder.Append("[color="); - operationTipBuilder.Append(Config.OperationTipActionColor); - operationTipBuilder.Append(']'); - operationTipBuilder.Append( - TranslationServerUtils.Translate(InputMap.ActionGetEvents("use_item")[0].AsText())); - operationTipBuilder.Append("[/color]"); - operationTipBuilder.Append(TranslationServerUtils.Translate("action_use_item")); - operationTipBuilder.Append(TranslationServerUtils.Translate(item.Name)); - } - } - - operationTipLabel.Text = operationTipBuilder.ToString(); - } - - - protected override void HookPhysicsProcess(ref Vector2 velocity, double delta) - { - //When the collision state between the platform detection ray and the platform changes - //在平台检测射线与平台碰撞状态改变时 - if (_platformDetectionRayCast2D != null && _platformDetectionRayCast2D.IsColliding() != _collidingWithPlatform) - { - //When the state changes, update the action hint - //当状态改变时,更新操作提示 - _collidingWithPlatform = _platformDetectionRayCast2D.IsColliding(); - UpdateOperationTip(); - } - - //If the character is on the ground, give an upward velocity when the jump button is pressed - //如果角色正在地面上,按下跳跃键时,给予一个向上的速度 - if (Input.IsActionJustPressed("ui_up") && IsOnFloor()) - velocity.Y = JumpVelocity; - - //Moving left and right - //左右移动 - var axis = Input.GetAxis("ui_left", "ui_right"); - velocity.X = axis * Speed * Config.CellSize * ProtectedSpeedScale; - - //Use items - //使用物品 - if (Input.IsActionPressed("use_item")) - { - UseItem(GetGlobalMousePosition()); - } - - //Pick up an item - //捡起物品 - if (Input.IsActionJustPressed("pick_up")) - { - var pickAbleItem = FindTheNearestItem(); - var success = PickItem(pickAbleItem); - if (success) - { - if (pickAbleItem != null) - { - PickingRangeBodiesList?.Remove(pickAbleItem); - } - - RecycleFloatLabel(); - } - } - - if (Input.IsActionJustPressed("ui_down")) - { - if (_collidingWithPlatform) - { - //When the character stands on the platform and presses the ui_down key, we cancel the collision between the character and the platform - //当角色站在平台上按下 ui_down 键时,我们取消角色与平台的碰撞 - var timer = new Timer(); - AddChild(timer); - timer.WaitTime = _platformCollisionRecoveryTime; - timer.OneShot = true; - timer.Start(); - timer.Timeout += () => - { - SetCollisionMaskValue(Config.LayerNumber.Platform, true); - timer.QueueFree(); - }; - SetCollisionMaskValue(Config.LayerNumber.Platform, false); - } - } - - - //Display a parabola when an item is thrown - //抛出物品时,显示抛物线 - if (Input.IsActionPressed("throw")) - { - if (_parabola == null) - { - return; - } - - if (ItemMarker2D == null) - { - //Cannot get the marked location of the item, then do not draw a line - //无法获取物品的标记位置,那么不绘制线 - return; - } - - _parabola.Points = CurrentItem == null - ? _emptyVector2Array - : ParabolicUtils.ComputeParabolic(ItemMarker2D.Position, GetThrowVelocity(), Gravity, 0.1f); - } - - - //When you raise your hand, throw the object - //抬起手时,抛出物品 - if (Input.IsActionJustReleased("throw")) - { - if (ItemContainer == null) - { - return; - } - - if (_parabola != null) - { - _parabola.Points = [Vector2.Zero]; - } - - ThrowItem(ItemContainer.GetSelectIndex(), 1, GetThrowVelocity()); - GameSceneNodeHolder.HideBackpackUiContainerIfVisible(); - CurrentItem = null; - } - } - - protected override void WhenUpdateCurrentItem(Node2D? currentItem) - { - UpdateOperationTip(); - } - - /// - /// 当玩家手动抛出物品时,施加到物品上的速度值 - /// - /// - private Vector2 GetThrowVelocity() - { - //We take the mouse position, normalize it, and then multiply it by the distance the player can throw - //我们拿到鼠标的位置,将其归一化处理,然后乘以玩家可扔出的距离 - return GetLocalMousePosition().Normalized() * _throwingVelocity; - } - - public override void _Process(double delta) - { - if (!Visible) - { - return; - } - - AimTheCurrentItemAtAPoint(GetGlobalMousePosition()); - var itemMarker2DPosition = Vector2.Zero; - if (ItemMarker2D != null) - { - itemMarker2DPosition = ItemMarker2D.Position; - } - - var axis = Input.GetAxis("ui_left", "ui_right"); - switch (axis) - { - case -1: - //Minus 1, we move to the left - //-1,向左移动 - FacingLeft = true; - if (ItemMarker2D != null) - { - itemMarker2DPosition.X = -ReadOnlyItemMarkerOriginalX; - } - - Flip(); - break; - case 1: - //1, move to the right - //1,向右移动 - FacingLeft = false; - if (ItemMarker2D != null) - { - itemMarker2DPosition.X = ReadOnlyItemMarkerOriginalX; - } - - Flip(); - break; - } - - if (ItemMarker2D != null) - { - ItemMarker2D.Position = itemMarker2DPosition; - } - } - - protected override void Flip() - { - base.Flip(); - //If there is a weapon, flip it too - //如果有武器的话,也要翻转 - if (CurrentItem is PickAbleTemplate pickAbleTemplate) - { - pickAbleTemplate.Flip(FacingLeft); - } - } - - public override void Revive(int newHp) - { - base.Revive(newHp); - var healthBarUi = GameSceneNodeHolder.HealthBarUi; - if (healthBarUi != null) - { - //The purpose of setting Hp to the current Hp is to cause the life bar to refresh. - //将Hp设置为当前Hp的目的是,使生命条刷新。 - healthBarUi.CurrentHp = CurrentHp; - } - } - - /// - /// When the player dies - /// 当玩家死亡的时候 - /// - /// - protected override async Task OnDie(DamageTemplate damageTemplate) - { - Hide(); - ProcessMode = ProcessModeEnum.Disabled; - if (EventManager.GameOverEvent == null) - { - return; - } - - var gameOverEvent = new GameOverEvent(); - if (damageTemplate.Attacker != null) - { - gameOverEvent.DeathInfo = await DeathInfoGenerator.GenerateDeathInfo(this, damageTemplate.Attacker); - } - - EventManager.GameOverEvent.Invoke(gameOverEvent); - } - - protected override void EnterThePickingRangeBody(Node node) - { - base.EnterThePickingRangeBody(node); - if (CurrentItem == node) - { - //If the node entering the pick range is the node held by the player, then return. - //如果说进入拾捡范围的节点是玩家所持有的节点,那么返回。 - return; - } - - if (node is not Node2D node2D) - { - return; - } - - if (_floatLabel != null) - { - if (node is not PickAbleTemplate pickAbleTemplate) - { - return; - } - - if (pickAbleTemplate.Picked) - { - //If the pickables are picked up, the label is not displayed. - //如果可拾捡物被捡起了,那么不显示标签。 - LogCat.LogWarning("pickable_picked_up"); - return; - } - - NodeUtils.CallDeferredReparent(node, _floatLabel); - var rotationDegreesNode2D = node2D.RotationDegrees; - var rotationDegreesNode2DAbs = Math.Abs(rotationDegreesNode2D); - _floatLabel.GlobalPosition = node2D.GlobalPosition; - _floatLabel.Position = rotationDegreesNode2DAbs > 90 - ? new Vector2(0, PromptTextDistance) - : new Vector2(0, -PromptTextDistance); - _floatLabel.RotationDegrees = 0 - rotationDegreesNode2D; - var label = _floatLabel.GetNode