怪物可以捡起武器打玩家了。

This commit is contained in:
Cold-Mint 2024-07-18 19:46:23 +08:00
parent 7812d9c570
commit e4583aed3c
9 changed files with 1114 additions and 1103 deletions

View File

@ -83,4 +83,8 @@ log_weapon_detected,检测到武器。,Weapon detected.,武器が検出されま
log_no_weapon_detected,没有检测到武器。,No weapon detected.,武器が検出されません。 log_no_weapon_detected,没有检测到武器。,No weapon detected.,武器が検出されません。
log_weapon_lost,武器丢失。,Weapon lost.,武器が失われました。 log_weapon_lost,武器丢失。,Weapon lost.,武器が失われました。
log_nearest_node_is_null,最近的节点为空。,The nearest node is null.,最も近いノードが空です。 log_nearest_node_is_null,最近的节点为空。,The nearest node is null.,最も近いノードが空です。
log_node_is_not_WeaponTemplate,节点不是WeaponTemplate。,The node is not a WeaponTemplate.,ードはWeaponTemplateではありません。 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.,拾い取り範囲に入ります。
1 id zh en ja
83 log_nearest_node_is_null 最近的节点为空。 The nearest node is null. 最も近いノードが空です。
84 log_node_is_not_WeaponTemplate 节点不是WeaponTemplate。 The node is not a WeaponTemplate. ノードはWeaponTemplateではありません。
85 log_weapon_not_in_pickup_range 武器不在拾取范围内。 The weapon is not within the pickup range. 武器は拾い取り範囲内にありません。
86 log_weapon_picked_up 武器被拾取。 Weapon picked up. 武器が拾い取られました。
87 log_weapon_pickup_failed 武器拾取失败。 Weapon pickup failed. 武器の拾い取りに失敗しました。
88 log_enter_the_picking_range_body 进入拾取范围。 Enter the picking range. 拾い取り範囲に入ります。
89
90

View File

@ -26,7 +26,6 @@ animations = [{
collision_layer = 4 collision_layer = 4
collision_mask = 34 collision_mask = 34
script = ExtResource("1_1dlls") script = ExtResource("1_1dlls")
LootListId = null
metadata/CampId = "Default" metadata/CampId = "Default"
metadata/MaxHp = 32 metadata/MaxHp = 32

View File

@ -11,7 +11,7 @@ radius = 20.0
height = 52.0 height = 52.0
[sub_resource type="CircleShape2D" id="CircleShape2D_vmqbt"] [sub_resource type="CircleShape2D" id="CircleShape2D_vmqbt"]
radius = 34.5398 radius = 65.3758
[sub_resource type="SpriteFrames" id="SpriteFrames_qumby"] [sub_resource type="SpriteFrames" id="SpriteFrames_qumby"]
animations = [{ animations = [{

View File

@ -8,19 +8,6 @@ namespace ColdMint.scripts;
public static class Config public static class Config
{ {
/// <summary>
/// <para>ID of the behavior tree</para>
/// <para>行为树的ID</para>
/// </summary>
public static class BehaviorTreeId
{
/// <summary>
/// <para>巡逻</para>
/// <para>Patrol</para>
/// </summary>
public const string Patrol = "Patrol";
}
/// <summary> /// <summary>
/// <para>Loot table ID</para> /// <para>Loot table ID</para>
/// <para>战利品表ID</para> /// <para>战利品表ID</para>
@ -34,31 +21,6 @@ public static class Config
public const string Test = "test"; public const string Test = "test";
} }
/// <summary>
/// <para>BehaviorTreeResult</para>
/// <para>行为树的结果</para>
/// </summary>
public static class BehaviorTreeResult
{
/// <summary>
/// <para>Running</para>
/// <para>运行中</para>
/// </summary>
public const int Running = 0;
/// <summary>
/// <para>Success</para>
/// <para>成功</para>
/// </summary>
public const int Success = 1;
/// <summary>
/// <para>Failure</para>
/// <para>失败</para>
/// </summary>
public const int Failure = 2;
}
/// <summary> /// <summary>
/// <para>Camp ID</para> /// <para>Camp ID</para>

View File

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

View File

@ -240,14 +240,12 @@ public partial class CharacterTemplate : CharacterBody2D
foreach (var pickingRangeBody in PickingRangeBodies) foreach (var pickingRangeBody in PickingRangeBodies)
{ {
if (pickingRangeBody is not WeaponTemplate weaponTemplate) continue; if (pickingRangeBody is not WeaponTemplate weaponTemplate) continue;
if (weaponTemplate.Owner != null) if (weaponTemplate.Picked)
{ {
continue; continue;
} }
weaponTemplates.Add(weaponTemplate); weaponTemplates.Add(weaponTemplate);
} }
return weaponTemplates.ToArray(); return weaponTemplates.ToArray();
} }
@ -308,7 +306,7 @@ public partial class CharacterTemplate : CharacterBody2D
/// </summary> /// </summary>
/// <param name="node"></param> /// <param name="node"></param>
/// <returns></returns> /// <returns></returns>
private bool CanPickItem(Node node) protected bool CanPickItem(Node node)
{ {
if (_currentItem != null && node == _currentItem) if (_currentItem != null && node == _currentItem)
{ {
@ -336,7 +334,7 @@ public partial class CharacterTemplate : CharacterBody2D
///<para>Whether successfully picked up</para> ///<para>Whether successfully picked up</para>
///<para>是否成功拾起</para> ///<para>是否成功拾起</para>
/// </returns> /// </returns>
protected bool PickItem(Node2D? pickAbleItemNode2D) public bool PickItem(Node2D? pickAbleItemNode2D)
{ {
//Empty reference checking is implicitly performed here. //Empty reference checking is implicitly performed here.
//此处隐式的执行了空引用检查。 //此处隐式的执行了空引用检查。
@ -585,12 +583,12 @@ public partial class CharacterTemplate : CharacterBody2D
if (damageTemplate.Attacker is CharacterTemplate characterTemplate && if (damageTemplate.Attacker is CharacterTemplate characterTemplate &&
!string.IsNullOrEmpty(characterTemplate.CharacterName)) !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); characterTemplate.CharacterName);
} }
else else
{ {
LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat,CharacterName, LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat, CharacterName,
damageTemplate.Attacker.Name); damageTemplate.Attacker.Name);
} }
} }
@ -612,6 +610,7 @@ public partial class CharacterTemplate : CharacterBody2D
return; return;
} }
LogCat.Log("enter_the_picking_range_body");
PickingRangeBodiesList?.Add(node); PickingRangeBodiesList?.Add(node);
} }

