Compare commits

..

No commits in common. "5ca995d689985ebe41135be82575d233cdd7b140" and "fac2047f795856cdc1957e688933f2334aeb0ea2" have entirely different histories.

12 changed files with 116 additions and 258 deletions

View File

@ -15,6 +15,11 @@ public partial class BehaviorNode : Node2D
InvokeBehaviorTreeNode(true, delta);
}
// public override void _Process(double delta)
// {
// InvokeBehaviorTreeNode(false, delta);
// }
/// <summary>
/// <para>InvokeBehaviorTreeNode</para>
/// <para>调用行为树节点</para>

View File

@ -1,9 +1,5 @@
namespace ColdMint.scripts.behaviorTree;
/// <summary>
/// <para>IBehavior Tree</para>
/// <para>行为树</para>
/// </summary>
public interface IBehaviorTree
{
string? Id { get; }

View File

@ -3,10 +3,6 @@ using ColdMint.scripts.character;
namespace ColdMint.scripts.behaviorTree.ai;
/// <summary>
/// <para>AI attack node</para>
/// <para>AI的攻击节点</para>
/// </summary>
public class AiAttackNode : BehaviorTreeNodeTemplate
{
public AiCharacter? Character { get; set; }
@ -33,49 +29,47 @@ public class AiAttackNode : BehaviorTreeNodeTemplate
var selfCamp = CampManager.GetCamp(Character.CampId);
foreach (var node in nodesInTheAttackRange)
{
if (node is not CharacterTemplate characterTemplate)
if (node is CharacterTemplate characterTemplate)
{
continue;
}
if (node == Character)
{
continue;
}
if (node == Character)
{
continue;
}
var characterCamp = CampManager.GetCamp(characterTemplate.CampId);
var canCause = CampManager.CanCauseHarm(selfCamp, characterCamp);
if (!canCause)
{
continue;
}
var characterCamp = CampManager.GetCamp(characterTemplate.CampId);
var canCause = CampManager.CanCauseHarm(selfCamp, characterCamp);
if (!canCause)
{
continue;
}
if (selfCamp == null || characterCamp == null)
{
continue;
}
if (selfCamp.Id == characterCamp.Id)
{
//If it is the same side, do not attack, if allowed friend damage, this code will prevent the AI from actively attacking the player.
//如果是同一阵营不攻击如果允许友伤这段代码会阻止AI主动攻击玩家。
continue;
}
if (selfCamp == null || characterCamp == null)
{
continue;
}
var distance = characterTemplate.GlobalPosition.DistanceTo(Character.GlobalPosition);
if (distance < closestDistance)
{
closestDistance = distance;
closestEnemy = characterTemplate;
if (selfCamp.Id == characterCamp.Id)
{
//如果是同一阵营,不攻击
continue;
}
var distance = characterTemplate.GlobalPosition - Character.GlobalPosition;
var distanceLength = distance.Length();
if (distanceLength < closestDistance)
{
closestDistance = distanceLength;
closestEnemy = characterTemplate;
}
}
}
if (closestEnemy != null && Character.AttackObstacleDetection != null)
{
//With the nearest enemy and no obstacles
//有距离最近的敌人,且没有障碍物
var distanceVector2 = closestEnemy.GlobalPosition - Character.GlobalPosition;
Character.AttackObstacleDetection.TargetPosition = distanceVector2;
//There are the closest enemies
//有距离最近的敌人
var distance = closestEnemy.GlobalPosition - Character.GlobalPosition;
Character.AttackObstacleDetection.TargetPosition = distance;
if (Character.AttackObstacleDetection.GetCollider() == null)
{
Character.StopMoving();

View File

@ -4,7 +4,6 @@ using ColdMint.scripts.character;
namespace ColdMint.scripts.behaviorTree.ai;
/// <summary>
/// <para>AI patrol node</para>
/// <para>AI巡逻节点</para>
/// </summary>
public class AiPatrolNode : SelectorNode

View File

@ -14,7 +14,7 @@ public class SequenceNode : BehaviorTreeNodeTemplate
/// <para>Check whether all child nodes are executed in sequence</para>
/// <para>所有子节点是否按顺序执行完毕</para>
/// </summary>
private bool _complete = true;
bool _complete = true;
public override int Execute(bool isPhysicsProcess, double delta)
{

View File

@ -8,11 +8,12 @@ namespace ColdMint.scripts.camp;
/// </summary>
public class Camp
{
private readonly string _id;
private readonly List<string> _friendlyCampIdList;
public Camp(string id)
{
Id = id;
_id = id;
_friendlyCampIdList = new List<string>();
}
@ -20,7 +21,7 @@ public class Camp
/// <para>Get camp ID</para>
/// <para>获取阵营ID</para>
/// </summary>
public string Id { get; }
public string Id => _id;
/// <summary>
/// <para>Get camp name</para>

View File

@ -6,7 +6,7 @@ namespace ColdMint.scripts.camp;
/// <para>Camp manager</para>
/// <para>阵营管理器</para>
/// </summary>
public static class CampManager
public class CampManager
{
private static readonly Dictionary<string, Camp?> Camps = new Dictionary<string, Camp?>();

View File

@ -9,7 +9,7 @@ namespace ColdMint.scripts.character;
/// <para>The role played by computers</para>
/// <para>由电脑扮演的角色</para>
/// </summary>
public sealed partial class AiCharacter : CharacterTemplate
public partial class AiCharacter : CharacterTemplate
{
/// <summary>
/// <para>How fast the character moves</para>
@ -67,11 +67,10 @@ public sealed partial class AiCharacter : CharacterTemplate
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
//其他区域不能检测到我们的攻击区域
//Other regions cannot detect our pick region
//其他区域不能检测到我们的拾取区域
_attackArea.Monitorable = false;
_attackArea.BodyEntered += EnterTheAttackArea;
_attackArea.BodyExited += ExitTheAttackArea;
@ -84,12 +83,12 @@ public sealed partial class AiCharacter : CharacterTemplate
// _behaviorNode.Root = patrolBehaviorTree.Root;
}
private void EnterTheAttackArea(Node node)
protected virtual void EnterTheAttackArea(Node node)
{
_nodesInTheAttackRange?.Add(node);
}
private void ExitTheAttackArea(Node node)
protected virtual void ExitTheAttackArea(Node node)
{
_nodesInTheAttackRange?.Remove(node);
}

View File

@ -6,6 +6,7 @@ using ColdMint.scripts.damage;
using ColdMint.scripts.debug;
using ColdMint.scripts.health;
using ColdMint.scripts.inventory;
using ColdMint.scripts.map.events;
using ColdMint.scripts.utils;
using ColdMint.scripts.weapon;
using Godot;
@ -29,9 +30,6 @@ public partial class CharacterTemplate : CharacterBody2D
protected const float JumpVelocity = -240;
//物品被扔出后多长时间恢复与地面和平台的碰撞(单位:秒)
private readonly double _itemCollisionRecoveryTime = 0.045f;
public string? ReadOnlyCharacterName => CharacterName;
protected string? CharacterName;
@ -261,9 +259,7 @@ public partial class CharacterTemplate : CharacterBody2D
/// </returns>
public bool PickItem(Node2D? pickAbleItem)
{
//Empty reference checking is implicitly performed here.
//此处隐式的执行了空引用检查。
if (pickAbleItem is not IItem item)
if (pickAbleItem == null)
{
return false;
}
@ -281,6 +277,11 @@ public partial class CharacterTemplate : CharacterBody2D
return false;
}
if (pickAbleItem is not IItem item)
{
return false;
}
//First check if we can pick up the item.
//先检查我们能否拾起此物品。
var canPick = _itemContainer.CanAddItem(item);
@ -367,7 +368,7 @@ public partial class CharacterTemplate : CharacterBody2D
/// <para>Update the role's health bar</para>
/// <para>更新角色的健康条</para>
/// </summary>
private void UpDataHealthBar()
private void UpDataHealthBar(DamageTemplate damageTemplate)
{
if (_healthBar == null)
{
@ -440,7 +441,7 @@ public partial class CharacterTemplate : CharacterBody2D
return true;
}
UpDataHealthBar();
UpDataHealthBar(damageTemplate);
return true;
}
@ -528,86 +529,6 @@ public partial class CharacterTemplate : CharacterBody2D
_animatedSprite2D.FlipH = FacingLeft;
}
/// <summary>
/// <para>Throw item</para>
/// <para>抛出物品</para>
/// </summary>
/// <param name="index">
///<para>Item slot subscript in item container</para>
///<para>物品容器内的物品槽下标</para>
/// </param>
/// <param name="number">
/// <para>How many to throw</para>
/// <para>要扔几个</para>
/// </param>
/// <param name="velocity">
///<para>The speed to be applied to the item</para>
///<para>要施加到物品上的速度</para>
/// </param>
protected void ThrowItem(int index, int number, Vector2 velocity)
{
if (_itemContainer == null)
{
return;
}
var itemSlotNode = _itemContainer.GetItemSlotNode(index);
if (itemSlotNode == null)
{
return;
}
var item = itemSlotNode.GetItem();
if (item == null)
{
return;
}
switch (item)
{
case WeaponTemplate weaponTemplate:
if (GameSceneNodeHolder.WeaponContainer == null)
{
return;
}
weaponTemplate.Reparent(GameSceneNodeHolder.WeaponContainer);
var timer = new Timer();
weaponTemplate.AddChild(timer);
timer.WaitTime = _itemCollisionRecoveryTime;
timer.OneShot = true;
timer.Timeout += () =>
{
//We cannot immediately resume the physical collision when the weapon is discharged, which will cause the weapon to collide with the ground and platform earlier, preventing the weapon from flying.
//仍出武器时,我们不能立即恢复物理碰撞,立即恢复会导致武器更早的与地面和平台碰撞,阻止武器的飞行。
weaponTemplate.EnableContactInjury = true;
weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, true);
weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, true);
timer.QueueFree();
};
timer.Start();
weaponTemplate.Sleeping = false;
// weaponTemplate.LinearVelocity = Vector2.Zero;
break;
}
//We apply force to objects.
//我们给物品施加力。
switch (CurrentItem)
{
case CharacterBody2D characterBody2D:
characterBody2D.Velocity = velocity;
break;
case RigidBody2D rigidBody2D:
rigidBody2D.LinearVelocity = velocity;
break;
}
//Remove items from the item container
//在物品容器内移除物品
itemSlotNode.RemoveItem(number);
}
public sealed override void _PhysicsProcess(double delta)
{

View File

@ -43,6 +43,9 @@ public partial class Player : CharacterTemplate
//角色从平台上跳下后,多少时间后恢复与平台的碰撞(单位:秒)
private double _platformCollisionRecoveryTime = 0.2f;
//物品被扔出后多长时间恢复与地面和平台的碰撞(单位:秒)
private readonly double _itemCollisionRecoveryTime = 0.045f;
public override void _Ready()
{
@ -252,7 +255,7 @@ public partial class Player : CharacterTemplate
//抬起手时,抛出物品
if (Input.IsActionJustReleased("throw"))
{
if (ItemContainer == null)
if (CurrentItem == null)
{
return;
}
@ -262,8 +265,46 @@ public partial class Player : CharacterTemplate
_parabola.Points = new[] { Vector2.Zero };
}
ThrowItem(ItemContainer.GetSelectIndex(), 1, GetThrowVelocity());
CurrentItem.Reparent(GameSceneNodeHolder.WeaponContainer);
switch (CurrentItem)
{
case WeaponTemplate weaponTemplate:
{
var timer = new Timer();
weaponTemplate.AddChild(timer);
timer.WaitTime = _itemCollisionRecoveryTime;
timer.OneShot = true;
timer.Timeout += () =>
{
//We cannot immediately resume the physical collision when the weapon is discharged, which will cause the weapon to collide with the ground and platform earlier, preventing the weapon from flying.
//仍出武器时,我们不能立即恢复物理碰撞,立即恢复会导致武器更早的与地面和平台碰撞,阻止武器的飞行。
weaponTemplate.EnableContactInjury = true;
weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, true);
weaponTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, true);
timer.QueueFree();
};
timer.Start();
weaponTemplate.Sleeping = false;
weaponTemplate.LinearVelocity = Vector2.Zero;
break;
}
}
//We apply force to objects.
//我们给物品施加力。
switch (CurrentItem)
{
case CharacterBody2D characterBody2D:
characterBody2D.Velocity = GetThrowVelocity();
break;
case RigidBody2D rigidBody2D:
rigidBody2D.LinearVelocity = GetThrowVelocity();
break;
}
CurrentItem = null;
var hotBar = GameSceneNodeHolder.HotBar;
hotBar?.RemoveItemFromItemSlotBySelectIndex(1);
}
}
@ -272,10 +313,6 @@ public partial class Player : CharacterTemplate
UpdateOperationTip();
}
/// <summary>
/// <para>当玩家手动抛出物品时,施加到物品上的速度值</para>
/// </summary>
/// <returns></returns>
private Vector2 GetThrowVelocity()
{
//We take the mouse position, normalize it, and then multiply it by the distance the player can throw
@ -345,7 +382,7 @@ public partial class Player : CharacterTemplate
{
base.Revive(newHp);
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.
//将Hp设置为当前Hp的目的是使生命条刷新。

View File

@ -14,12 +14,6 @@ public partial class HotBar : HBoxContainer, IItemContainer
private List<ItemSlotNode>? _itemSlotNodes;
/// <summary>
/// <para>UnknownIndex</para>
/// <para>未知位置</para>
/// </summary>
private const int UnknownIndex = -1;
//_selectIndex默认为0.
private int _selectIndex;
@ -85,7 +79,7 @@ public partial class HotBar : HBoxContainer, IItemContainer
{
_selectIndex = count - 1;
}
SelectItemSlot(oldSelectIndex, _selectIndex);
}
@ -166,14 +160,17 @@ public partial class HotBar : HBoxContainer, IItemContainer
return;
}
var safeIndex = GetSafeIndex(shortcutKeyIndex);
if (safeIndex == UnknownIndex)
var count = _itemSlotNodes.Count;
if (count == 0)
{
//Prevents the dividend from being 0
//防止被除数为0
return;
}
SelectItemSlot(_selectIndex, safeIndex);
_selectIndex = safeIndex;
var newIndex = shortcutKeyIndex % count;
SelectItemSlot(_selectIndex, newIndex);
_selectIndex = newIndex;
}
@ -188,27 +185,6 @@ public partial class HotBar : HBoxContainer, IItemContainer
return RemoveItemFromItemSlot(_selectIndex, number);
}
public int GetItemSlotCount()
{
if (_itemSlotNodes == null)
{
return 0;
}
return _itemSlotNodes.Count;
}
public ItemSlotNode? GetItemSlotNode(int index)
{
if (_itemSlotNodes == null)
{
return null;
}
var safeIndex = GetSafeIndex(index);
return _itemSlotNodes[safeIndex];
}
/// <summary>
/// <para>Remove items from the item slot</para>
/// <para>从物品槽内移除物品</para>
@ -228,41 +204,17 @@ public partial class HotBar : HBoxContainer, IItemContainer
return false;
}
var safeIndex = GetSafeIndex(itemSlotIndex);
if (safeIndex == UnknownIndex)
{
return false;
}
var itemSlot = _itemSlotNodes[safeIndex];
return itemSlot.RemoveItem(number);
}
/// <summary>
/// <para>Gets a secure subscript index</para>
/// <para>获取安全的下标索引</para>
/// </summary>
/// <param name="itemSlotIndex"></param>
/// <returns>
///<para>-1 is returned on failure, and the index that does not result in an out-of-bounds subscript is returned on success</para>
///<para>失败返回-1成功返回不会导致下标越界的索引</para>
/// </returns>
private int GetSafeIndex(int itemSlotIndex)
{
if (_itemSlotNodes == null)
{
return UnknownIndex;
}
var count = _itemSlotNodes.Count;
if (count == 0)
{
//Prevents the dividend from being 0
//防止被除数为0
return UnknownIndex;
return false;
}
return itemSlotIndex % count;
var newIndex = itemSlotIndex % count;
var itemSlot = _itemSlotNodes[newIndex];
return itemSlot.RemoveItem(number);
}
/// <summary>
@ -271,11 +223,6 @@ public partial class HotBar : HBoxContainer, IItemContainer
/// </summary>
private void SelectItemSlot(int oldSelectIndex, int newSelectIndex)
{
if (oldSelectIndex == newSelectIndex)
{
return;
}
if (_itemSlotNodes == null)
{
return;
@ -339,12 +286,10 @@ public partial class HotBar : HBoxContainer, IItemContainer
{
return false;
}
return itemSlotNode.SetItem(item);
}
public int GetSelectIndex()
{
return _selectIndex;
else
{
return itemSlotNode.SetItem(item);
}
}
public ItemSlotNode? GetSelectItemSlotNode()

View File

@ -26,13 +26,6 @@ public interface IItemContainer
/// <returns></returns>
bool AddItem(IItem item);
/// <summary>
/// <para>Gets the selected location</para>
/// <para>获取选中的位置</para>
/// </summary>
/// <returns></returns>
int GetSelectIndex();
/// <summary>
/// <para>Gets the currently selected node</para>
/// <para>获取当前选中的节点</para>
@ -40,38 +33,6 @@ public interface IItemContainer
/// <returns></returns>
ItemSlotNode? GetSelectItemSlotNode();
/// <summary>
/// <para>Removes an item from the inventory at the currently selected location</para>
/// <para>移除当前选中位置物品栏内的物品</para>
/// </summary>
/// <param name="number"></param>
/// <returns></returns>
bool RemoveItemFromItemSlotBySelectIndex(int number);
/// <summary>
/// <para>Gets the number of item slots</para>
/// <para>获取物品槽的数量</para>
/// </summary>
/// <returns></returns>
int GetItemSlotCount();
/// <summary>
/// <para>Gets the item slot for the specified location</para>
/// <para>获取指定位置的物品槽</para>
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
ItemSlotNode? GetItemSlotNode(int index);
/// <summary>
/// <para>Removes an item from the item slot in the specified location</para>
/// <para>在指定位置的物品槽内移除物品</para>
/// </summary>
/// <param name="itemSlotIndex"></param>
/// <param name="number"></param>
/// <returns></returns>
bool RemoveItemFromItemSlot(int itemSlotIndex, int number);
/// <summary>
/// <para>Based on the given item, match the item slots where it can be placed</para>
/// <para>根据给定的物品,匹配可放置它的物品槽</para>