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