View File

@ -17,498 +17,498 @@ namespace ColdMint.scripts.character;
/// </summary> /// </summary>
public partial class Player : CharacterTemplate public partial class Player : CharacterTemplate
{ {
private Control? _floatLabel; private Control? _floatLabel;
//Empty object projectile //Empty object projectile
//空的物品抛射线 //空的物品抛射线
private readonly Vector2[] _emptyVector2Array = [Vector2.Zero]; private readonly Vector2[] _emptyVector2Array = [Vector2.Zero];
//抛物线 //抛物线
private Line2D? _parabola; private Line2D? _parabola;
//用于检测玩家是否站在平台上的射线 //用于检测玩家是否站在平台上的射线
private RayCast2D? _platformDetectionRayCast2D; private RayCast2D? _platformDetectionRayCast2D;
private const float PromptTextDistance = 50; private const float PromptTextDistance = 50;
//抛出物品的飞行速度 //抛出物品的飞行速度
private float _throwingVelocity = Config.CellSize * 13; private float _throwingVelocity = Config.CellSize * 13;
//射线是否与平台碰撞 //射线是否与平台碰撞
private bool _collidingWithPlatform; 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) //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; private double _platformCollisionRecoveryTime = 0.2f;
public override void _Ready() public override void _Ready()
{ {
base._Ready(); base._Ready();
CharacterName = TranslationServerUtils.Translate("default_player_name"); CharacterName = TranslationServerUtils.Translate("default_player_name");
LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, LogCat.UploadFormat,ReadOnlyCharacterName, LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, LogCat.UploadFormat,ReadOnlyCharacterName,
GlobalPosition); GlobalPosition);
var floatLabelPackedScene = GD.Load<PackedScene>("res://prefab/ui/FloatLabel.tscn"); var floatLabelPackedScene = GD.Load<PackedScene>("res://prefab/ui/FloatLabel.tscn");
//Initializes the float label. //Initializes the float label.
//初始化悬浮标签。 //初始化悬浮标签。
_floatLabel = NodeUtils.InstantiatePackedScene<Control>(floatLabelPackedScene); _floatLabel = NodeUtils.InstantiatePackedScene<Control>(floatLabelPackedScene);
if (_floatLabel == null) if (_floatLabel == null)
{ {
throw new NullReferenceException(TranslationServer.Translate("float_label_instantiate_failed")); throw new NullReferenceException(TranslationServer.Translate("float_label_instantiate_failed"));
} }
_floatLabel.Hide(); _floatLabel.Hide();
NodeUtils.CallDeferredAddChild(this, _floatLabel); NodeUtils.CallDeferredAddChild(this, _floatLabel);
_parabola = GetNode<Line2D>("Parabola"); _parabola = GetNode<Line2D>("Parabola");
_platformDetectionRayCast2D = GetNode<RayCast2D>("PlatformDetectionRayCast"); _platformDetectionRayCast2D = GetNode<RayCast2D>("PlatformDetectionRayCast");
UpdateOperationTip(); UpdateOperationTip();
var healthBarUi = GameSceneNodeHolder.HealthBarUi; var healthBarUi = GameSceneNodeHolder.HealthBarUi;
if (healthBarUi != null) if (healthBarUi != null)
{ {
healthBarUi.MaxHp = MaxHp; healthBarUi.MaxHp = MaxHp;
healthBarUi.CurrentHp = CurrentHp; healthBarUi.CurrentHp = CurrentHp;
} }
} }
protected override void WhenBindItemContainer(IItemContainer? itemContainer) protected override void WhenBindItemContainer(IItemContainer? itemContainer)
{ {
if (itemContainer == null) if (itemContainer == null)
{ {
return; return;
} }
//Subscribe to events when the item container is bound to the player. //Subscribe to events when the item container is bound to the player.
//在物品容器与玩家绑定时订阅事件。 //在物品容器与玩家绑定时订阅事件。
itemContainer.SelectedItemSlotChangeEvent += SelectedItemSlotChangeEvent; itemContainer.SelectedItemSlotChangeEvent += SelectedItemSlotChangeEvent;
} }
public override void _ExitTree() public override void _ExitTree()
{ {
base._ExitTree(); base._ExitTree();
if (ProtectedItemContainer != null) if (ProtectedItemContainer != null)
{ {
//Unsubscribe to events when this object is destroyed. //Unsubscribe to events when this object is destroyed.
//此节点被销毁时,取消订阅事件。 //此节点被销毁时,取消订阅事件。
ProtectedItemContainer.SelectedItemSlotChangeEvent -= SelectedItemSlotChangeEvent; ProtectedItemContainer.SelectedItemSlotChangeEvent -= SelectedItemSlotChangeEvent;
} }
} }
private void SelectedItemSlotChangeEvent(SelectedItemSlotChangeEvent selectedItemSlotChangeEvent) private void SelectedItemSlotChangeEvent(SelectedItemSlotChangeEvent selectedItemSlotChangeEvent)
{ {
var item = selectedItemSlotChangeEvent.NewItemSlotNode?.GetItem(); var item = selectedItemSlotChangeEvent.NewItemSlotNode?.GetItem();
GameSceneNodeHolder.HideBackpackUiContainerIfVisible(); GameSceneNodeHolder.HideBackpackUiContainerIfVisible();
if (item is Node2D node2D) if (item is Node2D node2D)
{ {
CurrentItem = node2D; CurrentItem = node2D;
} }
else else
{ {
CurrentItem = null; CurrentItem = null;
} }
} }
/// <summary> /// <summary>
/// <para>Update operation prompt</para> /// <para>Update operation prompt</para>
/// <para>更新操作提示</para> /// <para>更新操作提示</para>
/// </summary> /// </summary>
private void UpdateOperationTip() private void UpdateOperationTip()
{ {
var operationTipLabel = GameSceneNodeHolder.OperationTipLabel; var operationTipLabel = GameSceneNodeHolder.OperationTipLabel;
if (operationTipLabel == null) if (operationTipLabel == null)
{ {
return; return;
} }
var operationTipBuilder = new StringBuilder(); var operationTipBuilder = new StringBuilder();
operationTipBuilder.Append("[color="); operationTipBuilder.Append("[color=");
operationTipBuilder.Append(Config.OperationTipActionColor); operationTipBuilder.Append(Config.OperationTipActionColor);
operationTipBuilder.Append(']'); operationTipBuilder.Append(']');
operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_left")[0].AsText())); operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_left")[0].AsText()));
operationTipBuilder.Append("[/color]"); operationTipBuilder.Append("[/color]");
operationTipBuilder.Append(TranslationServerUtils.Translate("action_move_left")); operationTipBuilder.Append(TranslationServerUtils.Translate("action_move_left"));
operationTipBuilder.Append(' '); operationTipBuilder.Append(' ');
operationTipBuilder.Append("[color="); operationTipBuilder.Append("[color=");
operationTipBuilder.Append(Config.OperationTipActionColor); operationTipBuilder.Append(Config.OperationTipActionColor);
operationTipBuilder.Append(']'); operationTipBuilder.Append(']');
operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_right")[0].AsText())); operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_right")[0].AsText()));
operationTipBuilder.Append("[/color]"); operationTipBuilder.Append("[/color]");
operationTipBuilder.Append(TranslationServerUtils.Translate("action_move_right")); operationTipBuilder.Append(TranslationServerUtils.Translate("action_move_right"));
operationTipBuilder.Append(' '); operationTipBuilder.Append(' ');
operationTipBuilder.Append("[color="); operationTipBuilder.Append("[color=");
operationTipBuilder.Append(Config.OperationTipActionColor); operationTipBuilder.Append(Config.OperationTipActionColor);
operationTipBuilder.Append(']'); operationTipBuilder.Append(']');
operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_up")[0].AsText())); operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_up")[0].AsText()));
operationTipBuilder.Append("[/color]"); operationTipBuilder.Append("[/color]");
operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump")); operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump"));
if (_collidingWithPlatform) if (_collidingWithPlatform)
{ {
operationTipBuilder.Append(' '); operationTipBuilder.Append(' ');
operationTipBuilder.Append("[color="); operationTipBuilder.Append("[color=");
operationTipBuilder.Append(Config.OperationTipActionColor); operationTipBuilder.Append(Config.OperationTipActionColor);
operationTipBuilder.Append(']'); operationTipBuilder.Append(']');
operationTipBuilder.Append( operationTipBuilder.Append(
TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_down")[0].AsText())); TranslationServerUtils.Translate(InputMap.ActionGetEvents("ui_down")[0].AsText()));
operationTipBuilder.Append("[/color]"); operationTipBuilder.Append("[/color]");
operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump_down")); operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump_down"));
} }
var nearestItem = FindTheNearestItem(); var nearestItem = FindTheNearestItem();
if (nearestItem != null) if (nearestItem != null)
{ {
operationTipBuilder.Append(' '); operationTipBuilder.Append(' ');
operationTipBuilder.Append("[color="); operationTipBuilder.Append("[color=");
operationTipBuilder.Append(Config.OperationTipActionColor); operationTipBuilder.Append(Config.OperationTipActionColor);
operationTipBuilder.Append(']'); operationTipBuilder.Append(']');
operationTipBuilder.Append( operationTipBuilder.Append(
TranslationServerUtils.Translate(InputMap.ActionGetEvents("pick_up")[0].AsText())); TranslationServerUtils.Translate(InputMap.ActionGetEvents("pick_up")[0].AsText()));
operationTipBuilder.Append("[/color]"); operationTipBuilder.Append("[/color]");
operationTipBuilder.Append(TranslationServerUtils.Translate("action_pick_up")); operationTipBuilder.Append(TranslationServerUtils.Translate("action_pick_up"));
if (nearestItem is IItem item) if (nearestItem is IItem item)
{ {
operationTipBuilder.Append(item.Name); operationTipBuilder.Append(item.Name);
} }
operationTipLabel.Text = operationTipBuilder.ToString(); operationTipLabel.Text = operationTipBuilder.ToString();
} }
if (CurrentItem != null) if (CurrentItem != null)
{ {
operationTipBuilder.Append(' '); operationTipBuilder.Append(' ');
operationTipBuilder.Append("[color="); operationTipBuilder.Append("[color=");
operationTipBuilder.Append(Config.OperationTipActionColor); operationTipBuilder.Append(Config.OperationTipActionColor);
operationTipBuilder.Append(']'); operationTipBuilder.Append(']');
operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("throw")[0].AsText())); operationTipBuilder.Append(TranslationServerUtils.Translate(InputMap.ActionGetEvents("throw")[0].AsText()));
operationTipBuilder.Append("[/color]"); operationTipBuilder.Append("[/color]");
operationTipBuilder.Append(TranslationServerUtils.Translate("action_throw")); operationTipBuilder.Append(TranslationServerUtils.Translate("action_throw"));
if (CurrentItem is IItem item) if (CurrentItem is IItem item)
{ {
operationTipBuilder.Append(TranslationServerUtils.Translate(item.Name)); operationTipBuilder.Append(TranslationServerUtils.Translate(item.Name));
operationTipBuilder.Append(' '); operationTipBuilder.Append(' ');
operationTipBuilder.Append("[color="); operationTipBuilder.Append("[color=");
operationTipBuilder.Append(Config.OperationTipActionColor); operationTipBuilder.Append(Config.OperationTipActionColor);
operationTipBuilder.Append(']'); operationTipBuilder.Append(']');
operationTipBuilder.Append( operationTipBuilder.Append(
TranslationServerUtils.Translate(InputMap.ActionGetEvents("use_item")[0].AsText())); TranslationServerUtils.Translate(InputMap.ActionGetEvents("use_item")[0].AsText()));
operationTipBuilder.Append("[/color]"); operationTipBuilder.Append("[/color]");
operationTipBuilder.Append(TranslationServerUtils.Translate("action_use_item")); operationTipBuilder.Append(TranslationServerUtils.Translate("action_use_item"));
operationTipBuilder.Append(TranslationServerUtils.Translate(item.Name)); operationTipBuilder.Append(TranslationServerUtils.Translate(item.Name));
} }
} }
operationTipLabel.Text = operationTipBuilder.ToString(); operationTipLabel.Text = operationTipBuilder.ToString();
} }
protected override void HookPhysicsProcess(ref Vector2 velocity, double delta) protected override void HookPhysicsProcess(ref Vector2 velocity, double delta)
{ {
//When the collision state between the platform detection ray and the platform changes //When the collision state between the platform detection ray and the platform changes
//在平台检测射线与平台碰撞状态改变时 //在平台检测射线与平台碰撞状态改变时
if (_platformDetectionRayCast2D != null && _platformDetectionRayCast2D.IsColliding() != _collidingWithPlatform) if (_platformDetectionRayCast2D != null && _platformDetectionRayCast2D.IsColliding() != _collidingWithPlatform)
{ {
//When the state changes, update the action hint //When the state changes, update the action hint
//当状态改变时,更新操作提示 //当状态改变时,更新操作提示
_collidingWithPlatform = _platformDetectionRayCast2D.IsColliding(); _collidingWithPlatform = _platformDetectionRayCast2D.IsColliding();
UpdateOperationTip(); UpdateOperationTip();
} }
//If the character is on the ground, give an upward velocity when the jump button is pressed //If the character is on the ground, give an upward velocity when the jump button is pressed
//如果角色正在地面上,按下跳跃键时,给予一个向上的速度 //如果角色正在地面上,按下跳跃键时,给予一个向上的速度
if (Input.IsActionJustPressed("ui_up") && IsOnFloor()) if (Input.IsActionJustPressed("ui_up") && IsOnFloor())
velocity.Y = JumpVelocity; velocity.Y = JumpVelocity;
//Moving left and right //Moving left and right
//左右移动 //左右移动
var axis = Input.GetAxis("ui_left", "ui_right"); var axis = Input.GetAxis("ui_left", "ui_right");
velocity.X = axis * Speed * Config.CellSize * ProtectedSpeedScale; velocity.X = axis * Speed * Config.CellSize * ProtectedSpeedScale;
//Use items //Use items
//使用物品 //使用物品
if (Input.IsActionPressed("use_item")) if (Input.IsActionPressed("use_item"))
{ {
UseItem(GetGlobalMousePosition()); UseItem(GetGlobalMousePosition());
} }
//Pick up an item //Pick up an item
//捡起物品 //捡起物品
if (Input.IsActionJustPressed("pick_up")) if (Input.IsActionJustPressed("pick_up"))
{ {
var pickAbleItem = FindTheNearestItem(); var pickAbleItem = FindTheNearestItem();
var success = PickItem(pickAbleItem); var success = PickItem(pickAbleItem);
if (success) if (success)
{ {
if (pickAbleItem != null) if (pickAbleItem != null)
{ {
PickingRangeBodiesList?.Remove(pickAbleItem); PickingRangeBodiesList?.Remove(pickAbleItem);
} }
RecycleFloatLabel(); RecycleFloatLabel();
} }
} }
if (Input.IsActionJustPressed("ui_down")) if (Input.IsActionJustPressed("ui_down"))
{ {
if (_collidingWithPlatform) 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 //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 键时,我们取消角色与平台的碰撞 //当角色站在平台上按下 ui_down 键时,我们取消角色与平台的碰撞
var timer = new Timer(); var timer = new Timer();
AddChild(timer); AddChild(timer);
timer.WaitTime = _platformCollisionRecoveryTime; timer.WaitTime = _platformCollisionRecoveryTime;
timer.OneShot = true; timer.OneShot = true;
timer.Start(); timer.Start();
timer.Timeout += () => timer.Timeout += () =>
{ {
SetCollisionMaskValue(Config.LayerNumber.Platform, true); SetCollisionMaskValue(Config.LayerNumber.Platform, true);
timer.QueueFree(); timer.QueueFree();
}; };
SetCollisionMaskValue(Config.LayerNumber.Platform, false); SetCollisionMaskValue(Config.LayerNumber.Platform, false);
} }
} }
//Display a parabola when an item is thrown //Display a parabola when an item is thrown
//抛出物品时,显示抛物线 //抛出物品时,显示抛物线
if (Input.IsActionPressed("throw")) if (Input.IsActionPressed("throw"))
{ {
if (_parabola == null) if (_parabola == null)
{ {
return; return;
} }
if (ItemMarker2D == null) if (ItemMarker2D == null)
{ {
//Cannot get the marked location of the item, then do not draw a line //Cannot get the marked location of the item, then do not draw a line
//无法获取物品的标记位置,那么不绘制线 //无法获取物品的标记位置,那么不绘制线
return; return;
} }
_parabola.Points = CurrentItem == null _parabola.Points = CurrentItem == null
? _emptyVector2Array ? _emptyVector2Array
: ParabolicUtils.ComputeParabolic(ItemMarker2D.Position, GetThrowVelocity(), Gravity, 0.1f); : ParabolicUtils.ComputeParabolic(ItemMarker2D.Position, GetThrowVelocity(), Gravity, 0.1f);
} }
//When you raise your hand, throw the object //When you raise your hand, throw the object
//抬起手时,抛出物品 //抬起手时,抛出物品
if (Input.IsActionJustReleased("throw")) if (Input.IsActionJustReleased("throw"))
{ {
if (ItemContainer == null) if (ItemContainer == null)
{ {
return; return;
} }
if (_parabola != null) if (_parabola != null)
{ {
_parabola.Points = [Vector2.Zero]; _parabola.Points = [Vector2.Zero];
} }
ThrowItem(ItemContainer.GetSelectIndex(), 1, GetThrowVelocity()); ThrowItem(ItemContainer.GetSelectIndex(), 1, GetThrowVelocity());
GameSceneNodeHolder.HideBackpackUiContainerIfVisible(); GameSceneNodeHolder.HideBackpackUiContainerIfVisible();
CurrentItem = null; CurrentItem = null;
} }
} }
protected override void WhenUpdateCurrentItem(Node2D? currentItem) protected override void WhenUpdateCurrentItem(Node2D? currentItem)
{ {
UpdateOperationTip(); UpdateOperationTip();
} }
/// <summary> /// <summary>
/// <para>当玩家手动抛出物品时,施加到物品上的速度值</para> /// <para>当玩家手动抛出物品时,施加到物品上的速度值</para>
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private Vector2 GetThrowVelocity() private Vector2 GetThrowVelocity()
{ {
//We take the mouse position, normalize it, and then multiply it by the distance the player can throw //We take the mouse position, normalize it, and then multiply it by the distance the player can throw
//我们拿到鼠标的位置,将其归一化处理,然后乘以玩家可扔出的距离 //我们拿到鼠标的位置,将其归一化处理,然后乘以玩家可扔出的距离
return GetLocalMousePosition().Normalized() * _throwingVelocity; return GetLocalMousePosition().Normalized() * _throwingVelocity;
} }
public override void _Process(double delta) public override void _Process(double delta)
{ {
if (!Visible) if (!Visible)
{ {
return; return;
} }
AimTheCurrentItemAtAPoint(GetGlobalMousePosition()); AimTheCurrentItemAtAPoint(GetGlobalMousePosition());
var itemMarker2DPosition = Vector2.Zero; var itemMarker2DPosition = Vector2.Zero;
if (ItemMarker2D != null) if (ItemMarker2D != null)
{ {
itemMarker2DPosition = ItemMarker2D.Position; itemMarker2DPosition = ItemMarker2D.Position;
} }
var axis = Input.GetAxis("ui_left", "ui_right"); var axis = Input.GetAxis("ui_left", "ui_right");
switch (axis) switch (axis)
{ {
case -1: case -1:
//Minus 1, we move to the left //Minus 1, we move to the left
//-1向左移动 //-1向左移动
FacingLeft = true; FacingLeft = true;
if (ItemMarker2D != null) if (ItemMarker2D != null)
{ {
itemMarker2DPosition.X = -ReadOnlyItemMarkerOriginalX; itemMarker2DPosition.X = -ReadOnlyItemMarkerOriginalX;
} }
Flip(); Flip();
break; break;
case 1: case 1:
//1, move to the right //1, move to the right
//1向右移动 //1向右移动
FacingLeft = false; FacingLeft = false;
if (ItemMarker2D != null) if (ItemMarker2D != null)
{ {
itemMarker2DPosition.X = ReadOnlyItemMarkerOriginalX; itemMarker2DPosition.X = ReadOnlyItemMarkerOriginalX;
} }
Flip(); Flip();
break; break;
} }
if (ItemMarker2D != null) if (ItemMarker2D != null)
{ {
ItemMarker2D.Position = itemMarker2DPosition; ItemMarker2D.Position = itemMarker2DPosition;
} }
} }
protected override void Flip() protected override void Flip()
{ {
base.Flip(); base.Flip();
//If there is a weapon, flip it too //If there is a weapon, flip it too
//如果有武器的话,也要翻转 //如果有武器的话,也要翻转
if (CurrentItem is PickAbleTemplate pickAbleTemplate) if (CurrentItem is PickAbleTemplate pickAbleTemplate)
{ {
pickAbleTemplate.Flip(FacingLeft); pickAbleTemplate.Flip(FacingLeft);
} }
} }
public override void Revive(int newHp) public override void Revive(int newHp)
{ {
base.Revive(newHp); base.Revive(newHp);
var healthBarUi = GameSceneNodeHolder.HealthBarUi; var healthBarUi = GameSceneNodeHolder.HealthBarUi;
if (healthBarUi != null) if (healthBarUi != null)
{ {
//The purpose of setting Hp to the current Hp is to cause the life bar to refresh. //The purpose of setting Hp to the current Hp is to cause the life bar to refresh.
//将Hp设置为当前Hp的目的是使生命条刷新。 //将Hp设置为当前Hp的目的是使生命条刷新。
healthBarUi.CurrentHp = CurrentHp; healthBarUi.CurrentHp = CurrentHp;
} }
} }
/// <summary> /// <summary>
/// <para>When the player dies</para> /// <para>When the player dies</para>
/// <para>当玩家死亡的时候</para> /// <para>当玩家死亡的时候</para>
/// </summary> /// </summary>
/// <param name="damageTemplate"></param> /// <param name="damageTemplate"></param>
protected override async Task OnDie(DamageTemplate damageTemplate) protected override async Task OnDie(DamageTemplate damageTemplate)
{ {
Hide(); Hide();
ProcessMode = ProcessModeEnum.Disabled; ProcessMode = ProcessModeEnum.Disabled;
if (EventManager.GameOverEvent == null) if (EventManager.GameOverEvent == null)
{ {
return; return;
} }
var gameOverEvent = new GameOverEvent(); var gameOverEvent = new GameOverEvent();
if (damageTemplate.Attacker != null) if (damageTemplate.Attacker != null)
{ {
gameOverEvent.DeathInfo = await DeathInfoGenerator.GenerateDeathInfo(this, damageTemplate.Attacker); gameOverEvent.DeathInfo = await DeathInfoGenerator.GenerateDeathInfo(this, damageTemplate.Attacker);
} }
EventManager.GameOverEvent.Invoke(gameOverEvent); EventManager.GameOverEvent.Invoke(gameOverEvent);
} }
protected override void EnterThePickingRangeBody(Node node) protected override void EnterThePickingRangeBody(Node node)
{ {
base.EnterThePickingRangeBody(node); base.EnterThePickingRangeBody(node);
if (CurrentItem == node) if (CurrentItem == node)
{ {
//If the node entering the pick range is the node held by the player, then return. //If the node entering the pick range is the node held by the player, then return.
//如果说进入拾捡范围的节点是玩家所持有的节点,那么返回。 //如果说进入拾捡范围的节点是玩家所持有的节点,那么返回。
return; return;
} }
if (node is not Node2D node2D) if (node is not Node2D node2D)
{ {
return; return;
} }
if (_floatLabel != null) if (_floatLabel != null)
{ {
if (node is not PickAbleTemplate pickAbleTemplate) if (node is not PickAbleTemplate pickAbleTemplate)
{ {
return; return;
} }
if (pickAbleTemplate.Picked) if (pickAbleTemplate.Picked)
{ {
//If the pickables are picked up, the label is not displayed. //If the pickables are picked up, the label is not displayed.
//如果可拾捡物被捡起了,那么不显示标签。 //如果可拾捡物被捡起了,那么不显示标签。
LogCat.LogWarning("pickable_picked_up"); LogCat.LogWarning("pickable_picked_up");
return; return;
} }
NodeUtils.CallDeferredReparent(node, _floatLabel); NodeUtils.CallDeferredReparent(node, _floatLabel);
var rotationDegreesNode2D = node2D.RotationDegrees; var rotationDegreesNode2D = node2D.RotationDegrees;
var rotationDegreesNode2DAbs = Math.Abs(rotationDegreesNode2D); var rotationDegreesNode2DAbs = Math.Abs(rotationDegreesNode2D);
_floatLabel.GlobalPosition = node2D.GlobalPosition; _floatLabel.GlobalPosition = node2D.GlobalPosition;
_floatLabel.Position = rotationDegreesNode2DAbs > 90 _floatLabel.Position = rotationDegreesNode2DAbs > 90
? new Vector2(0, PromptTextDistance) ? new Vector2(0, PromptTextDistance)
: new Vector2(0, -PromptTextDistance); : new Vector2(0, -PromptTextDistance);
_floatLabel.RotationDegrees = 0 - rotationDegreesNode2D; _floatLabel.RotationDegrees = 0 - rotationDegreesNode2D;
var label = _floatLabel.GetNode<Label>("Label"); var label = _floatLabel.GetNode<Label>("Label");
var stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
if (pickAbleTemplate.Owner is CharacterTemplate characterTemplate) if (pickAbleTemplate.Owner is CharacterTemplate characterTemplate)
{ {
stringBuilder.Append(characterTemplate.ReadOnlyCharacterName); stringBuilder.Append(characterTemplate.ReadOnlyCharacterName);
stringBuilder.Append(TranslationServerUtils.Translate("de")); stringBuilder.Append(TranslationServerUtils.Translate("de"));
} }
stringBuilder.Append(TranslationServerUtils.Translate(pickAbleTemplate.Name)); stringBuilder.Append(TranslationServerUtils.Translate(pickAbleTemplate.Name));
label.Text = stringBuilder.ToString(); label.Text = stringBuilder.ToString();
_floatLabel.Show(); _floatLabel.Show();
} }
UpdateOperationTip(); UpdateOperationTip();
} }
protected override void ExitThePickingRangeBody(Node node) protected override void ExitThePickingRangeBody(Node node)
{ {
base.ExitThePickingRangeBody(node); base.ExitThePickingRangeBody(node);
if (node is not Node2D) if (node is not Node2D)
{ {
return; return;
} }
RecycleFloatLabel(); RecycleFloatLabel();
UpdateOperationTip(); UpdateOperationTip();
} }
/// <summary> /// <summary>
/// <para>Recycle Float Label</para> /// <para>Recycle Float Label</para>
/// <para>回收悬浮标签</para> /// <para>回收悬浮标签</para>
/// </summary> /// </summary>
private void RecycleFloatLabel() private void RecycleFloatLabel()
{ {
if (_floatLabel == null) if (_floatLabel == null)
{ {
return; return;
} }
_floatLabel.Hide(); _floatLabel.Hide();
NodeUtils.CallDeferredReparent(this, _floatLabel); NodeUtils.CallDeferredReparent(this, _floatLabel);
} }
protected override void OnHit(DamageTemplate damageTemplate) protected override void OnHit(DamageTemplate damageTemplate)
{ {
base.OnHit(damageTemplate); base.OnHit(damageTemplate);
var healthBarUi = GameSceneNodeHolder.HealthBarUi; var healthBarUi = GameSceneNodeHolder.HealthBarUi;
if (healthBarUi != null) if (healthBarUi != null)
{ {
healthBarUi.CurrentHp = CurrentHp; healthBarUi.CurrentHp = CurrentHp;
} }
} }
} }

View File

@ -20,100 +20,100 @@ namespace ColdMint.scripts.loader.uiLoader;
/// </summary> /// </summary>
public partial class SplashScreenLoader : UiLoaderTemplate public partial class SplashScreenLoader : UiLoaderTemplate
{ {
private Label? _loadingLabel; private Label? _loadingLabel;
private PackedScene? _mainMenuScene; private PackedScene? _mainMenuScene;
private AnimationPlayer? _animationPlayer; private AnimationPlayer? _animationPlayer;
private string _startup = "startup"; private string _startup = "startup";
private Label? _nameLabel; private Label? _nameLabel;
public override void InitializeData() public override void InitializeData()
{ {
_mainMenuScene = GD.Load<PackedScene>("res://scenes/mainMenu.tscn"); _mainMenuScene = GD.Load<PackedScene>("res://scenes/mainMenu.tscn");
} }
public override void InitializeUi() public override void InitializeUi()
{ {
_nameLabel = GetNode<Label>("NameLabel"); _nameLabel = GetNode<Label>("NameLabel");
_loadingLabel = GetNode<Label>("loadingStateLabel"); _loadingLabel = GetNode<Label>("loadingStateLabel");
_animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer"); _animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
//Disable animation in Debug mode. //Disable animation in Debug mode.
//在Debug模式禁用动画。 //在Debug模式禁用动画。
if (Config.IsDebug()) if (Config.IsDebug())
{ {
_loadingLabel.Modulate = Colors.White; _loadingLabel.Modulate = Colors.White;
_nameLabel.Modulate = Colors.White; _nameLabel.Modulate = Colors.White;
AnimationFinished(_startup); AnimationFinished(_startup);
} }
else else
{ {
_animationPlayer.Play(_startup); _animationPlayer.Play(_startup);
_animationPlayer.AnimationFinished += AnimationFinished; _animationPlayer.AnimationFinished += AnimationFinished;
} }
} }
private async void AnimationFinished(StringName name) private async void AnimationFinished(StringName name)
{ {
await LoadingGlobalData(); await LoadingGlobalData();
if (_mainMenuScene == null) if (_mainMenuScene == null)
{ {
return; return;
} }
GetTree().ChangeSceneToPacked(_mainMenuScene); GetTree().ChangeSceneToPacked(_mainMenuScene);
} }
/// <summary> /// <summary>
/// <para>Load the game's global data</para> /// <para>Load the game's global data</para>
/// <para>加载游戏的全局数据</para> /// <para>加载游戏的全局数据</para>
/// </summary> /// </summary>
private async Task LoadingGlobalData() private async Task LoadingGlobalData()
{ {
//Loading App configuration //Loading App configuration
//加载App配置 //加载App配置
var appConfigData = AppConfig.LoadFromFile(); var appConfigData = AppConfig.LoadFromFile();
if (appConfigData != null) if (appConfigData != null)
{ {
AppConfig.ApplyAppConfig(appConfigData); AppConfig.ApplyAppConfig(appConfigData);
} }
//Set the minimum log level to Info in debug mode.(Print all logs) //Set the minimum log level to Info in debug mode.(Print all logs)
//在调试模式下将最小日志等级设置为Info。打印全部日志 //在调试模式下将最小日志等级设置为Info。打印全部日志
//Disable all logs in the release version. //Disable all logs in the release version.
//在发行版禁用所有日志。 //在发行版禁用所有日志。
LogCat.MinLogLevel = Config.IsDebug() ? LogCat.InfoLogLevel : LogCat.DisableAllLogLevel; LogCat.MinLogLevel = Config.IsDebug() ? LogCat.InfoLogLevel : LogCat.DisableAllLogLevel;
ContributorDataManager.RegisterAllContributorData(); ContributorDataManager.RegisterAllContributorData();
DeathInfoGenerator.RegisterDeathInfoHandler(new SelfDeathInfoHandler()); DeathInfoGenerator.RegisterDeathInfoHandler(new SelfDeathInfoHandler());
MapGenerator.RegisterRoomInjectionProcessor(new ChanceRoomInjectionProcessor()); MapGenerator.RegisterRoomInjectionProcessor(new ChanceRoomInjectionProcessor());
MapGenerator.RegisterRoomInjectionProcessor(new TimeIntervalRoomInjectorProcessor()); MapGenerator.RegisterRoomInjectionProcessor(new TimeIntervalRoomInjectorProcessor());
//Register the corresponding encoding provider to solve the problem of garbled Chinese path of the compressed package //Register the corresponding encoding provider to solve the problem of garbled Chinese path of the compressed package
//注册对应的编码提供程序,解决压缩包中文路径乱码问题 //注册对应的编码提供程序,解决压缩包中文路径乱码问题
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
//创建游戏数据文件夹 //创建游戏数据文件夹
var dataPath = Config.GetGameDataDirectory(); var dataPath = Config.GetGameDataDirectory();
if (!Directory.Exists(dataPath)) if (!Directory.Exists(dataPath))
{ {
Directory.CreateDirectory(dataPath); Directory.CreateDirectory(dataPath);
} }
//Registered camp //Registered camp
//注册阵营 //注册阵营
var defaultCamp = new Camp(Config.CampId.Default) var defaultCamp = new Camp(Config.CampId.Default)
{ {
FriendInjury = true FriendInjury = true
}; };
CampManager.SetDefaultCamp(defaultCamp); CampManager.SetDefaultCamp(defaultCamp);
var mazoku = new Camp(Config.CampId.Mazoku); var mazoku = new Camp(Config.CampId.Mazoku);
CampManager.AddCamp(mazoku); CampManager.AddCamp(mazoku);
var aborigines = new Camp(Config.CampId.Aborigines); var aborigines = new Camp(Config.CampId.Aborigines);
CampManager.AddCamp(aborigines); CampManager.AddCamp(aborigines);
//Register ItemTypes from file //Register ItemTypes from file
//从文件注册物品类型 //从文件注册物品类型
ItemTypeRegister.RegisterFromFile(); ItemTypeRegister.RegisterFromFile();
//Hardcoded ItemTypes Register //Hardcoded ItemTypes Register
//硬编码注册物品类型 //硬编码注册物品类型
ItemTypeRegister.StaticRegister(); ItemTypeRegister.StaticRegister();
//静态注册掉落表 //静态注册掉落表
LootRegister.StaticRegister(); LootRegister.StaticRegister();
await Task.Delay(TimeSpan.FromMilliseconds(500)); await Task.Delay(TimeSpan.FromMilliseconds(500));
} }
} }

View File

@ -27,7 +27,51 @@ public class LookForWeaponProcessor : StateProcessorTemplate
{ {
//If the nearest weapon is found, move the character to the weapon. //If the nearest weapon is found, move the character to the weapon.
//如果有最近的武器被找到了,那么将角色移动到武器旁边。 //如果有最近的武器被找到了,那么将角色移动到武器旁边。
aiCharacter.SetTargetPosition(TargetWeapon.GlobalPosition); var weaponTemplates = aiCharacter.GetCanPickedWeapon();
//Weapons are not in the range of the pickup.
//武器没在拾捡范围内。
if (weaponTemplates.Length == 0)
{
LogCat.Log("weapon_not_in_pickup_range", LogCat.LogLabel.LookForWeaponProcessor);
aiCharacter.SetTargetPosition(TargetWeapon.GlobalPosition);
}
else
{
var haveWeapon = false;
foreach (var weaponTemplate in weaponTemplates)
{
if (weaponTemplate == TargetWeapon)
{
haveWeapon = true;
}
}
if (haveWeapon)
{
var pickResult = aiCharacter.PickItem(TargetWeapon);
if (pickResult)
{
context.CurrentState = context.PreviousState;
//Successfully picked up the weapon.
//成功拾起武器。
LogCat.Log("weapon_picked_up", LogCat.LogLabel.LookForWeaponProcessor);
}
else
{
TargetWeapon = null;
//Weapon failed to pick up.
//武器捡起时失败。
LogCat.Log("weapon_pickup_failed", LogCat.LogLabel.LookForWeaponProcessor);
}
}
else
{
//No weapons are included in the pickup area.
//拾捡范围内不包含武器。
LogCat.Log("weapon_not_in_pickup_range", LogCat.LogLabel.LookForWeaponProcessor);
aiCharacter.SetTargetPosition(TargetWeapon.GlobalPosition);
}
}
return; return;
} }