Improve empty references and improve code robustness.
改善空引用,提升代码健壮性。
This commit is contained in:
parent
1c116eb820
commit
222d26441e
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=10 format=3 uid="uid://cj65pso40syj5"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/character/AICharacter.cs" id="1_hym0i"]
|
||||
[ext_resource type="Script" path="res://scripts/character/AiCharacter.cs" id="1_ubaid"]
|
||||
[ext_resource type="Texture2D" uid="uid://b1twcink38sh0" path="res://sprites/Player.png" id="2_eha68"]
|
||||
[ext_resource type="Script" path="res://scripts/damage/DamageNumberNodeSpawn.cs" id="3_kiam3"]
|
||||
[ext_resource type="PackedScene" uid="uid://sqqfrmikmk5v" path="res://prefab/ui/HealthBar.tscn" id="4_gt388"]
|
||||
|
@ -30,7 +30,7 @@ radius = 129.027
|
|||
[node name="DelivererOfDarkMagic" type="CharacterBody2D"]
|
||||
collision_layer = 64
|
||||
collision_mask = 38
|
||||
script = ExtResource("1_hym0i")
|
||||
script = ExtResource("1_ubaid")
|
||||
metadata/CampId = "Mazoku"
|
||||
metadata/MaxHp = 99999
|
||||
metadata/Name = "死灵法师"
|
||||
|
|
|
@ -14,7 +14,7 @@ public static class Config
|
|||
/// <para>巡逻</para>
|
||||
/// <para>Patrol</para>
|
||||
/// </summary>
|
||||
public const string Patrol = "Patrol";
|
||||
public const string? Patrol = "Patrol";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -20,11 +20,11 @@ public static class GameSceneNodeHolder
|
|||
/// <para>WeaponContainer</para>
|
||||
/// <para>武器容器</para>
|
||||
/// </summary>
|
||||
public static Node2D WeaponContainer { get; set; }
|
||||
public static Node2D? WeaponContainer { get; set; }
|
||||
|
||||
public static HotBar HotBar { get; set; }
|
||||
public static HotBar? HotBar { get; set; }
|
||||
|
||||
|
||||
public static HealthBarUi HealthBarUi { get; set; }
|
||||
public static Label OperationTipLabel { get; set; }
|
||||
public static HealthBarUi? HealthBarUi { get; set; }
|
||||
public static Label? OperationTipLabel { get; set; }
|
||||
}
|
|
@ -114,7 +114,7 @@ public partial class HealthBarUi : HBoxContainer
|
|||
/// </summary>
|
||||
/// <param name="percent"></param>
|
||||
/// <returns></returns>
|
||||
private Texture2D GetTexture2DByPercent(float percent)
|
||||
private Texture2D? GetTexture2DByPercent(float percent)
|
||||
{
|
||||
if (percent == 0)
|
||||
{
|
||||
|
@ -139,11 +139,11 @@ public partial class HealthBarUi : HBoxContainer
|
|||
}
|
||||
}
|
||||
|
||||
private Texture2D _heartFull;
|
||||
private Texture2D _heartEmpty;
|
||||
private Texture2D _heartHalf;
|
||||
private Texture2D _heartQuarter;
|
||||
private Texture2D _heartThreeFourths;
|
||||
private Texture2D? _heartFull;
|
||||
private Texture2D? _heartEmpty;
|
||||
private Texture2D? _heartHalf;
|
||||
private Texture2D? _heartQuarter;
|
||||
private Texture2D? _heartThreeFourths;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ public static class SloganProvider
|
|||
/// <para>Define how many banners you want to display</para>
|
||||
/// <para>定义共有多少条标语需要展示</para>
|
||||
/// </summary>
|
||||
private const int total = 12;
|
||||
private const int Total = 12;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Swipe the machine to get a slogan</para>
|
||||
|
@ -21,6 +21,6 @@ public static class SloganProvider
|
|||
/// <returns></returns>
|
||||
public static string GetSlogan()
|
||||
{
|
||||
return TranslationServer.Translate("slogan_" + GD.RandRange(1, total));
|
||||
return TranslationServer.Translate("slogan_" + GD.RandRange(1, Total));
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ namespace ColdMint.scripts.behaviorTree;
|
|||
/// </summary>
|
||||
public partial class BehaviorNode : Node2D
|
||||
{
|
||||
public IBehaviorTreeNode Root { get; set; }
|
||||
public IBehaviorTreeNode? Root { get; set; }
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace ColdMint.scripts.behaviorTree;
|
|||
/// </summary>
|
||||
public abstract class BehaviorTreeNodeTemplate : IBehaviorTreeNode
|
||||
{
|
||||
private List<IBehaviorTreeNode> _children = new List<IBehaviorTreeNode>();
|
||||
private readonly List<IBehaviorTreeNode> _children = new List<IBehaviorTreeNode>();
|
||||
|
||||
public void AddChild(IBehaviorTreeNode child)
|
||||
{
|
||||
|
@ -29,7 +29,7 @@ public abstract class BehaviorTreeNodeTemplate : IBehaviorTreeNode
|
|||
/// <param name="defaultT"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public T GetChild<T>(T defaultT)
|
||||
protected T? GetChild<T>(T? defaultT)
|
||||
{
|
||||
if (_children.Count == 0)
|
||||
{
|
||||
|
@ -49,6 +49,6 @@ public abstract class BehaviorTreeNodeTemplate : IBehaviorTreeNode
|
|||
|
||||
public abstract int Execute(bool isPhysicsProcess, double delta);
|
||||
|
||||
public IBehaviorTreeNode Parent { get; set; }
|
||||
public IBehaviorTreeNode? Parent { get; set; }
|
||||
public IBehaviorTreeNode[] Children => _children.ToArray();
|
||||
}
|
|
@ -6,18 +6,18 @@
|
|||
/// </summary>
|
||||
public abstract class BehaviorTreeTemplate : IBehaviorTree
|
||||
{
|
||||
private IBehaviorTreeNode _root;
|
||||
private string _id;
|
||||
public IBehaviorTreeNode Root => _root;
|
||||
public string ID => _id;
|
||||
private IBehaviorTreeNode? _root;
|
||||
private string? _id;
|
||||
public IBehaviorTreeNode? Root => _root;
|
||||
public string? Id => _id;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
_root = CreateRoot();
|
||||
_id = CreateID();
|
||||
_id = CreateId();
|
||||
}
|
||||
|
||||
protected abstract IBehaviorTreeNode CreateRoot();
|
||||
protected abstract IBehaviorTreeNode? CreateRoot();
|
||||
|
||||
protected abstract string CreateID();
|
||||
protected abstract string? CreateId();
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
public interface IBehaviorTree
|
||||
{
|
||||
string ID { get; }
|
||||
string? Id { get; }
|
||||
|
||||
IBehaviorTreeNode Root { get; }
|
||||
IBehaviorTreeNode? Root { get; }
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using Godot;
|
||||
|
||||
|
||||
namespace ColdMint.scripts.behaviorTree;
|
||||
|
||||
/// <summary>
|
||||
|
@ -22,7 +21,7 @@ public interface IBehaviorTreeNode
|
|||
/// <para>The parent of this node</para>
|
||||
/// <para>此节点的父节点</para>
|
||||
/// </summary>
|
||||
IBehaviorTreeNode Parent { get; set; }
|
||||
IBehaviorTreeNode? Parent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>child node</para>
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
using ColdMint.scripts.camp;
|
||||
using ColdMint.scripts.character;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.behaviorTree.ai;
|
||||
|
||||
public class AIAttackNode : BehaviorTreeNodeTemplate
|
||||
public class AiAttackNode : BehaviorTreeNodeTemplate
|
||||
{
|
||||
public AICharacter Character { get; set; }
|
||||
public AiCharacter? Character { get; set; }
|
||||
|
||||
public override int Execute(bool isPhysicsProcess, double delta)
|
||||
{
|
||||
|
@ -25,7 +24,7 @@ public class AIAttackNode : BehaviorTreeNodeTemplate
|
|||
|
||||
//Save the nearest enemy
|
||||
//保存最近的敌人
|
||||
CharacterTemplate closestEnemy = null;
|
||||
CharacterTemplate? closestEnemy = null;
|
||||
var closestDistance = float.MaxValue;
|
||||
var selfCamp = CampManager.GetCamp(Character.CampId);
|
||||
foreach (var node in nodesInTheAttackRange)
|
||||
|
@ -44,7 +43,12 @@ public class AIAttackNode : BehaviorTreeNodeTemplate
|
|||
continue;
|
||||
}
|
||||
|
||||
if (selfCamp.ID == characterCamp.ID)
|
||||
if (selfCamp == null || characterCamp == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (selfCamp.Id == characterCamp.Id)
|
||||
{
|
||||
//如果是同一阵营,不攻击
|
||||
continue;
|
||||
|
@ -60,7 +64,7 @@ public class AIAttackNode : BehaviorTreeNodeTemplate
|
|||
}
|
||||
}
|
||||
|
||||
if (closestEnemy != null)
|
||||
if (closestEnemy != null && Character.AttackObstacleDetection != null)
|
||||
{
|
||||
//There are the closest enemies
|
||||
//有距离最近的敌人
|
|
@ -1,40 +1,41 @@
|
|||
using ColdMint.scripts.behaviorTree.framework;
|
||||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.weapon;
|
||||
|
||||
namespace ColdMint.scripts.behaviorTree.ai;
|
||||
|
||||
/// <summary>
|
||||
/// <para>AI巡逻节点</para>
|
||||
/// </summary>
|
||||
public class AIPatrolNode : SelectorNode
|
||||
public class AiPatrolNode : SelectorNode
|
||||
{
|
||||
public AICharacter Character { get; set; }
|
||||
public AiCharacter? Character { get; set; }
|
||||
|
||||
public override IBehaviorTreeNode SelectNode(bool isPhysicsProcess, double delta, IBehaviorTreeNode[] children)
|
||||
protected override IBehaviorTreeNode? SelectNode(bool isPhysicsProcess, double delta, IBehaviorTreeNode[] children)
|
||||
{
|
||||
if (Character == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Character.NodesInTheAttackRange.Length > 1)
|
||||
{
|
||||
if (Character.CurrentItem == null)
|
||||
{
|
||||
//No weapon
|
||||
//没有武器
|
||||
if (Character.NodesInTheAttackRange.Length > 0)
|
||||
var weaponTemplates = Character.GetCanPickedWeapon();
|
||||
if (weaponTemplates.Length > 0)
|
||||
{
|
||||
var weaponTemplates = Character.GetCanPickedWeapon();
|
||||
if (weaponTemplates.Length > 0)
|
||||
var aiPickNode = GetChild<AiPickNode>(null);
|
||||
if (aiPickNode != null)
|
||||
{
|
||||
var aiPickNode = GetChild<AIPickNode>(null);
|
||||
if (aiPickNode != null)
|
||||
{
|
||||
return aiPickNode;
|
||||
}
|
||||
return aiPickNode;
|
||||
}
|
||||
}
|
||||
|
||||
//No weapon, and no weapon to pick up, then try to escape
|
||||
//没有武器,且没有武器可捡,那么尝试逃跑
|
||||
var aiRotorNode = GetChild<AIRotorNode>(null);
|
||||
var aiRotorNode = GetChild<AiRotorNode>(null);
|
||||
if (aiRotorNode != null)
|
||||
{
|
||||
return aiRotorNode;
|
||||
|
@ -45,9 +46,9 @@ public class AIPatrolNode : SelectorNode
|
|||
|
||||
//There are enemies around
|
||||
//周围有敌人
|
||||
if (Character.AttackObstacleDetection.GetCollider() == null)
|
||||
if (Character.AttackObstacleDetection != null && Character.AttackObstacleDetection.GetCollider() == null)
|
||||
{
|
||||
var aiAttackNode = GetChild<AIAttackNode>(null);
|
||||
var aiAttackNode = GetChild<AiAttackNode>(null);
|
||||
if (aiAttackNode != null)
|
||||
{
|
||||
return aiAttackNode;
|
||||
|
@ -55,11 +56,11 @@ public class AIPatrolNode : SelectorNode
|
|||
}
|
||||
}
|
||||
|
||||
if (Character.WallDetection.GetCollider() != null)
|
||||
if (Character.WallDetection?.GetCollider() != null)
|
||||
{
|
||||
//Encounter a wall
|
||||
//遇到墙壁
|
||||
var aiRotorNode = GetChild<AIRotorNode>(null);
|
||||
var aiRotorNode = GetChild<AiRotorNode>(null);
|
||||
if (aiRotorNode != null)
|
||||
{
|
||||
return aiRotorNode;
|
|
@ -1,6 +1,5 @@
|
|||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.weapon;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.behaviorTree.ai;
|
||||
|
||||
|
@ -8,11 +7,11 @@ namespace ColdMint.scripts.behaviorTree.ai;
|
|||
/// <para>Deal with AI picking up items</para>
|
||||
/// <para>处理AI拾起物品的行为</para>
|
||||
/// </summary>
|
||||
public class AIPickNode : BehaviorTreeNodeTemplate
|
||||
public class AiPickNode : BehaviorTreeNodeTemplate
|
||||
{
|
||||
|
||||
|
||||
public AICharacter Character { get; set; }
|
||||
public AiCharacter? Character { get; set; }
|
||||
|
||||
public override int Execute(bool isPhysicsProcess, double delta)
|
||||
{
|
||||
|
@ -40,7 +39,7 @@ public class AIPickNode : BehaviorTreeNodeTemplate
|
|||
|
||||
//The closest weapon
|
||||
//距离最近的武器
|
||||
WeaponTemplate closestWeapon = null;
|
||||
WeaponTemplate? closestWeapon = null;
|
||||
var closestDistance = float.MaxValue;
|
||||
foreach (var weaponTemplate in Character.GetCanPickedWeapon())
|
||||
{
|
|
@ -6,9 +6,9 @@ namespace ColdMint.scripts.behaviorTree.ai;
|
|||
/// <para>The node that controls the rotor when the AI is facing the wall</para>
|
||||
/// <para>当AI面向墙壁时,控制转头的节点</para>
|
||||
/// </summary>
|
||||
public class AIRotorNode : BehaviorTreeNodeTemplate
|
||||
public class AiRotorNode : BehaviorTreeNodeTemplate
|
||||
{
|
||||
public AICharacter Character { get; set; }
|
||||
public AiCharacter? Character { get; set; }
|
||||
|
||||
public override int Execute(bool isPhysicsProcess, double delta)
|
||||
{
|
||||
|
@ -17,7 +17,7 @@ public class AIRotorNode : BehaviorTreeNodeTemplate
|
|||
return Config.BehaviorTreeResult.Failure;
|
||||
}
|
||||
|
||||
var notFacingTheWall = Character.WallDetection.GetCollider() == null;
|
||||
var notFacingTheWall = Character.WallDetection?.GetCollider() == null;
|
||||
if (notFacingTheWall)
|
||||
{
|
||||
return Config.BehaviorTreeResult.Failure;
|
|
@ -6,9 +6,9 @@ namespace ColdMint.scripts.behaviorTree.ai;
|
|||
/// <para>一个节点用于实现角色的移动</para>
|
||||
/// <para>A node is used to implement the movement of the character</para>
|
||||
/// </summary>
|
||||
public class AIWalkNode : BehaviorTreeNodeTemplate
|
||||
public class AiWalkNode : BehaviorTreeNodeTemplate
|
||||
{
|
||||
public AICharacter Character { get; set; }
|
||||
public AiCharacter? Character { get; set; }
|
||||
|
||||
public override int Execute(bool isPhysicsProcess, double delta)
|
||||
{
|
|
@ -1,5 +1,4 @@
|
|||
using ColdMint.scripts.behaviorTree.ai;
|
||||
using ColdMint.scripts.behaviorTree.framework;
|
||||
using ColdMint.scripts.character;
|
||||
|
||||
namespace ColdMint.scripts.behaviorTree.behavior;
|
||||
|
@ -10,14 +9,14 @@ namespace ColdMint.scripts.behaviorTree.behavior;
|
|||
/// </summary>
|
||||
public class PatrolBehaviorTree : BehaviorTreeTemplate
|
||||
{
|
||||
public AICharacter Character { get; set; }
|
||||
public AiCharacter? Character { get; set; }
|
||||
protected override IBehaviorTreeNode CreateRoot()
|
||||
{
|
||||
var patrolNode = new AIPatrolNode();
|
||||
var aiWalkNode = new AIWalkNode();
|
||||
var aiRotorNode = new AIRotorNode();
|
||||
var aIPickNode = new AIPickNode();
|
||||
var aiAttackNode = new AIAttackNode();
|
||||
var patrolNode = new AiPatrolNode();
|
||||
var aiWalkNode = new AiWalkNode();
|
||||
var aiRotorNode = new AiRotorNode();
|
||||
var aIPickNode = new AiPickNode();
|
||||
var aiAttackNode = new AiAttackNode();
|
||||
aiWalkNode.Character = Character;
|
||||
patrolNode.Character = Character;
|
||||
aiRotorNode.Character = Character;
|
||||
|
@ -30,7 +29,7 @@ public class PatrolBehaviorTree : BehaviorTreeTemplate
|
|||
return patrolNode;
|
||||
}
|
||||
|
||||
protected override string CreateID()
|
||||
protected override string? CreateId()
|
||||
{
|
||||
return Config.BehaviorTreeId.Patrol;
|
||||
}
|
||||
|
|
|
@ -13,12 +13,7 @@ public abstract class SelectorNode : BehaviorTreeNodeTemplate
|
|||
public override int Execute(bool isPhysicsProcess, double delta)
|
||||
{
|
||||
var behaviorTreeNode = SelectNode(isPhysicsProcess, delta, Children);
|
||||
if (behaviorTreeNode == null)
|
||||
{
|
||||
return Config.BehaviorTreeResult.Failure;
|
||||
}
|
||||
|
||||
return behaviorTreeNode.Execute(isPhysicsProcess, delta);
|
||||
return behaviorTreeNode?.Execute(isPhysicsProcess, delta) ?? Config.BehaviorTreeResult.Failure;
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,5 +23,5 @@ public abstract class SelectorNode : BehaviorTreeNodeTemplate
|
|||
/// <para>选择节点的抽象方法</para>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract IBehaviorTreeNode SelectNode(bool isPhysicsProcess, double delta, IBehaviorTreeNode[] children);
|
||||
protected abstract IBehaviorTreeNode? SelectNode(bool isPhysicsProcess, double delta, IBehaviorTreeNode[] children);
|
||||
}
|
|
@ -9,7 +9,7 @@ namespace ColdMint.scripts.camp;
|
|||
public class Camp
|
||||
{
|
||||
private string _id;
|
||||
private List<string> _friendlyCampIdList;
|
||||
private readonly List<string> _friendlyCampIdList;
|
||||
|
||||
public Camp(string id)
|
||||
{
|
||||
|
@ -21,13 +21,13 @@ public class Camp
|
|||
/// <para>Get camp ID</para>
|
||||
/// <para>获取阵营ID</para>
|
||||
/// </summary>
|
||||
public string ID => _id;
|
||||
public string Id => _id;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Get camp name</para>
|
||||
/// <para>获取阵营名</para>
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Friend Injury</para>
|
||||
|
|
|
@ -8,106 +8,112 @@ namespace ColdMint.scripts.camp;
|
|||
/// </summary>
|
||||
public class CampManager
|
||||
{
|
||||
private static readonly Dictionary<string, Camp> Camps = new Dictionary<string, Camp>();
|
||||
private static readonly Dictionary<string, Camp?> Camps = new Dictionary<string, Camp?>();
|
||||
|
||||
/// <summary>
|
||||
/// <para>The default camp is returned if no corresponding camp is obtained</para>
|
||||
/// <para>当获取不到对应的阵营时,返回的默认阵营</para>
|
||||
/// </summary>
|
||||
private static Camp _defaultCamp;
|
||||
/// <summary>
|
||||
/// <para>The default camp is returned if no corresponding camp is obtained</para>
|
||||
/// <para>当获取不到对应的阵营时,返回的默认阵营</para>
|
||||
/// </summary>
|
||||
private static Camp? _defaultCamp;
|
||||
|
||||
/// <summary>
|
||||
/// <para>SetDefaultCamp</para>
|
||||
/// <para>设置默认阵营</para>
|
||||
/// </summary>
|
||||
/// <param name="camp">
|
||||
///<para>Camp, whose ID must be the default camp ID.</para>
|
||||
///<para>阵营,要求其ID必须为默认阵营ID。</para>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
///<para>Return whether the setting is successful</para>
|
||||
///<para>返回是否设置成功</para>
|
||||
/// </returns>
|
||||
public static bool SetDefaultCamp(Camp camp)
|
||||
{
|
||||
if (camp.ID == Config.CampId.Default)
|
||||
{
|
||||
_defaultCamp = camp;
|
||||
AddCamp(camp);
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>SetDefaultCamp</para>
|
||||
/// <para>设置默认阵营</para>
|
||||
/// </summary>
|
||||
/// <param name="camp">
|
||||
///<para>Camp, whose ID must be the default camp ID.</para>
|
||||
///<para>阵营,要求其ID必须为默认阵营ID。</para>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
///<para>Return whether the setting is successful</para>
|
||||
///<para>返回是否设置成功</para>
|
||||
/// </returns>
|
||||
public static bool SetDefaultCamp(Camp? camp)
|
||||
{
|
||||
if (camp == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
if (camp.Id != Config.CampId.Default) return false;
|
||||
_defaultCamp = camp;
|
||||
AddCamp(camp);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Whether camp A can damage camp B</para>
|
||||
/// <para>阵营A是否可伤害阵营B</para>
|
||||
/// </summary>
|
||||
/// <param name="attacker"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CanCauseHarm(Camp attacker, Camp target)
|
||||
{
|
||||
if (attacker.ID == target.ID)
|
||||
{
|
||||
//In the same camp, return whether friendly fire is allowed
|
||||
//在同一阵营内,返回是否允许友伤
|
||||
return attacker.FriendInjury;
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>Whether camp A can damage camp B</para>
|
||||
/// <para>阵营A是否可伤害阵营B</para>
|
||||
/// </summary>
|
||||
/// <param name="attacker"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CanCauseHarm(Camp? attacker, Camp? target)
|
||||
{
|
||||
if (attacker == null || target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//A camp ID that the attacker considers friendly
|
||||
//攻击者认为友好的阵营ID
|
||||
var friendlyCampIdArray = attacker.FriendlyCampIdArray;
|
||||
var targetId = target.ID;
|
||||
if (friendlyCampIdArray.Length > 0)
|
||||
{
|
||||
foreach (var friendlyCampId in friendlyCampIdArray)
|
||||
{
|
||||
if (friendlyCampId == targetId)
|
||||
{
|
||||
//The attacker thinks the target is friendly, and we can't hurt a friendly target
|
||||
//攻击者认为目标友好,我们不能伤害友好的目标
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attacker.Id == target.Id)
|
||||
{
|
||||
//In the same camp, return whether friendly fire is allowed
|
||||
//在同一阵营内,返回是否允许友伤
|
||||
return attacker.FriendInjury;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//A camp ID that the attacker considers friendly
|
||||
//攻击者认为友好的阵营ID
|
||||
var friendlyCampIdArray = attacker.FriendlyCampIdArray;
|
||||
var targetId = target.Id;
|
||||
if (friendlyCampIdArray.Length > 0)
|
||||
{
|
||||
foreach (var friendlyCampId in friendlyCampIdArray)
|
||||
{
|
||||
if (friendlyCampId == targetId)
|
||||
{
|
||||
//The attacker thinks the target is friendly, and we can't hurt a friendly target
|
||||
//攻击者认为目标友好,我们不能伤害友好的目标
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Add camp</para>
|
||||
/// <para>添加阵营</para>
|
||||
/// </summary>
|
||||
/// <param name="camp"></param>
|
||||
/// <returns></returns>
|
||||
public static bool AddCamp(Camp camp)
|
||||
{
|
||||
return Camps.TryAdd(camp.ID, camp);
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>Add camp</para>
|
||||
/// <para>添加阵营</para>
|
||||
/// </summary>
|
||||
/// <param name="camp"></param>
|
||||
/// <returns></returns>
|
||||
public static bool AddCamp(Camp? camp)
|
||||
{
|
||||
return camp != null && Camps.TryAdd(camp.Id, camp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Get camp based on ID</para>
|
||||
/// <para>根据ID获取阵营</para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///<para>Cannot get back to default camp</para>
|
||||
///<para>获取不到返回默认阵营</para>
|
||||
/// </remarks>
|
||||
/// <param name="id">
|
||||
///<para>Camp ID</para>
|
||||
///<para>阵营ID</para>
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public static Camp GetCamp(string id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return _defaultCamp;
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>Get camp based on ID</para>
|
||||
/// <para>根据ID获取阵营</para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///<para>Cannot get back to default camp</para>
|
||||
///<para>获取不到返回默认阵营</para>
|
||||
/// </remarks>
|
||||
/// <param name="id">
|
||||
///<para>Camp ID</para>
|
||||
///<para>阵营ID</para>
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public static Camp? GetCamp(string? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return _defaultCamp;
|
||||
}
|
||||
|
||||
return Camps.GetValueOrDefault(id, _defaultCamp);
|
||||
}
|
||||
}
|
||||
return Camps.GetValueOrDefault(id, _defaultCamp);
|
||||
}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.behaviorTree;
|
||||
using ColdMint.scripts.behaviorTree.ai;
|
||||
using ColdMint.scripts.behaviorTree.behavior;
|
||||
using ColdMint.scripts.behaviorTree.framework;
|
||||
using ColdMint.scripts.damage;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.character;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The role played by computers</para>
|
||||
/// <para>由电脑扮演的角色</para>
|
||||
/// </summary>
|
||||
public partial class AICharacter : CharacterTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>How fast the character moves</para>
|
||||
/// <para>角色的移动速度</para>
|
||||
/// </summary>
|
||||
private float MovementSpeed = 300.0f;
|
||||
|
||||
private BehaviorNode _behaviorNode;
|
||||
|
||||
//Used to detect rays on walls
|
||||
//用于检测墙壁的射线
|
||||
private RayCast2D _wallDetection;
|
||||
|
||||
public RayCast2D WallDetection => _wallDetection;
|
||||
private Vector2 _wallDetectionOrigin;
|
||||
private Area2D _attackArea;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Nodes in the attack range</para>
|
||||
/// <para>在攻击范围内的节点</para>
|
||||
/// </summary>
|
||||
private List<Node> _nodesInTheAttackRange;
|
||||
|
||||
/// <summary>
|
||||
/// <para>All nodes in the attack range</para>
|
||||
/// <para>在攻击范围内的全部节点</para>
|
||||
/// </summary>
|
||||
public Node[] NodesInTheAttackRange => _nodesInTheAttackRange.ToArray();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Obstacle detection ray during attack</para>
|
||||
/// <para>攻击时的障碍物检测射线</para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///<para></para>
|
||||
///<para>检测与目标点直接是否间隔墙壁</para>
|
||||
/// </remarks>
|
||||
private RayCast2D _attackObstacleDetection;
|
||||
|
||||
|
||||
public RayCast2D AttackObstacleDetection => _attackObstacleDetection;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
_nodesInTheAttackRange = new List<Node>();
|
||||
_behaviorNode = GetNode<BehaviorNode>("Behavior");
|
||||
_wallDetection = GetNode<RayCast2D>("WallDetection");
|
||||
_attackArea = GetNode<Area2D>("AttackArea2D");
|
||||
_attackObstacleDetection = ItemMarker2D.GetNode<RayCast2D>("AttackObstacleDetection");
|
||||
if (_attackArea != null)
|
||||
{
|
||||
//如果为true,该区域将检测进出该区域的物体或区域。
|
||||
_attackArea.Monitoring = true;
|
||||
//Other regions cannot detect our pick region
|
||||
//其他区域不能检测到我们的拾取区域
|
||||
_attackArea.Monitorable = false;
|
||||
_attackArea.BodyEntered += EnterTheAttackArea;
|
||||
_attackArea.BodyExited += ExitTheAttackArea;
|
||||
}
|
||||
|
||||
_wallDetectionOrigin = _wallDetection.TargetPosition;
|
||||
// var patrolBehaviorTree = new PatrolBehaviorTree();
|
||||
// patrolBehaviorTree.Character = this;
|
||||
// patrolBehaviorTree.Init();
|
||||
// _behaviorNode.Root = patrolBehaviorTree.Root;
|
||||
}
|
||||
|
||||
protected virtual void EnterTheAttackArea(Node node)
|
||||
{
|
||||
_nodesInTheAttackRange.Add(node);
|
||||
}
|
||||
|
||||
protected virtual void ExitTheAttackArea(Node node)
|
||||
{
|
||||
_nodesInTheAttackRange.Remove(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Move left</para>
|
||||
/// <para>向左移动</para>
|
||||
/// </summary>
|
||||
public void MoveLeft()
|
||||
{
|
||||
var oldVelocity = Velocity;
|
||||
oldVelocity.X = -MovementSpeed;
|
||||
Velocity = oldVelocity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Move right</para>
|
||||
/// <para>向右移动</para>
|
||||
/// </summary>
|
||||
public void MoveRight()
|
||||
{
|
||||
var oldVelocity = Velocity;
|
||||
oldVelocity.X = MovementSpeed;
|
||||
Velocity = oldVelocity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Rotor</para>
|
||||
/// <para>转头</para>
|
||||
/// </summary>
|
||||
public void Rotor()
|
||||
{
|
||||
FacingLeft = !FacingLeft;
|
||||
Flip();
|
||||
//Change the direction of the wall detection
|
||||
//改变墙壁检测的方向
|
||||
var newDirection = _wallDetectionOrigin;
|
||||
newDirection.X = FacingLeft ? -_wallDetectionOrigin.X : _wallDetectionOrigin.X;
|
||||
_wallDetection.TargetPosition = newDirection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>stop moving</para>
|
||||
/// <para>停止移动</para>
|
||||
/// </summary>
|
||||
public void StopMoving()
|
||||
{
|
||||
Velocity = Vector2.Zero;
|
||||
}
|
||||
|
||||
}
|
142
scripts/character/AiCharacter.cs
Normal file
142
scripts/character/AiCharacter.cs
Normal file
|
@ -0,0 +1,142 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.behaviorTree;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.character;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The role played by computers</para>
|
||||
/// <para>由电脑扮演的角色</para>
|
||||
/// </summary>
|
||||
public partial class AiCharacter : CharacterTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>How fast the character moves</para>
|
||||
/// <para>角色的移动速度</para>
|
||||
/// </summary>
|
||||
private float _movementSpeed = 300.0f;
|
||||
|
||||
private BehaviorNode? _behaviorNode;
|
||||
|
||||
//Used to detect rays on walls
|
||||
//用于检测墙壁的射线
|
||||
private RayCast2D? _wallDetection;
|
||||
|
||||
public RayCast2D? WallDetection => _wallDetection;
|
||||
private Vector2 _wallDetectionOrigin;
|
||||
private Area2D? _attackArea;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Nodes in the attack range</para>
|
||||
/// <para>在攻击范围内的节点</para>
|
||||
/// </summary>
|
||||
private List<Node>? _nodesInTheAttackRange;
|
||||
|
||||
/// <summary>
|
||||
/// <para>All nodes in the attack range</para>
|
||||
/// <para>在攻击范围内的全部节点</para>
|
||||
/// </summary>
|
||||
public Node[] NodesInTheAttackRange => _nodesInTheAttackRange?.ToArray() ?? Array.Empty<Node>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Obstacle detection ray during attack</para>
|
||||
/// <para>攻击时的障碍物检测射线</para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///<para></para>
|
||||
///<para>检测与目标点直接是否间隔墙壁</para>
|
||||
/// </remarks>
|
||||
private RayCast2D? _attackObstacleDetection;
|
||||
|
||||
|
||||
public RayCast2D? AttackObstacleDetection => _attackObstacleDetection;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
_nodesInTheAttackRange = new List<Node>();
|
||||
_behaviorNode = GetNode<BehaviorNode>("Behavior");
|
||||
_wallDetection = GetNode<RayCast2D>("WallDetection");
|
||||
_attackArea = GetNode<Area2D>("AttackArea2D");
|
||||
if (ItemMarker2D != null)
|
||||
{
|
||||
_attackObstacleDetection = ItemMarker2D.GetNode<RayCast2D>("AttackObstacleDetection");
|
||||
}
|
||||
|
||||
if (_attackArea != null)
|
||||
{
|
||||
//如果为true,该区域将检测进出该区域的物体或区域。
|
||||
_attackArea.Monitoring = true;
|
||||
//Other regions cannot detect our pick region
|
||||
//其他区域不能检测到我们的拾取区域
|
||||
_attackArea.Monitorable = false;
|
||||
_attackArea.BodyEntered += EnterTheAttackArea;
|
||||
_attackArea.BodyExited += ExitTheAttackArea;
|
||||
}
|
||||
|
||||
_wallDetectionOrigin = _wallDetection.TargetPosition;
|
||||
// var patrolBehaviorTree = new PatrolBehaviorTree();
|
||||
// patrolBehaviorTree.Character = this;
|
||||
// patrolBehaviorTree.Init();
|
||||
// _behaviorNode.Root = patrolBehaviorTree.Root;
|
||||
}
|
||||
|
||||
protected virtual void EnterTheAttackArea(Node node)
|
||||
{
|
||||
_nodesInTheAttackRange?.Add(node);
|
||||
}
|
||||
|
||||
protected virtual void ExitTheAttackArea(Node node)
|
||||
{
|
||||
_nodesInTheAttackRange?.Remove(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Move left</para>
|
||||
/// <para>向左移动</para>
|
||||
/// </summary>
|
||||
public void MoveLeft()
|
||||
{
|
||||
var oldVelocity = Velocity;
|
||||
oldVelocity.X = -_movementSpeed;
|
||||
Velocity = oldVelocity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Move right</para>
|
||||
/// <para>向右移动</para>
|
||||
/// </summary>
|
||||
public void MoveRight()
|
||||
{
|
||||
var oldVelocity = Velocity;
|
||||
oldVelocity.X = _movementSpeed;
|
||||
Velocity = oldVelocity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Rotor</para>
|
||||
/// <para>转头</para>
|
||||
/// </summary>
|
||||
public void Rotor()
|
||||
{
|
||||
FacingLeft = !FacingLeft;
|
||||
Flip();
|
||||
//Change the direction of the wall detection
|
||||
//改变墙壁检测的方向
|
||||
if (_wallDetection == null) return;
|
||||
var newDirection = _wallDetectionOrigin;
|
||||
newDirection.X = FacingLeft ? -_wallDetectionOrigin.X : _wallDetectionOrigin.X;
|
||||
_wallDetection.TargetPosition = newDirection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>stop moving</para>
|
||||
/// <para>停止移动</para>
|
||||
/// </summary>
|
||||
public void StopMoving()
|
||||
{
|
||||
Velocity = Vector2.Zero;
|
||||
}
|
||||
}
|
|
@ -310,17 +310,20 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
var playerCamp = CampManager.GetCamp(GameSceneNodeHolder.Player.CampId);
|
||||
if (CampManager.CanCauseHarm(targetCamp, playerCamp))
|
||||
{
|
||||
if (targetCamp.ID == playerCamp.ID)
|
||||
if (targetCamp != null && playerCamp != null)
|
||||
{
|
||||
//If an attack is allowed and you are on the same side, it is displayed as a friendly color (friend damage).
|
||||
//如果允许攻击,且属于同一阵营,则显示为友好颜色(友伤)
|
||||
_healthBar.SetFriendlyTones();
|
||||
}
|
||||
else
|
||||
{
|
||||
//If the injured target is an enemy of the player, it is displayed as an enemy color
|
||||
//如果受伤的目标是玩家的敌人,则显示为敌对颜色
|
||||
_healthBar.SetEnemyTones();
|
||||
if (targetCamp.Id == playerCamp.Id)
|
||||
{
|
||||
//If an attack is allowed and you are on the same side, it is displayed as a friendly color (friend damage).
|
||||
//如果允许攻击,且属于同一阵营,则显示为友好颜色(友伤)
|
||||
_healthBar.SetFriendlyTones();
|
||||
}
|
||||
else
|
||||
{
|
||||
//If the injured target is an enemy of the player, it is displayed as an enemy color
|
||||
//如果受伤的目标是玩家的敌人,则显示为敌对颜色
|
||||
_healthBar.SetEnemyTones();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -56,8 +56,12 @@ public partial class Player : CharacterTemplate
|
|||
_parabola = GetNode<Line2D>("Parabola");
|
||||
_platformDetectionRayCast2D = GetNode<RayCast2D>("PlatformDetectionRayCast");
|
||||
UpdateOperationTip();
|
||||
GameSceneNodeHolder.HealthBarUi.MaxHp = MaxHp;
|
||||
GameSceneNodeHolder.HealthBarUi.CurrentHp = CurrentHp;
|
||||
var healthBarUi = GameSceneNodeHolder.HealthBarUi;
|
||||
if (healthBarUi != null)
|
||||
{
|
||||
healthBarUi.MaxHp = MaxHp;
|
||||
healthBarUi.CurrentHp = CurrentHp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -66,6 +70,12 @@ public partial class Player : CharacterTemplate
|
|||
/// </summary>
|
||||
private void UpdateOperationTip()
|
||||
{
|
||||
var operationTipLabel = GameSceneNodeHolder.OperationTipLabel;
|
||||
if (operationTipLabel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var operationTipBuilder = new StringBuilder();
|
||||
if (_totalNumberOfPickups > 0)
|
||||
{
|
||||
|
@ -129,7 +139,7 @@ public partial class Player : CharacterTemplate
|
|||
}
|
||||
}
|
||||
|
||||
GameSceneNodeHolder.OperationTipLabel.Text = operationTipBuilder.ToString();
|
||||
operationTipLabel.Text = operationTipBuilder.ToString();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -165,7 +175,7 @@ public partial class Player : CharacterTemplate
|
|||
}
|
||||
}
|
||||
|
||||
GameSceneNodeHolder.OperationTipLabel.Text = operationTipBuilder.ToString();
|
||||
operationTipLabel.Text = operationTipBuilder.ToString();
|
||||
}
|
||||
|
||||
|
||||
|
@ -264,7 +274,10 @@ public partial class Player : CharacterTemplate
|
|||
//抬起手时,抛出物品
|
||||
if (Input.IsActionJustReleased("throw"))
|
||||
{
|
||||
if (CurrentItem == null) return;
|
||||
if (CurrentItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_parabola != null)
|
||||
{
|
||||
_parabola.Points = new[] { Vector2.Zero };
|
||||
|
@ -293,6 +306,12 @@ public partial class Player : CharacterTemplate
|
|||
weaponTemplate.LinearVelocity = Vector2.Zero;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//We apply force to objects.
|
||||
//我们给物品施加力。
|
||||
switch (CurrentItem)
|
||||
{
|
||||
case CharacterBody2D characterBody2D:
|
||||
characterBody2D.Velocity = GetThrowVelocity();
|
||||
break;
|
||||
|
@ -303,7 +322,9 @@ public partial class Player : CharacterTemplate
|
|||
|
||||
CurrentItem = null;
|
||||
_totalNumberOfPickups++;
|
||||
GameSceneNodeHolder.HotBar.RemoveItemFromItemSlotBySelectIndex(1);
|
||||
var hotBar = GameSceneNodeHolder.HotBar;
|
||||
hotBar?.RemoveItemFromItemSlotBySelectIndex(1);
|
||||
|
||||
UpdateOperationTip();
|
||||
}
|
||||
}
|
||||
|
@ -444,6 +465,10 @@ public partial class Player : CharacterTemplate
|
|||
protected override void OnHit(DamageTemplate damageTemplate)
|
||||
{
|
||||
base.OnHit(damageTemplate);
|
||||
GameSceneNodeHolder.HealthBarUi.CurrentHp = CurrentHp;
|
||||
var healthBarUi = GameSceneNodeHolder.HealthBarUi;
|
||||
if (healthBarUi != null)
|
||||
{
|
||||
healthBarUi.CurrentHp = CurrentHp;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
using ColdMint.scripts.debug;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.damage;
|
||||
|
@ -9,7 +8,7 @@ namespace ColdMint.scripts.damage;
|
|||
/// </summary>
|
||||
public partial class DamageNumber : CharacterBody2D
|
||||
{
|
||||
private VisibleOnScreenNotifier2D _visibleOnScreenNotifier2D;
|
||||
private VisibleOnScreenNotifier2D? _visibleOnScreenNotifier2D;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
|
@ -24,22 +23,22 @@ public partial class DamageNumber : CharacterBody2D
|
|||
QueueFree();
|
||||
}
|
||||
|
||||
private float gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
|
||||
private float _gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
|
||||
|
||||
private bool enableGravity;
|
||||
private bool _enableGravity;
|
||||
|
||||
public void SetVelocity(Vector2 velocity)
|
||||
{
|
||||
Velocity = velocity;
|
||||
enableGravity = true;
|
||||
_enableGravity = true;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
var velocity = Velocity;
|
||||
if (enableGravity)
|
||||
if (_enableGravity)
|
||||
{
|
||||
velocity.Y += gravity * (float)delta;
|
||||
velocity.Y += _gravity * (float)delta;
|
||||
}
|
||||
|
||||
Velocity = velocity;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.debug;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.damage;
|
||||
|
@ -10,139 +8,144 @@ namespace ColdMint.scripts.damage;
|
|||
/// </summary>
|
||||
public partial class DamageNumberNodeSpawn : Marker2D
|
||||
{
|
||||
private PackedScene _damageNumberPackedScene;
|
||||
private Node2D _rootNode;
|
||||
private PackedScene? _damageNumberPackedScene;
|
||||
private Node2D? _rootNode;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The horizontal velocity is in the X positive direction</para>
|
||||
/// <para>水平速度的X正方向</para>
|
||||
/// </summary>
|
||||
private int horizontalVelocityPositiveDirection;
|
||||
/// <summary>
|
||||
/// <para>The horizontal velocity is in the X positive direction</para>
|
||||
/// <para>水平速度的X正方向</para>
|
||||
/// </summary>
|
||||
private int _horizontalVelocityPositiveDirection;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Horizontal velocity in the negative X direction</para>
|
||||
/// <para>水平速度的X负方向</para>
|
||||
/// </summary>
|
||||
private int horizontalVelocityNegativeDirection;
|
||||
/// <summary>
|
||||
/// <para>Horizontal velocity in the negative X direction</para>
|
||||
/// <para>水平速度的X负方向</para>
|
||||
/// </summary>
|
||||
private int _horizontalVelocityNegativeDirection;
|
||||
|
||||
/// <summary>
|
||||
/// <para>vertical height</para>
|
||||
/// <para>垂直高度</para>
|
||||
/// </summary>
|
||||
private int verticalHeight;
|
||||
/// <summary>
|
||||
/// <para>vertical height</para>
|
||||
/// <para>垂直高度</para>
|
||||
/// </summary>
|
||||
private int _verticalHeight;
|
||||
|
||||
/// <summary>
|
||||
/// <para>物理渐变色</para>
|
||||
/// <para>physical Gradient</para>
|
||||
/// </summary>
|
||||
private Gradient physicalGradient;
|
||||
/// <summary>
|
||||
/// <para>物理渐变色</para>
|
||||
/// <para>physical Gradient</para>
|
||||
/// </summary>
|
||||
private Gradient? _physicalGradient;
|
||||
|
||||
/// <summary>
|
||||
/// <para>魔法渐变色</para>
|
||||
/// <para>magic Gradient</para>
|
||||
/// </summary>
|
||||
private Gradient magicGradient;
|
||||
/// <summary>
|
||||
/// <para>魔法渐变色</para>
|
||||
/// <para>magic Gradient</para>
|
||||
/// </summary>
|
||||
private Gradient? _magicGradient;
|
||||
|
||||
/// <summary>
|
||||
/// <para>默认渐变色</para>
|
||||
/// <para>default Gradient</para>
|
||||
/// </summary>
|
||||
private Gradient defaultGradient;
|
||||
/// <summary>
|
||||
/// <para>默认渐变色</para>
|
||||
/// <para>default Gradient</para>
|
||||
/// </summary>
|
||||
private Gradient? _defaultGradient;
|
||||
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
_damageNumberPackedScene = GD.Load("res://prefab/ui/DamageNumber.tscn") as PackedScene;
|
||||
_rootNode = GetNode<Node2D>("/root/Game/DamageNumberContainer");
|
||||
horizontalVelocityPositiveDirection = Config.CellSize * Config.HorizontalSpeedOfDamageNumbers;
|
||||
horizontalVelocityNegativeDirection = -horizontalVelocityPositiveDirection;
|
||||
verticalHeight = -Config.CellSize * Config.VerticalVelocityOfDamageNumbers;
|
||||
physicalGradient = new Gradient();
|
||||
//物理色 从OpenColor2 到 OpenColor6(红色)
|
||||
physicalGradient.SetColor(0, new Color("#ffc9c9"));
|
||||
physicalGradient.SetColor(1, new Color("#fa5252"));
|
||||
magicGradient = new Gradient();
|
||||
//魔法色 从OpenColor2 到 OpenColor6(紫色)
|
||||
magicGradient.SetColor(0, new Color("#d0bfff"));
|
||||
magicGradient.SetColor(1, new Color("#7950f2"));
|
||||
defaultGradient = new Gradient();
|
||||
//默认行为
|
||||
defaultGradient.SetColor(0, new Color("#ff8787"));
|
||||
defaultGradient.SetColor(1, new Color("#fa5252"));
|
||||
}
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
_damageNumberPackedScene = GD.Load("res://prefab/ui/DamageNumber.tscn") as PackedScene;
|
||||
_rootNode = GetNode<Node2D>("/root/Game/DamageNumberContainer");
|
||||
_horizontalVelocityPositiveDirection = Config.CellSize * Config.HorizontalSpeedOfDamageNumbers;
|
||||
_horizontalVelocityNegativeDirection = -_horizontalVelocityPositiveDirection;
|
||||
_verticalHeight = -Config.CellSize * Config.VerticalVelocityOfDamageNumbers;
|
||||
_physicalGradient = new Gradient();
|
||||
//物理色 从OpenColor2 到 OpenColor6(红色)
|
||||
_physicalGradient.SetColor(0, new Color("#ffc9c9"));
|
||||
_physicalGradient.SetColor(1, new Color("#fa5252"));
|
||||
_magicGradient = new Gradient();
|
||||
//魔法色 从OpenColor2 到 OpenColor6(紫色)
|
||||
_magicGradient.SetColor(0, new Color("#d0bfff"));
|
||||
_magicGradient.SetColor(1, new Color("#7950f2"));
|
||||
_defaultGradient = new Gradient();
|
||||
//默认行为
|
||||
_defaultGradient.SetColor(0, new Color("#ff8787"));
|
||||
_defaultGradient.SetColor(1, new Color("#fa5252"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Added a damage digital node</para>
|
||||
/// <para>添加伤害数字节点</para>
|
||||
/// </summary>
|
||||
/// <param name="damageNumber"></param>
|
||||
private void AddDamageNumberNode(Node2D damageNumber)
|
||||
{
|
||||
_rootNode.AddChild(damageNumber);
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>Added a damage digital node</para>
|
||||
/// <para>添加伤害数字节点</para>
|
||||
/// </summary>
|
||||
/// <param name="damageNumber"></param>
|
||||
private void AddDamageNumberNode(Node2D damageNumber)
|
||||
{
|
||||
_rootNode?.AddChild(damageNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Show damage</para>
|
||||
/// <para>显示伤害</para>
|
||||
/// </summary>
|
||||
/// <param name="damageTemplate"></param>
|
||||
public void Display(DamageTemplate damageTemplate)
|
||||
{
|
||||
if (_damageNumberPackedScene == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>Show damage</para>
|
||||
/// <para>显示伤害</para>
|
||||
/// </summary>
|
||||
/// <param name="damageTemplate"></param>
|
||||
public void Display(DamageTemplate damageTemplate)
|
||||
{
|
||||
if (_damageNumberPackedScene == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_damageNumberPackedScene.Instantiate() is not DamageNumber damageNumber)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_damageNumberPackedScene.Instantiate() is not DamageNumber damageNumber)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CallDeferred("AddDamageNumberNode", damageNumber);
|
||||
CallDeferred("AddDamageNumberNode", damageNumber);
|
||||
|
||||
damageNumber.Position = GlobalPosition;
|
||||
if (damageTemplate.MoveLeft)
|
||||
{
|
||||
damageNumber.SetVelocity(new Vector2(horizontalVelocityNegativeDirection, verticalHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
damageNumber.SetVelocity(new Vector2(horizontalVelocityPositiveDirection, verticalHeight));
|
||||
}
|
||||
damageNumber.Position = GlobalPosition;
|
||||
if (damageTemplate.MoveLeft)
|
||||
{
|
||||
damageNumber.SetVelocity(new Vector2(_horizontalVelocityNegativeDirection, _verticalHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
damageNumber.SetVelocity(new Vector2(_horizontalVelocityPositiveDirection, _verticalHeight));
|
||||
}
|
||||
|
||||
var damageLabel = damageNumber.GetNode<Label>("Label");
|
||||
damageLabel.Text = damageTemplate.Damage.ToString();
|
||||
var labelSettings = new LabelSettings();
|
||||
var offset = damageTemplate.Damage / (float)damageTemplate.MaxDamage;
|
||||
labelSettings.FontColor = GetDamageColorByType(damageTemplate.Type).Sample(offset);
|
||||
if (damageTemplate.IsCriticalStrike)
|
||||
{
|
||||
labelSettings.FontSize = Config.CritDamageTextSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
labelSettings.FontSize = Config.NormalDamageTextSize;
|
||||
}
|
||||
var damageLabel = damageNumber.GetNode<Label>("Label");
|
||||
damageLabel.Text = damageTemplate.Damage.ToString();
|
||||
var labelSettings = new LabelSettings();
|
||||
var offset = damageTemplate.Damage / (float)damageTemplate.MaxDamage;
|
||||
var gradient = GetDamageColorByType(damageTemplate.Type);
|
||||
if (gradient != null)
|
||||
{
|
||||
labelSettings.FontColor = gradient.Sample(offset);
|
||||
}
|
||||
|
||||
damageLabel.LabelSettings = labelSettings;
|
||||
damageLabel.Position = Vector2.Zero;
|
||||
}
|
||||
if (damageTemplate.IsCriticalStrike)
|
||||
{
|
||||
labelSettings.FontSize = Config.CritDamageTextSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
labelSettings.FontSize = Config.NormalDamageTextSize;
|
||||
}
|
||||
|
||||
damageLabel.LabelSettings = labelSettings;
|
||||
damageLabel.Position = Vector2.Zero;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Gets text color based on damage type</para>
|
||||
/// <para>根据伤害类型获取文本颜色</para>
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
private Gradient GetDamageColorByType(int type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
Config.DamageType.Physical => physicalGradient,
|
||||
Config.DamageType.Magic => magicGradient,
|
||||
_ => defaultGradient
|
||||
};
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>Gets text color based on damage type</para>
|
||||
/// <para>根据伤害类型获取文本颜色</para>
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
private Gradient? GetDamageColorByType(int type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
Config.DamageType.Physical => _physicalGradient,
|
||||
Config.DamageType.Magic => _magicGradient,
|
||||
_ => _defaultGradient
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using ColdMint.scripts.debug;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.damage;
|
||||
|
@ -30,7 +29,7 @@ public abstract class DamageTemplate
|
|||
/// <para></para>
|
||||
/// <para>攻击者</para>
|
||||
/// </summary>
|
||||
public Node2D Attacker { get; set; }
|
||||
public Node2D? Attacker { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Whether the damage text moves to the left</para>
|
||||
|
@ -81,11 +80,11 @@ public abstract class DamageTemplate
|
|||
/// <para>An event performed before harming the enemy</para>
|
||||
/// <para>在伤害敌人之前执行的事件</para>
|
||||
/// </summary>
|
||||
public Action<Node2D> BeforeDamage;
|
||||
public Action<Node2D>? BeforeDamage;
|
||||
|
||||
/// <summary>
|
||||
/// <para>After damaging the enemy</para>
|
||||
/// <para>在伤害敌人之后</para>
|
||||
/// </summary>
|
||||
public Action<Node2D> AfterDamage;
|
||||
public Action<Node2D>? AfterDamage;
|
||||
}
|
|
@ -4,17 +4,17 @@ using Godot;
|
|||
|
||||
namespace ColdMint.scripts.debug;
|
||||
|
||||
public class LogCat
|
||||
public static class LogCat
|
||||
{
|
||||
private static readonly StringBuilder _stringBuilder = new StringBuilder();
|
||||
private static readonly StringBuilder StringBuilder = new StringBuilder();
|
||||
|
||||
|
||||
private static StringBuilder HandleMessage(string message)
|
||||
{
|
||||
_stringBuilder.Clear();
|
||||
_stringBuilder.Append(DateTime.Now.ToString("yyyy-M-d HH:mm:ss |"));
|
||||
_stringBuilder.Append(TranslationServer.Translate(message));
|
||||
return _stringBuilder;
|
||||
StringBuilder.Clear();
|
||||
StringBuilder.Append(DateTime.Now.ToString("yyyy-M-d HH:mm:ss |"));
|
||||
StringBuilder.Append(TranslationServer.Translate(message));
|
||||
return StringBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -9,12 +9,12 @@ namespace ColdMint.scripts.inventory;
|
|||
/// </summary>
|
||||
public class CommonItem : IItem
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public int MaxStackQuantity { get; set; }
|
||||
public Texture2D Icon { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public Action<IItem> OnUse { get; set; }
|
||||
public Func<IItem, Node> OnInstantiation { get; set; }
|
||||
public Texture2D? Icon { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public Action<IItem>? OnUse { get; set; }
|
||||
public Func<IItem, Node>? OnInstantiation { get; set; }
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.utils;
|
||||
using ColdMint.scripts.weapon;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.inventory;
|
||||
|
@ -13,9 +10,12 @@ namespace ColdMint.scripts.inventory;
|
|||
/// </summary>
|
||||
public partial class HotBar : HBoxContainer, IItemContainer
|
||||
{
|
||||
private PackedScene _itemSlotPackedScene;
|
||||
private List<ItemSlotNode> _itemSlotNodes;
|
||||
private int selectIndex = 0;
|
||||
private PackedScene? _itemSlotPackedScene;
|
||||
|
||||
private List<ItemSlotNode>? _itemSlotNodes;
|
||||
|
||||
//_selectIndex默认为0.
|
||||
private int _selectIndex;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
|
@ -29,47 +29,75 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Select the next item slot</para>
|
||||
/// <para>选择下一个物品槽</para>
|
||||
/// </summary>
|
||||
private void SelectTheNextItemSlot()
|
||||
{
|
||||
if (_itemSlotNodes == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var count = _itemSlotNodes.Count;
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldSelectIndex = _selectIndex;
|
||||
_selectIndex++;
|
||||
if (_selectIndex >= count)
|
||||
{
|
||||
_selectIndex = 0;
|
||||
}
|
||||
|
||||
SelectItemSlot(oldSelectIndex, _selectIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Select the previous item slot</para>
|
||||
/// <para>选择上一个物品槽</para>
|
||||
/// </summary>
|
||||
private void SelectThePreviousItemSlot()
|
||||
{
|
||||
if (_itemSlotNodes == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var count = _itemSlotNodes.Count;
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldSelectIndex = _selectIndex;
|
||||
_selectIndex--;
|
||||
if (_selectIndex < 0)
|
||||
{
|
||||
_selectIndex = count - 1;
|
||||
}
|
||||
|
||||
SelectItemSlot(oldSelectIndex, _selectIndex);
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
base._Process(delta);
|
||||
if (Input.IsActionJustPressed("hotbar_next"))
|
||||
{
|
||||
var count = _itemSlotNodes.Count;
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Mouse wheel down
|
||||
//鼠标滚轮向下
|
||||
var oldSelectIndex = selectIndex;
|
||||
selectIndex++;
|
||||
if (selectIndex >= count)
|
||||
{
|
||||
selectIndex = 0;
|
||||
}
|
||||
|
||||
SelectItemSlot(oldSelectIndex, selectIndex);
|
||||
SelectTheNextItemSlot();
|
||||
}
|
||||
|
||||
if (Input.IsActionJustPressed("hotbar_previous"))
|
||||
{
|
||||
var count = _itemSlotNodes.Count;
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Mouse wheel up
|
||||
//鼠标滚轮向上
|
||||
var oldSelectIndex = selectIndex;
|
||||
selectIndex--;
|
||||
if (selectIndex < 0)
|
||||
{
|
||||
selectIndex = count - 1;
|
||||
}
|
||||
|
||||
SelectItemSlot(oldSelectIndex, selectIndex);
|
||||
SelectThePreviousItemSlot();
|
||||
}
|
||||
|
||||
if (Input.IsActionJustPressed("hotbar_1"))
|
||||
|
@ -127,6 +155,11 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
/// <param name="shortcutKeyIndex"></param>
|
||||
private void SelectItemSlotByHotBarShortcutKey(int shortcutKeyIndex)
|
||||
{
|
||||
if (_itemSlotNodes == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var count = _itemSlotNodes.Count;
|
||||
if (count == 0)
|
||||
{
|
||||
|
@ -136,8 +169,8 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
}
|
||||
|
||||
var newIndex = shortcutKeyIndex % count;
|
||||
SelectItemSlot(selectIndex, newIndex);
|
||||
selectIndex = newIndex;
|
||||
SelectItemSlot(_selectIndex, newIndex);
|
||||
_selectIndex = newIndex;
|
||||
}
|
||||
|
||||
|
||||
|
@ -149,7 +182,7 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
/// <returns></returns>
|
||||
public bool RemoveItemFromItemSlotBySelectIndex(int number)
|
||||
{
|
||||
return RemoveItemFromItemSlot(selectIndex, number);
|
||||
return RemoveItemFromItemSlot(_selectIndex, number);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -160,8 +193,17 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
///<para>When this number is greater than the number of item slots, residual filtering is used.</para>
|
||||
///<para>当此数量大于物品槽的数量时,会使用余数筛选。</para>
|
||||
/// </param>
|
||||
/// <param name="number">
|
||||
///<para>The number of items removed</para>
|
||||
///<para>移除物品的数量</para>
|
||||
/// </param>
|
||||
public bool RemoveItemFromItemSlot(int itemSlotIndex, int number)
|
||||
{
|
||||
if (_itemSlotNodes == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var count = _itemSlotNodes.Count;
|
||||
if (count == 0)
|
||||
{
|
||||
|
@ -181,6 +223,11 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
/// </summary>
|
||||
private void SelectItemSlot(int oldSelectIndex, int newSelectIndex)
|
||||
{
|
||||
if (_itemSlotNodes == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_itemSlotNodes[oldSelectIndex].IsSelect = false;
|
||||
_itemSlotNodes[newSelectIndex].IsSelect = true;
|
||||
var oldItem = _itemSlotNodes[oldSelectIndex].GetItem();
|
||||
|
@ -247,16 +294,16 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
|
||||
public ItemSlotNode? GetSelectItemSlotNode()
|
||||
{
|
||||
if (_itemSlotNodes.Count == 0)
|
||||
if (_itemSlotNodes == null || _itemSlotNodes.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (selectIndex < _itemSlotNodes.Count)
|
||||
if (_selectIndex < _itemSlotNodes.Count)
|
||||
{
|
||||
//Prevent subscripts from going out of bounds.
|
||||
//防止下标越界。
|
||||
return _itemSlotNodes[selectIndex];
|
||||
return _itemSlotNodes[_selectIndex];
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -264,7 +311,7 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
|
||||
public ItemSlotNode? Matching(IItem item)
|
||||
{
|
||||
if (_itemSlotNodes.Count == 0)
|
||||
if (_itemSlotNodes == null || _itemSlotNodes.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@ -289,6 +336,11 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
/// </summary>
|
||||
private void AddItemSlot(int index)
|
||||
{
|
||||
if (_itemSlotNodes == null || _itemSlotPackedScene == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var itemSlotNode = _itemSlotPackedScene.Instantiate() as ItemSlotNode;
|
||||
if (itemSlotNode == null)
|
||||
{
|
||||
|
@ -296,7 +348,7 @@ public partial class HotBar : HBoxContainer, IItemContainer
|
|||
}
|
||||
|
||||
AddChild(itemSlotNode);
|
||||
itemSlotNode.IsSelect = index == selectIndex;
|
||||
itemSlotNode.IsSelect = index == _selectIndex;
|
||||
_itemSlotNodes.Add(itemSlotNode);
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ public interface IItem
|
|||
/// <para>Item and ID</para>
|
||||
/// <para>物品还有ID</para>
|
||||
/// </summary>
|
||||
string Id { get; set; }
|
||||
string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Represents the quantity of this item</para>
|
||||
|
@ -33,24 +33,24 @@ public interface IItem
|
|||
/// <para>Item has a name</para>
|
||||
/// <para>物品有名称</para>
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Description</para>
|
||||
/// <para>描述</para>
|
||||
/// </summary>
|
||||
string Description { get; set; }
|
||||
string? Description { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>When using items</para>
|
||||
/// <para>当使用物品时</para>
|
||||
/// </summary>
|
||||
Action<IItem> OnUse { get; set; }
|
||||
Action<IItem>? OnUse { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>When removing items from the backpack, instantiate them</para>
|
||||
/// <para>当从背包内取出,实例化物品时</para>
|
||||
/// </summary>
|
||||
Func<IItem, Node> OnInstantiation { get; set; }
|
||||
Func<IItem, Node>? OnInstantiation { get; set; }
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ColdMint.scripts.inventory;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Item manager</para>
|
||||
/// <para>物品管理器</para>
|
||||
/// </summary>
|
||||
public static class ItemManager
|
||||
{
|
||||
private static Dictionary<string, IItem> _dictionary = new Dictionary<string, IItem>();
|
||||
|
||||
/// <summary>
|
||||
/// <para>Add items to Item Manager</para>
|
||||
/// <para>在物品管理器内添加物品</para>
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public static void AddItem(IItem item)
|
||||
{
|
||||
if (_dictionary.ContainsKey(item.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_dictionary.Add(item.Id, item);
|
||||
}
|
||||
}
|
|
@ -9,33 +9,29 @@ namespace ColdMint.scripts.inventory;
|
|||
public partial class ItemSlotNode : MarginContainer
|
||||
{
|
||||
private IItem? _item;
|
||||
private TextureRect _backgroundTextureRect;
|
||||
private TextureRect _iconTextureRect;
|
||||
private Label _quantityLabel;
|
||||
private Control _control;
|
||||
private TextureRect? _backgroundTextureRect;
|
||||
private TextureRect? _iconTextureRect;
|
||||
private Label? _quantityLabel;
|
||||
private Control? _control;
|
||||
private bool _isSelect;
|
||||
private Texture2D _backgroundTexture;
|
||||
private Texture2D _backgroundTextureWhenSelect;
|
||||
private Texture2D? _backgroundTexture;
|
||||
private Texture2D? _backgroundTextureWhenSelect;
|
||||
|
||||
public bool IsSelect
|
||||
{
|
||||
get => _isSelect;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
if (_backgroundTextureRect != null)
|
||||
{
|
||||
_backgroundTextureRect.Texture = _backgroundTextureWhenSelect;
|
||||
}
|
||||
else
|
||||
{
|
||||
_backgroundTextureRect.Texture = _backgroundTexture;
|
||||
_backgroundTextureRect.Texture = value ? _backgroundTextureWhenSelect : _backgroundTexture;
|
||||
}
|
||||
|
||||
_isSelect = value;
|
||||
}
|
||||
}
|
||||
|
||||
public TextureRect BackgroundTextureRect => _backgroundTextureRect;
|
||||
public TextureRect? BackgroundTextureRect => _backgroundTextureRect;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Get the items in the item slot</para>
|
||||
|
@ -88,9 +84,20 @@ public partial class ItemSlotNode : MarginContainer
|
|||
public void ClearItem()
|
||||
{
|
||||
_item = null;
|
||||
_iconTextureRect.Texture = null;
|
||||
_control.TooltipText = null;
|
||||
_quantityLabel.Visible = false;
|
||||
if (_iconTextureRect != null)
|
||||
{
|
||||
_iconTextureRect.Texture = null;
|
||||
}
|
||||
|
||||
if (_control != null)
|
||||
{
|
||||
_control.TooltipText = null;
|
||||
}
|
||||
|
||||
if (_quantityLabel != null)
|
||||
{
|
||||
_quantityLabel.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -140,7 +147,7 @@ public partial class ItemSlotNode : MarginContainer
|
|||
|
||||
if (_item == null)
|
||||
{
|
||||
if (item.Icon != null)
|
||||
if (item.Icon != null && _iconTextureRect != null)
|
||||
{
|
||||
_iconTextureRect.Texture = item.Icon;
|
||||
}
|
||||
|
@ -167,6 +174,11 @@ public partial class ItemSlotNode : MarginContainer
|
|||
/// <param name="item"></param>
|
||||
private void UpdateTooltipText(IItem item)
|
||||
{
|
||||
if (_control == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config.IsDebug())
|
||||
{
|
||||
_control.TooltipText = string.Format(TranslationServer.Translate("item_prompt_debug"), item.Id,
|
||||
|
@ -188,6 +200,11 @@ public partial class ItemSlotNode : MarginContainer
|
|||
/// <param name="quantity"></param>
|
||||
private void UpdateQuantityLabel(int? quantity)
|
||||
{
|
||||
if (_quantityLabel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (quantity)
|
||||
{
|
||||
case null:
|
||||
|
|
|
@ -71,6 +71,7 @@ public partial class GameSceneLoader : SceneLoaderTemplate
|
|||
LogCat.LogError("map_generator_is_not_configured");
|
||||
return;
|
||||
}
|
||||
|
||||
await _mapGenerator.Generate(_mapGeneratorConfig);
|
||||
var packedScene = GD.Load<PackedScene>("res://prefab/entitys/Character.tscn");
|
||||
//Register players in the holder
|
||||
|
@ -100,7 +101,8 @@ public partial class GameSceneLoader : SceneLoaderTemplate
|
|||
{
|
||||
var wn = (Node2D)w.Instantiate();
|
||||
wn.Position = new Vector2(55, 90);
|
||||
GameSceneNodeHolder.WeaponContainer.AddChild(wn);
|
||||
var weaponContainer = GameSceneNodeHolder.WeaponContainer;
|
||||
weaponContainer?.AddChild(wn);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ public interface IUiLoaderContract
|
|||
/// <para>This method loads the information that the UI needs to display, such as: button text, images, etc.</para>
|
||||
/// <para>在此方法内加载UI需要显示的信息,例如:按钮的文字,图像等。</para>
|
||||
/// </summary>
|
||||
void InitializeUI();
|
||||
void InitializeUi();
|
||||
|
||||
/// <summary>
|
||||
/// <para>initialization data</para>
|
||||
|
@ -26,5 +26,5 @@ public interface IUiLoaderContract
|
|||
/// <para>Load user actions that the UI needs to respond to, such as setting a click event for a button.</para>
|
||||
/// <para>加载UI需要响应的用户行动,例如为按钮设置点击事件。</para>
|
||||
/// </summary>
|
||||
void LoadUIActions();
|
||||
void LoadUiActions();
|
||||
}
|
|
@ -3,7 +3,6 @@ using System.IO;
|
|||
using System.Text;
|
||||
using ColdMint.scripts.camp;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.inventory;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.loader.uiLoader;
|
||||
|
@ -14,75 +13,81 @@ namespace ColdMint.scripts.loader.uiLoader;
|
|||
/// </summary>
|
||||
public partial class MainMenuLoader : UiLoaderTemplate
|
||||
{
|
||||
private Button _startGameButton;
|
||||
private Label _copyrightLabel;
|
||||
private StringBuilder _copyrightBuilder;
|
||||
private PackedScene _gameScene;
|
||||
private Label _sloganLabel;
|
||||
private Label _versionLabel;
|
||||
private Button? _startGameButton;
|
||||
private Label? _copyrightLabel;
|
||||
private StringBuilder? _copyrightBuilder;
|
||||
private PackedScene? _gameScene;
|
||||
private Label? _sloganLabel;
|
||||
private Label? _versionLabel;
|
||||
|
||||
public override async void InitializeData()
|
||||
{
|
||||
//Register the corresponding encoding provider to solve the problem of garbled Chinese path of the compressed package
|
||||
//注册对应的编码提供程序,解决压缩包中文路径乱码问题
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
//创建游戏数据文件夹
|
||||
var dataPath = Config.GetGameDataDirectory();
|
||||
if (dataPath != null && !Directory.Exists(dataPath))
|
||||
{
|
||||
Directory.CreateDirectory(dataPath);
|
||||
}
|
||||
//Registered article
|
||||
//注册物品
|
||||
// var staffOfTheUndead = new CommonItem();
|
||||
// staffOfTheUndead.Name = "item_staff_of_the_undead";
|
||||
// staffOfTheUndead.Id = "staff_of_the_undead";
|
||||
// staffOfTheUndead.Description = "";
|
||||
//Registered camp
|
||||
//注册阵营
|
||||
var defaultCamp = new Camp(Config.CampId.Default);
|
||||
defaultCamp.FriendInjury = true;
|
||||
CampManager.SetDefaultCamp(defaultCamp);
|
||||
var mazoku = new Camp(Config.CampId.Mazoku);
|
||||
CampManager.AddCamp(mazoku);
|
||||
var aborigines = new Camp(Config.CampId.Aborigines);
|
||||
CampManager.AddCamp(aborigines);
|
||||
_gameScene = (PackedScene)GD.Load("res://scenes/game.tscn");
|
||||
}
|
||||
public override void InitializeData()
|
||||
{
|
||||
//Register the corresponding encoding provider to solve the problem of garbled Chinese path of the compressed package
|
||||
//注册对应的编码提供程序,解决压缩包中文路径乱码问题
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
//创建游戏数据文件夹
|
||||
var dataPath = Config.GetGameDataDirectory();
|
||||
if (!Directory.Exists(dataPath))
|
||||
{
|
||||
Directory.CreateDirectory(dataPath);
|
||||
}
|
||||
|
||||
public override void InitializeUI()
|
||||
{
|
||||
_startGameButton = GetNode<Button>("StartGameButton");
|
||||
_startGameButton.GrabFocus();
|
||||
_versionLabel = GetNode<Label>("VBoxContainer2/VersionLabel");
|
||||
//Generative copyright
|
||||
//生成版权
|
||||
_copyrightLabel = GetNode<Label>("VBoxContainer/CopyrightLabel");
|
||||
_sloganLabel = GetNode<Label>("CenterContainer2/SloganLabel");
|
||||
_copyrightBuilder = new StringBuilder();
|
||||
_copyrightBuilder.Append('\u00a9');
|
||||
var currentYear = DateTime.Now.Year;
|
||||
_copyrightBuilder.Append(Config.CreationYear);
|
||||
if (currentYear != Config.CreationYear)
|
||||
{
|
||||
_copyrightBuilder.Append('-');
|
||||
_copyrightBuilder.Append(currentYear);
|
||||
}
|
||||
//Registered article
|
||||
//注册物品
|
||||
// var staffOfTheUndead = new CommonItem();
|
||||
// staffOfTheUndead.Name = "item_staff_of_the_undead";
|
||||
// staffOfTheUndead.Id = "staff_of_the_undead";
|
||||
// staffOfTheUndead.Description = "";
|
||||
//Registered camp
|
||||
//注册阵营
|
||||
var defaultCamp = new Camp(Config.CampId.Default)
|
||||
{
|
||||
FriendInjury = true
|
||||
};
|
||||
CampManager.SetDefaultCamp(defaultCamp);
|
||||
var mazoku = new Camp(Config.CampId.Mazoku);
|
||||
CampManager.AddCamp(mazoku);
|
||||
var aborigines = new Camp(Config.CampId.Aborigines);
|
||||
CampManager.AddCamp(aborigines);
|
||||
_gameScene = (PackedScene)GD.Load("res://scenes/game.tscn");
|
||||
}
|
||||
|
||||
_copyrightBuilder.Append(' ');
|
||||
_copyrightBuilder.Append(Config.CompanyName);
|
||||
_copyrightBuilder.Append(" all rights reserved.");
|
||||
_copyrightLabel.Text = _copyrightBuilder.ToString();
|
||||
_versionLabel.Text = "ver." + Config.GetVersion();
|
||||
_sloganLabel.Text = SloganProvider.GetSlogan();
|
||||
}
|
||||
public override void InitializeUi()
|
||||
{
|
||||
_startGameButton = GetNode<Button>("StartGameButton");
|
||||
_startGameButton.GrabFocus();
|
||||
_versionLabel = GetNode<Label>("VBoxContainer2/VersionLabel");
|
||||
//Generative copyright
|
||||
//生成版权
|
||||
_copyrightLabel = GetNode<Label>("VBoxContainer/CopyrightLabel");
|
||||
_sloganLabel = GetNode<Label>("CenterContainer2/SloganLabel");
|
||||
_copyrightBuilder = new StringBuilder();
|
||||
_copyrightBuilder.Append('\u00a9');
|
||||
var currentYear = DateTime.Now.Year;
|
||||
_copyrightBuilder.Append(Config.CreationYear);
|
||||
if (currentYear != Config.CreationYear)
|
||||
{
|
||||
_copyrightBuilder.Append('-');
|
||||
_copyrightBuilder.Append(currentYear);
|
||||
}
|
||||
|
||||
public override void LoadUIActions()
|
||||
{
|
||||
_startGameButton.Pressed += () =>
|
||||
{
|
||||
LogCat.Log("start_game");
|
||||
GetTree().ChangeSceneToPacked(_gameScene);
|
||||
};
|
||||
}
|
||||
}
|
||||
_copyrightBuilder.Append(' ');
|
||||
_copyrightBuilder.Append(Config.CompanyName);
|
||||
_copyrightBuilder.Append(" all rights reserved.");
|
||||
_copyrightLabel.Text = _copyrightBuilder.ToString();
|
||||
_versionLabel.Text = "ver." + Config.GetVersion();
|
||||
_sloganLabel.Text = SloganProvider.GetSlogan();
|
||||
}
|
||||
|
||||
public override void LoadUiActions()
|
||||
{
|
||||
if (_startGameButton != null)
|
||||
{
|
||||
_startGameButton.Pressed += () =>
|
||||
{
|
||||
LogCat.Log("start_game");
|
||||
GetTree().ChangeSceneToPacked(_gameScene);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,12 +11,12 @@ public partial class UiLoaderTemplate : Control, IUiLoaderContract
|
|||
public sealed override void _Ready()
|
||||
{
|
||||
InitializeData();
|
||||
InitializeUI();
|
||||
LoadUIActions();
|
||||
InitializeUi();
|
||||
LoadUiActions();
|
||||
}
|
||||
|
||||
|
||||
public virtual void InitializeUI()
|
||||
public virtual void InitializeUi()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ public partial class UiLoaderTemplate : Control, IUiLoaderContract
|
|||
|
||||
}
|
||||
|
||||
public virtual void LoadUIActions()
|
||||
public virtual void LoadUiActions()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -10,90 +10,102 @@ namespace ColdMint.scripts.map;
|
|||
|
||||
public class MapGenerator : IMapGenerator
|
||||
{
|
||||
public int TimeOutPeriod { get; set; }
|
||||
public IRoomSlotsMatcher RoomSlotsMatcher { get; set; }
|
||||
public IRoomHolder RoomHolder { get; set; }
|
||||
public int TimeOutPeriod { get; set; }
|
||||
public IRoomSlotsMatcher? RoomSlotsMatcher { get; set; }
|
||||
public IRoomHolder? RoomHolder { get; set; }
|
||||
|
||||
public IRoomPlacer RoomPlacer { get; set; }
|
||||
public IRoomPlacer? RoomPlacer { get; set; }
|
||||
|
||||
public IRoomProvider RoomProvider { get; set; }
|
||||
public IRoomProvider? RoomProvider { get; set; }
|
||||
|
||||
public async Task Generate(IMapGeneratorConfig mapGeneratorConfig)
|
||||
{
|
||||
if (RoomPlacer == null || RoomHolder == null || RoomProvider == null || RoomSlotsMatcher == null)
|
||||
{
|
||||
PrintMissingParametersError();
|
||||
return;
|
||||
}
|
||||
public async Task Generate(IMapGeneratorConfig mapGeneratorConfig)
|
||||
{
|
||||
if (RoomPlacer == null || RoomHolder == null || RoomProvider == null || RoomSlotsMatcher == null ||
|
||||
RoomProvider.InitialRoom == null)
|
||||
{
|
||||
PrintMissingParametersError();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var roomPlacerConfig = new RoomPlacerConfig();
|
||||
//获取原点
|
||||
var origin = Vector2.Zero;
|
||||
//在提供者哪里获取房间,并放置他(首次拿初始房间)
|
||||
var originRoom =
|
||||
RoomFactory.CreateRoom(RoomProvider.InitialRoom.RoomResPath);
|
||||
await PlaceRoom(origin, originRoom);
|
||||
var endTime = DateTime.Now + TimeSpan.FromSeconds(TimeOutPeriod);
|
||||
while (RoomHolder.PlacedRoomNumber < mapGeneratorConfig.RoomCount)
|
||||
{
|
||||
if (DateTime.Now > endTime)
|
||||
{
|
||||
LogCat.LogError("connected_room_timeout");
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
var roomPlacerConfig = new RoomPlacerConfig();
|
||||
//获取原点
|
||||
var origin = Vector2.Zero;
|
||||
//在提供者哪里获取房间,并放置他(首次拿初始房间)
|
||||
var originRoom =
|
||||
RoomFactory.CreateRoom(RoomProvider.InitialRoom.RoomResPath);
|
||||
await PlaceRoom(origin, originRoom);
|
||||
var endTime = DateTime.Now + TimeSpan.FromSeconds(TimeOutPeriod);
|
||||
while (RoomHolder.PlacedRoomNumber < mapGeneratorConfig.RoomCount)
|
||||
{
|
||||
if (DateTime.Now > endTime)
|
||||
{
|
||||
LogCat.LogError("connected_room_timeout");
|
||||
break;
|
||||
}
|
||||
|
||||
//我们会一直尝试放置房间,直到达到指定的数量
|
||||
var newRoom =
|
||||
RoomFactory.CreateRoom(RoomProvider.GetRoomRes(RoomHolder.PlacedRoomNumber, mapGeneratorConfig)
|
||||
.RoomResPath);
|
||||
if (await RoomSlotsMatcher.IsMatch(RoomHolder.LastRoom, newRoom))
|
||||
{
|
||||
LogCat.Log("匹配成功" + RoomSlotsMatcher.LastMatchedMainSlot.DistanceToMidpointOfRoom[0] + " " +
|
||||
RoomSlotsMatcher.LastMatchedMainSlot.DistanceToMidpointOfRoom[1] + "到" +
|
||||
RoomSlotsMatcher.LastMatchedMinorSlot.DistanceToMidpointOfRoom[0] + " " +
|
||||
RoomSlotsMatcher.LastMatchedMinorSlot.DistanceToMidpointOfRoom[1]);
|
||||
await PlaceRoom(
|
||||
await RoomPlacer.CalculatedPosition(originRoom, newRoom, RoomSlotsMatcher.LastMatchedMainSlot,
|
||||
RoomSlotsMatcher.LastMatchedMinorSlot, roomPlacerConfig), newRoom);
|
||||
originRoom = newRoom;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogCat.LogError(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
//我们会一直尝试放置房间,直到达到指定的数量
|
||||
var roomRes = RoomProvider.GetRoomRes(RoomHolder.PlacedRoomNumber, mapGeneratorConfig);
|
||||
if (roomRes == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>PrintMissingParametersError</para>
|
||||
/// <para>打印缺少参数错误</para>
|
||||
/// </summary>
|
||||
private void PrintMissingParametersError()
|
||||
{
|
||||
LogCat.LogError("missing_parameters");
|
||||
}
|
||||
var newRoom =
|
||||
RoomFactory.CreateRoom(roomRes
|
||||
.RoomResPath);
|
||||
if (await RoomSlotsMatcher.IsMatch(RoomHolder.LastRoom, newRoom))
|
||||
{
|
||||
// LogCat.Log("匹配成功" + RoomSlotsMatcher.LastMatchedMainSlot.DistanceToMidpointOfRoom[0] + " " +
|
||||
// RoomSlotsMatcher.LastMatchedMainSlot.DistanceToMidpointOfRoom[1] + "到" +
|
||||
// RoomSlotsMatcher.LastMatchedMinorSlot.DistanceToMidpointOfRoom[0] + " " +
|
||||
// RoomSlotsMatcher.LastMatchedMinorSlot.DistanceToMidpointOfRoom[1]);
|
||||
await PlaceRoom(
|
||||
await RoomPlacer.CalculatedPosition(originRoom, newRoom, RoomSlotsMatcher.LastMatchedMainSlot,
|
||||
RoomSlotsMatcher.LastMatchedMinorSlot, roomPlacerConfig), newRoom);
|
||||
originRoom = newRoom;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogCat.LogError(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>PlaceRoom</para>
|
||||
/// <para>放置房间</para>
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="room"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> PlaceRoom(Vector2 position, IRoom room)
|
||||
{
|
||||
if (await RoomPlacer.PlaceRoom(position, room))
|
||||
{
|
||||
RoomHolder.AddRoom(room);
|
||||
LogCat.Log("我要放置房间,但是成功");
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>PrintMissingParametersError</para>
|
||||
/// <para>打印缺少参数错误</para>
|
||||
/// </summary>
|
||||
private void PrintMissingParametersError()
|
||||
{
|
||||
LogCat.LogError("missing_parameters");
|
||||
}
|
||||
|
||||
LogCat.Log("我要放置房间,但是失败了");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>PlaceRoom</para>
|
||||
/// <para>放置房间</para>
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="room"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> PlaceRoom(Vector2 position, IRoom room)
|
||||
{
|
||||
if (RoomPlacer == null || RoomHolder == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await RoomPlacer.PlaceRoom(position, room))
|
||||
{
|
||||
RoomHolder.AddRoom(room);
|
||||
// LogCat.Log("我要放置房间,但是成功");
|
||||
return true;
|
||||
}
|
||||
|
||||
// LogCat.Log("我要放置房间,但是失败了");
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.map.interfaces;
|
||||
using ColdMint.scripts.map.interfaces;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.map;
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
using System;
|
||||
using Godot;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.map;
|
||||
|
||||
public partial class RoomAreaDetector : Node2D
|
||||
{
|
||||
private Area2D _area2D;
|
||||
private Area2D? _area2D;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
_area2D = GetNode<Area2D>("Area2D");
|
||||
_area2D.BodyEntered += (body =>
|
||||
{
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.map.interfaces;
|
||||
using ColdMint.scripts.map.room;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.map.RoomProvider;
|
||||
|
||||
|
@ -23,7 +21,7 @@ public class RoomProvider : IRoomProvider
|
|||
///<para>If the initial room is not set, the first room added will be automatically set as the initial room</para>
|
||||
///<para>若未设置初始房间,那么第一次添加的房间将被自动设置为初始房间</para>
|
||||
/// </remarks>
|
||||
/// <param name="resPath"></param>
|
||||
/// <param name="roomTemplate"></param>
|
||||
public void AddRoom(RoomTemplate roomTemplate)
|
||||
{
|
||||
if (InitialRoom == null)
|
||||
|
@ -36,13 +34,13 @@ public class RoomProvider : IRoomProvider
|
|||
}
|
||||
|
||||
|
||||
public IRoomTemplate InitialRoom { get; set; }
|
||||
public IRoomTemplate? InitialRoom { get; set; }
|
||||
|
||||
public IRoomTemplate GetRoomRes(int index, IMapGeneratorConfig config)
|
||||
{
|
||||
var indexInList = config.RandomNumberGenerator.RandiRange(0, _roomTemplates.Count - 1);
|
||||
LogCat.Log("种子" + config.Seed + "获取" + index + "返回" + indexInList);
|
||||
IRoomTemplate result = _roomTemplates[indexInList];
|
||||
|
||||
//添加一次使用次数,当模板不能再次使用时,从列表内移除。
|
||||
result.AddUsedNumber();
|
||||
if (!result.CanUse)
|
||||
|
|
|
@ -5,6 +5,7 @@ using Godot;
|
|||
namespace ColdMint.scripts.map.dateBean;
|
||||
|
||||
/// <summary>
|
||||
/// <para>RoomSlot</para>
|
||||
/// <para>槽</para>
|
||||
/// </summary>
|
||||
public class RoomSlot
|
||||
|
@ -23,10 +24,7 @@ public class RoomSlot
|
|||
/// <para>Is it a horizontal slot</para>
|
||||
/// <para>是水平方向的槽吗</para>
|
||||
/// </summary>
|
||||
public bool IsHorizontal
|
||||
{
|
||||
get { return StartPosition.Y == EndPosition.Y; }
|
||||
}
|
||||
public bool IsHorizontal => StartPosition.Y == EndPosition.Y;
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
@ -37,13 +35,7 @@ public class RoomSlot
|
|||
///<para>Element 1 represents left and right, element 2 represents up and down</para>
|
||||
///<para>元素1,代表左右,元素2代表上下</para>
|
||||
/// </remarks>
|
||||
public CoordinateUtils.OrientationDescribe[] DistanceToMidpointOfRoom { get; set; }
|
||||
public CoordinateUtils.OrientationDescribe[]? DistanceToMidpointOfRoom { get; set; }
|
||||
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return Math.Max(Math.Abs(EndPosition.X - StartPosition.X), Math.Abs(EndPosition.Y - StartPosition.Y)) + 1;
|
||||
}
|
||||
}
|
||||
public int Length => Math.Max(Math.Abs(EndPosition.X - StartPosition.X), Math.Abs(EndPosition.Y - StartPosition.Y)) + 1;
|
||||
}
|
|
@ -14,11 +14,11 @@ public interface IMapGenerator
|
|||
/// </remarks>
|
||||
public int TimeOutPeriod { get; set; }
|
||||
|
||||
public IRoomSlotsMatcher RoomSlotsMatcher { get; set; }
|
||||
public IRoomHolder RoomHolder { get; set; }
|
||||
public IRoomPlacer RoomPlacer { get; set; }
|
||||
public IRoomSlotsMatcher? RoomSlotsMatcher { get; set; }
|
||||
public IRoomHolder? RoomHolder { get; set; }
|
||||
public IRoomPlacer? RoomPlacer { get; set; }
|
||||
|
||||
public IRoomProvider RoomProvider { get; set; }
|
||||
public IRoomProvider? RoomProvider { get; set; }
|
||||
|
||||
Task Generate(IMapGeneratorConfig mapGeneratorConfig);
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.map.dateBean;
|
||||
using ColdMint.scripts.map.dateBean;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.map.interfaces;
|
||||
|
@ -14,19 +13,19 @@ public interface IRoom
|
|||
/// <para>Set room scene</para>
|
||||
/// <para>设置房间场景</para>
|
||||
/// </summary>
|
||||
PackedScene RoomScene { get; set; }
|
||||
PackedScene? RoomScene { get; set; }
|
||||
|
||||
TileMap TileMap { get; set; }
|
||||
TileMap? TileMap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Gets the root node of the room</para>
|
||||
/// <para>获取房间的根节点</para>
|
||||
/// </summary>
|
||||
Node2D RootNode { get; }
|
||||
Node2D? RootNode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>The room holds the corresponding slot data</para>
|
||||
/// <para>房间持有对应的插槽数据</para>
|
||||
/// </summary>
|
||||
RoomSlot[] RoomSlots { get; }
|
||||
RoomSlot?[]? RoomSlots { get; }
|
||||
}
|
|
@ -8,7 +8,7 @@ public interface IRoomHolder
|
|||
/// <para>LastRoom</para>
|
||||
/// <para>最后添加的房间</para>
|
||||
/// </summary>
|
||||
IRoom LastRoom { get; }
|
||||
IRoom? LastRoom { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Number of rooms that have been placed</para>
|
||||
|
|
|
@ -31,7 +31,10 @@ public interface IRoomPlacer
|
|||
/// </summary>
|
||||
/// <param name="mainRoom"></param>
|
||||
/// <param name="newRoom"></param>
|
||||
/// <param name="mainRoomSlot"></param>
|
||||
/// <param name="newRoomSlot"></param>
|
||||
/// <param name="roomPlacerConfig"></param>
|
||||
/// <returns></returns>
|
||||
public Task<Vector2> CalculatedPosition(IRoom mainRoom, IRoom newRoom, RoomSlot mainRoomSlot, RoomSlot newRoomSlot,
|
||||
public Task<Vector2> CalculatedPosition(IRoom mainRoom, IRoom newRoom, RoomSlot? mainRoomSlot, RoomSlot? newRoomSlot,
|
||||
RoomPlacerConfig roomPlacerConfig);
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ColdMint.scripts.map.interfaces;
|
||||
namespace ColdMint.scripts.map.interfaces;
|
||||
|
||||
public interface IRoomProvider
|
||||
{
|
||||
|
@ -8,7 +6,7 @@ public interface IRoomProvider
|
|||
/// <para>Initial room</para>
|
||||
/// <para>初始房间</para>
|
||||
/// </summary>
|
||||
IRoomTemplate InitialRoom { get; set; }
|
||||
IRoomTemplate? InitialRoom { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
@ -18,5 +16,5 @@ public interface IRoomProvider
|
|||
/// <param name="index"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
IRoomTemplate GetRoomRes(int index, IMapGeneratorConfig config);
|
||||
IRoomTemplate? GetRoomRes(int index, IMapGeneratorConfig config);
|
||||
}
|
|
@ -16,18 +16,18 @@ public interface IRoomSlotsMatcher
|
|||
/// <param name="mainRoom"></param>
|
||||
/// <param name="newRoom"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> IsMatch(IRoom mainRoom, IRoom newRoom);
|
||||
Task<bool> IsMatch(IRoom? mainRoom, IRoom newRoom);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>LastMatchedMainSlot</para>
|
||||
/// <para>最后匹配的主要插槽</para>
|
||||
/// </summary>
|
||||
RoomSlot LastMatchedMainSlot { get; }
|
||||
RoomSlot? LastMatchedMainSlot { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>LastMatchedMinorSlot</para>
|
||||
/// <para>最后匹配的次要插槽</para>
|
||||
/// </summary>
|
||||
RoomSlot LastMatchedMinorSlot { get; }
|
||||
RoomSlot? LastMatchedMinorSlot { get; }
|
||||
}
|
|
@ -5,7 +5,6 @@ using ColdMint.scripts.map.dateBean;
|
|||
using ColdMint.scripts.map.interfaces;
|
||||
using ColdMint.scripts.utils;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace ColdMint.scripts.map.room;
|
||||
|
||||
|
@ -19,17 +18,17 @@ namespace ColdMint.scripts.map.room;
|
|||
/// </remarks>
|
||||
public class Room : IRoom
|
||||
{
|
||||
private Node2D _rootNode;
|
||||
private RoomSlot[] _roomSlots;
|
||||
private TileMap _tileMap;
|
||||
private Node2D? _rootNode;
|
||||
private RoomSlot?[]? _roomSlots;
|
||||
private TileMap? _tileMap;
|
||||
|
||||
public PackedScene RoomScene
|
||||
public PackedScene? RoomScene
|
||||
{
|
||||
get => default;
|
||||
set => AnalyzeRoomData(value);
|
||||
}
|
||||
|
||||
public TileMap TileMap
|
||||
public TileMap? TileMap
|
||||
{
|
||||
get => _tileMap;
|
||||
set => _tileMap = value;
|
||||
|
@ -40,9 +39,9 @@ public class Room : IRoom
|
|||
/// <para>解析房间的数据</para>
|
||||
/// </summary>
|
||||
/// <param name="packedScene"></param>
|
||||
private void AnalyzeRoomData(PackedScene packedScene)
|
||||
private void AnalyzeRoomData(PackedScene? packedScene)
|
||||
{
|
||||
var node = packedScene.Instantiate();
|
||||
var node = packedScene?.Instantiate();
|
||||
if (node is not Node2D node2D)
|
||||
{
|
||||
//房间节点不是Node2D类型,抛出异常
|
||||
|
@ -56,19 +55,25 @@ public class Room : IRoom
|
|||
node2D.GetNode<Node2D>("RoomSlotList"));
|
||||
}
|
||||
|
||||
public Node2D RootNode => _rootNode;
|
||||
public Node2D? RootNode => _rootNode;
|
||||
|
||||
public RoomSlot[] RoomSlots => _roomSlots;
|
||||
public RoomSlot?[]? RoomSlots => _roomSlots;
|
||||
|
||||
/// <summary>
|
||||
/// <para>GetRoomSlots</para>
|
||||
/// <para>在房间内获取所有插槽</para>
|
||||
/// </summary>
|
||||
/// <param name="tileMap"></param>
|
||||
/// <param name="roomArea2D"></param>
|
||||
/// <param name="slotList"></param>
|
||||
/// <returns></returns>
|
||||
private RoomSlot[] GetRoomSlots(TileMap tileMap, Area2D roomArea2D, Node2D slotList)
|
||||
private RoomSlot?[]? GetRoomSlots(TileMap? tileMap, Area2D roomArea2D, Node2D slotList)
|
||||
{
|
||||
if (tileMap == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var slotCount = slotList.GetChildCount();
|
||||
if (slotCount == 0)
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ public class RoomHolder : IRoomHolder
|
|||
return true;
|
||||
}
|
||||
|
||||
public IRoom LastRoom
|
||||
public IRoom? LastRoom
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Threading.Tasks;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.map.dateBean;
|
||||
using ColdMint.scripts.map.interfaces;
|
||||
using Godot;
|
||||
|
@ -9,26 +8,39 @@ namespace ColdMint.scripts.map.RoomPlacer;
|
|||
|
||||
public class RoomPlacer : RoomPlacerTemplate
|
||||
{
|
||||
public IMapGeneratorConfig MapGeneratorConfig { get; set; }
|
||||
private Vector2 _halfCell = new Vector2(CellSize / 2, CellSize / 2);
|
||||
public IMapGeneratorConfig? MapGeneratorConfig { get; set; }
|
||||
private readonly Vector2 _halfCell = new Vector2(CellSize / 2f, CellSize / 2f);
|
||||
|
||||
public async override Task<bool> PlaceRoom(Vector2 position, IRoom room)
|
||||
public override Task<bool> PlaceRoom(Vector2 position, IRoom room)
|
||||
{
|
||||
var node = room.RootNode;
|
||||
MapGeneratorConfig.MapRoot.AddChild(node);
|
||||
if (node is Node2D node2D)
|
||||
if (MapGeneratorConfig == null)
|
||||
{
|
||||
node2D.Position = position;
|
||||
return true;
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
var node = room.RootNode;
|
||||
MapGeneratorConfig.MapRoot.AddChild(node);
|
||||
if (node is { } node2D)
|
||||
{
|
||||
//If the Node is not empty and is a 2D node.
|
||||
//如果Node不是空的,且是2D节点。
|
||||
node2D.Position = position;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
public async override Task<Vector2> CalculatedPosition(IRoom mainRoom, IRoom newRoom, RoomSlot mainRoomSlot,
|
||||
RoomSlot newRoomSlot,
|
||||
public override Task<Vector2> CalculatedPosition(IRoom mainRoom, IRoom newRoom, RoomSlot? mainRoomSlot,
|
||||
RoomSlot? newRoomSlot,
|
||||
RoomPlacerConfig roomPlacerConfig)
|
||||
{
|
||||
if (mainRoom.RootNode == null || mainRoom.TileMap == null || newRoom.TileMap == null || mainRoomSlot == null ||
|
||||
newRoomSlot == null)
|
||||
{
|
||||
return Task.FromResult(Vector2.Zero);
|
||||
}
|
||||
|
||||
//计算主插槽中点在世界中的位置。
|
||||
//mainRoom.RootNode.Position意为房间所在的世界位置
|
||||
//mainRoom.TileMap.MapToLocal(mainRoomSlot.StartPosition)意为主插槽在房间中的位置
|
||||
|
@ -55,6 +67,6 @@ public class RoomPlacer : RoomPlacerTemplate
|
|||
result -= newRoom.TileMap.MapToLocal(new Vector2I(0, newRoomSlot.EndPosition.Y)) - _halfCell;
|
||||
}
|
||||
|
||||
return result;
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using ColdMint.scripts.map.dateBean;
|
||||
using ColdMint.scripts.map.interfaces;
|
||||
using Godot;
|
||||
|
@ -8,29 +7,8 @@ namespace ColdMint.scripts.map.RoomPlacer;
|
|||
|
||||
public abstract class RoomPlacerTemplate : IRoomPlacer
|
||||
{
|
||||
private List<IRoom> _placedRooms;
|
||||
|
||||
protected void Init()
|
||||
{
|
||||
_placedRooms = new List<IRoom>();
|
||||
}
|
||||
|
||||
public abstract Task<bool> PlaceRoom(Vector2 position, IRoom room);
|
||||
|
||||
public abstract Task<Vector2> CalculatedPosition(IRoom mainRoom, IRoom newRoom, RoomSlot mainRoomSlot, RoomSlot newRoomSlot,
|
||||
public abstract Task<Vector2> CalculatedPosition(IRoom mainRoom, IRoom newRoom, RoomSlot? mainRoomSlot, RoomSlot? newRoomSlot,
|
||||
RoomPlacerConfig roomPlacerConfig);
|
||||
|
||||
|
||||
public int PlacedRoomNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_placedRooms == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _placedRooms.Count;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +1,54 @@
|
|||
using System.Threading.Tasks;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.map.dateBean;
|
||||
using ColdMint.scripts.map.interfaces;
|
||||
using ColdMint.scripts.utils;
|
||||
|
||||
namespace ColdMint.scripts.map.slotsMatcher;
|
||||
|
||||
public class RoomSlotsMatcher : IRoomSlotsMatcher
|
||||
{
|
||||
private RoomSlot _lastMatchedMainSlot;
|
||||
private RoomSlot _lastMatchedMinorSlot;
|
||||
private RoomSlot? _lastMatchedMainSlot;
|
||||
private RoomSlot? _lastMatchedMinorSlot;
|
||||
|
||||
public async Task<bool> IsMatch(IRoom mainRoom, IRoom newRoom)
|
||||
public Task<bool> IsMatch(IRoom? mainRoom, IRoom newRoom)
|
||||
{
|
||||
foreach (var mainRoomSlot in mainRoom.RoomSlots)
|
||||
if (mainRoom == null)
|
||||
{
|
||||
if (mainRoomSlot.Matched)
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
var roomSlots = mainRoom.RoomSlots;
|
||||
if (roomSlots == null)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
var newRoomSlots = newRoom.RoomSlots;
|
||||
if (newRoomSlots == null)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
foreach (var mainRoomSlot in roomSlots)
|
||||
{
|
||||
if (mainRoomSlot == null || mainRoomSlot.Matched)
|
||||
{
|
||||
//如果已经匹配过了,就不再匹配
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var newRoomSlot in newRoom.RoomSlots)
|
||||
foreach (var newRoomSlot in newRoomSlots)
|
||||
{
|
||||
if (newRoomSlot == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newRoomSlot.Matched)
|
||||
{
|
||||
//如果已经匹配过了,就不再匹配
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mainRoomSlot.IsHorizontal != newRoomSlot.IsHorizontal)
|
||||
{
|
||||
continue;
|
||||
|
@ -40,8 +61,11 @@ public class RoomSlotsMatcher : IRoomSlotsMatcher
|
|||
|
||||
var distanceToMidpointOfRoom = mainRoomSlot.DistanceToMidpointOfRoom;
|
||||
var newDistanceToMidpointOfRoom = newRoomSlot.DistanceToMidpointOfRoom;
|
||||
LogCat.Log("尝试匹配" + distanceToMidpointOfRoom[0] + " " + distanceToMidpointOfRoom[1] + " 到 " +
|
||||
newDistanceToMidpointOfRoom[0] + " " + newDistanceToMidpointOfRoom[1]);
|
||||
if (distanceToMidpointOfRoom == null || newDistanceToMidpointOfRoom == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (distanceToMidpointOfRoom[0] == newDistanceToMidpointOfRoom[0] &&
|
||||
distanceToMidpointOfRoom[1] == newDistanceToMidpointOfRoom[1])
|
||||
{
|
||||
|
@ -52,13 +76,13 @@ public class RoomSlotsMatcher : IRoomSlotsMatcher
|
|||
newRoomSlot.Matched = true;
|
||||
_lastMatchedMainSlot = mainRoomSlot;
|
||||
_lastMatchedMinorSlot = newRoomSlot;
|
||||
return true;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
public RoomSlot LastMatchedMainSlot => _lastMatchedMainSlot;
|
||||
public RoomSlot LastMatchedMinorSlot => _lastMatchedMinorSlot;
|
||||
public RoomSlot? LastMatchedMainSlot => _lastMatchedMainSlot;
|
||||
public RoomSlot? LastMatchedMinorSlot => _lastMatchedMinorSlot;
|
||||
}
|
|
@ -2,7 +2,6 @@ using System;
|
|||
using ColdMint.scripts.camp;
|
||||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.damage;
|
||||
using ColdMint.scripts.debug;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.projectile;
|
||||
|
@ -13,29 +12,29 @@ namespace ColdMint.scripts.projectile;
|
|||
/// </summary>
|
||||
public partial class ProjectileTemplate : CharacterBody2D
|
||||
{
|
||||
protected Timer _timer;
|
||||
protected Timer? Timer;
|
||||
|
||||
protected double _life;
|
||||
protected double Life;
|
||||
|
||||
//The durability of the projectile
|
||||
//抛射体的耐久度
|
||||
//When the projectile hits the object, the durability will be reduced, and when the durability is less than or equal to 0, the projectile will be destroyed
|
||||
//当抛射体撞击到物体时,会减少耐久度,当耐久度小于等于0时,销毁抛射体
|
||||
protected double _durability;
|
||||
protected double Durability;
|
||||
|
||||
protected int _maxDamage;
|
||||
protected int _minDamage;
|
||||
protected int _damageType;
|
||||
protected int MaxDamage;
|
||||
protected int MinDamage;
|
||||
protected int DamageType;
|
||||
|
||||
//We use the Time node to specify when to destroy the projectile
|
||||
//我们用Time节点来指定何时销毁抛射体
|
||||
private Timer timer;
|
||||
private Timer? _timer;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The impact area of the bullet</para>
|
||||
/// <para>子弹的碰撞区域</para>
|
||||
/// </summary>
|
||||
protected Area2D _area2D;
|
||||
protected Area2D? Area2D;
|
||||
|
||||
/// <summary>
|
||||
/// <para>knockback</para>
|
||||
|
@ -45,7 +44,7 @@ public partial class ProjectileTemplate : CharacterBody2D
|
|||
///<para>How much force does it have when hitting the character? Unit: Number of cells,The X direction of the force is inferred automatically.</para>
|
||||
///<para>当击中角色时带有多大的力?单位:格数,力的X方向是自动推断的。</para>
|
||||
/// </remarks>
|
||||
protected Vector2 _knockbackForce;
|
||||
protected Vector2 KnockbackForce;
|
||||
|
||||
public float Speed
|
||||
{
|
||||
|
@ -56,34 +55,34 @@ public partial class ProjectileTemplate : CharacterBody2D
|
|||
/// <para>The master of the weapon</para>
|
||||
/// <para>武器的主人</para>
|
||||
/// </summary>
|
||||
public Node2D Owner { get; set; }
|
||||
public new Node2D? Owner { get; set; }
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
//子弹的碰撞检测区域
|
||||
_area2D = GetNode<Area2D>("CollisionDetectionArea");
|
||||
_area2D.Monitoring = true;
|
||||
_area2D.BodyEntered += OnBodyEnter;
|
||||
_area2D.BodyExited += OnBodyExited;
|
||||
_durability = GetMeta("Durability", "1").AsDouble();
|
||||
_maxDamage = GetMeta("MaxDamage", "7").AsInt32();
|
||||
_minDamage = GetMeta("MinDamage", "5").AsInt32();
|
||||
_damageType = GetMeta("DamageType", Config.DamageType.Physical).AsInt32();
|
||||
_knockbackForce = GetMeta("Knockback", Vector2.Zero).AsVector2();
|
||||
Area2D = GetNode<Area2D>("CollisionDetectionArea");
|
||||
Area2D.Monitoring = true;
|
||||
Area2D.BodyEntered += OnBodyEnter;
|
||||
Area2D.BodyExited += OnBodyExited;
|
||||
Durability = GetMeta("Durability", "1").AsDouble();
|
||||
MaxDamage = GetMeta("MaxDamage", "7").AsInt32();
|
||||
MinDamage = GetMeta("MinDamage", "5").AsInt32();
|
||||
DamageType = GetMeta("DamageType", Config.DamageType.Physical).AsInt32();
|
||||
KnockbackForce = GetMeta("Knockback", Vector2.Zero).AsVector2();
|
||||
//子弹的存在时间
|
||||
_life = GetMeta("Life", "10").AsDouble();
|
||||
Life = GetMeta("Life", "10").AsDouble();
|
||||
//如果存在时间小于等于0,那么设置为存在10秒,禁止无限期存在的抛射体
|
||||
if (_life <= 0)
|
||||
if (Life <= 0)
|
||||
{
|
||||
_life = 10;
|
||||
Life = 10;
|
||||
}
|
||||
|
||||
_timer = new Timer();
|
||||
AddChild(_timer);
|
||||
_timer.WaitTime = _life;
|
||||
_timer.OneShot = true;
|
||||
_timer.Start();
|
||||
_timer.Timeout += OnTimeOut;
|
||||
Timer = new Timer();
|
||||
AddChild(Timer);
|
||||
Timer.WaitTime = Life;
|
||||
Timer.OneShot = true;
|
||||
Timer.Start();
|
||||
Timer.Timeout += OnTimeOut;
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,7 +93,7 @@ public partial class ProjectileTemplate : CharacterBody2D
|
|||
/// <param name="owner"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
private bool CanCauseHarm(Node2D owner, Node2D target)
|
||||
private bool CanCauseHarm(Node2D? owner, Node2D target)
|
||||
{
|
||||
//We must know who the owner of the bullet is in order to determine whether it should cause damage or not
|
||||
//我们必须知道子弹的主人是谁,才能判断是否应该造成伤害
|
||||
|
@ -133,7 +132,7 @@ public partial class ProjectileTemplate : CharacterBody2D
|
|||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
/// <param name="target"></param>
|
||||
private void DoDamage(Node2D owner, Node2D target)
|
||||
private void DoDamage(Node2D? owner, Node2D target)
|
||||
{
|
||||
if (target is not CharacterTemplate characterTemplate)
|
||||
{
|
||||
|
@ -144,18 +143,18 @@ public partial class ProjectileTemplate : CharacterBody2D
|
|||
//允许造成伤害
|
||||
var damage = new Damage();
|
||||
damage.Attacker = owner;
|
||||
damage.MaxDamage = _maxDamage;
|
||||
damage.MinDamage = _minDamage;
|
||||
damage.MaxDamage = MaxDamage;
|
||||
damage.MinDamage = MinDamage;
|
||||
damage.CreateDamage();
|
||||
damage.MoveLeft = Velocity.X < 0;
|
||||
damage.Type = _damageType;
|
||||
damage.Type = DamageType;
|
||||
characterTemplate.Damage(damage);
|
||||
if (_knockbackForce != Vector2.Zero)
|
||||
if (KnockbackForce != Vector2.Zero)
|
||||
{
|
||||
//If we set the attack force, then apply the force to the object
|
||||
//如果我们设置了攻退力,那么将力应用到对象上
|
||||
var force = new Vector2();
|
||||
var forceX = Math.Abs(_knockbackForce.X);
|
||||
var forceX = Math.Abs(KnockbackForce.X);
|
||||
if (Velocity.X < 0)
|
||||
{
|
||||
//Beat back to port
|
||||
|
@ -164,7 +163,7 @@ public partial class ProjectileTemplate : CharacterBody2D
|
|||
}
|
||||
|
||||
force.X = forceX * Config.CellSize;
|
||||
force.Y = _knockbackForce.Y * Config.CellSize;
|
||||
force.Y = KnockbackForce.Y * Config.CellSize;
|
||||
characterTemplate.AddForce(force);
|
||||
}
|
||||
}
|
||||
|
@ -188,8 +187,8 @@ public partial class ProjectileTemplate : CharacterBody2D
|
|||
//请在Mask内配置子弹会和谁碰撞
|
||||
//When a bullet hits an object, its durability decreases
|
||||
//子弹撞击到物体时,耐久度减少
|
||||
_durability--;
|
||||
if (_durability <= 0)
|
||||
Durability--;
|
||||
if (Durability <= 0)
|
||||
{
|
||||
//When the durability is less than or equal to 0, destroy the bullet
|
||||
//当耐久度小于等于0时,销毁子弹
|
||||
|
|
|
@ -10,19 +10,19 @@ public class CoordinateUtils
|
|||
public enum OrientationDescribe
|
||||
{
|
||||
//上
|
||||
UP,
|
||||
Up,
|
||||
|
||||
//下
|
||||
DOWN,
|
||||
Down,
|
||||
|
||||
//左
|
||||
LEFT,
|
||||
Left,
|
||||
|
||||
//右
|
||||
RIGHT,
|
||||
Right,
|
||||
|
||||
//原点
|
||||
ORIGIN
|
||||
Origin
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -44,28 +44,28 @@ public class CoordinateUtils
|
|||
var orientationDescribes = new OrientationDescribe[2];
|
||||
if (vector2.X < 0)
|
||||
{
|
||||
orientationDescribes[0] = OrientationDescribe.LEFT;
|
||||
orientationDescribes[0] = OrientationDescribe.Left;
|
||||
}
|
||||
else if (vector2.X == 0)
|
||||
{
|
||||
orientationDescribes[0] = OrientationDescribe.ORIGIN;
|
||||
orientationDescribes[0] = OrientationDescribe.Origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
orientationDescribes[0] = OrientationDescribe.RIGHT;
|
||||
orientationDescribes[0] = OrientationDescribe.Right;
|
||||
}
|
||||
|
||||
if (vector2.Y > 0)
|
||||
{
|
||||
orientationDescribes[1] = OrientationDescribe.DOWN;
|
||||
orientationDescribes[1] = OrientationDescribe.Down;
|
||||
}
|
||||
else if (vector2.Y == 0)
|
||||
{
|
||||
orientationDescribes[0] = OrientationDescribe.ORIGIN;
|
||||
orientationDescribes[0] = OrientationDescribe.Origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
orientationDescribes[1] = OrientationDescribe.UP;
|
||||
orientationDescribes[1] = OrientationDescribe.Up;
|
||||
}
|
||||
|
||||
return orientationDescribes;
|
||||
|
|
|
@ -34,6 +34,6 @@ public class NodeUtils
|
|||
/// <returns></returns>
|
||||
public static async Task<int> DeleteAllChildAsync(Node parent)
|
||||
{
|
||||
return await Task.Run<int>(() => DeleteAllChild(parent));
|
||||
return await Task.Run(() => DeleteAllChild(parent));
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using ColdMint.scripts.debug;
|
||||
using Godot;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.utils;
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.map.dateBean;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace ColdMint.scripts.utils;
|
||||
|
||||
|
@ -47,7 +44,7 @@ public static class TileMapUtils
|
|||
///<para>此方法由Github Copilot编写。</para>
|
||||
/// </remarks>
|
||||
/// <returns></returns>
|
||||
public static RoomSlot[] GetRoomSlots(TileMap tileMap, string layerName)
|
||||
public static RoomSlot[]? GetRoomSlots(TileMap tileMap, string layerName)
|
||||
{
|
||||
var layer = GetTileMapLayer(tileMap, layerName);
|
||||
if (layer == -1)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.projectile;
|
||||
using Godot;
|
||||
|
@ -8,43 +7,52 @@ namespace ColdMint.scripts.weapon;
|
|||
|
||||
public partial class ProjectileWeapon : WeaponTemplate
|
||||
{
|
||||
private Marker2D _marker2D;
|
||||
private string[] _projectiles;
|
||||
private Dictionary<string, PackedScene> _projectileCache;
|
||||
private Node2D _projectileContainer;
|
||||
private Marker2D? _marker2D;
|
||||
private string[]? _projectiles;
|
||||
private Dictionary<string, PackedScene>? _projectileCache;
|
||||
private Node2D? _projectileContainer;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
_marker2D = GetNode<Marker2D>("Marker2D");
|
||||
_projectileCache = new Dictionary<string, PackedScene>();
|
||||
_projectiles = GetMeta("Projectiles", "").AsStringArray();
|
||||
foreach (var projectileItem in _projectiles)
|
||||
{
|
||||
var packedScene = GD.Load<PackedScene>(projectileItem);
|
||||
_projectileCache.Add(projectileItem, packedScene);
|
||||
}
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
_marker2D = GetNode<Marker2D>("Marker2D");
|
||||
_projectileCache = new Dictionary<string, PackedScene>();
|
||||
_projectiles = GetMeta("Projectiles", "").AsStringArray();
|
||||
foreach (var projectileItem in _projectiles)
|
||||
{
|
||||
var packedScene = GD.Load<PackedScene>(projectileItem);
|
||||
_projectileCache.Add(projectileItem, packedScene);
|
||||
}
|
||||
|
||||
_projectileContainer = GetNode("/root/Game/ProjectileContainer") as Node2D;
|
||||
}
|
||||
_projectileContainer = GetNode("/root/Game/ProjectileContainer") as Node2D;
|
||||
}
|
||||
|
||||
|
||||
protected override void DoFire(Node2D owner, Vector2 enemyGlobalPosition)
|
||||
{
|
||||
if (_projectiles.IsEmpty())
|
||||
{
|
||||
LogCat.LogError("projectiles_is_empty");
|
||||
return;
|
||||
}
|
||||
protected override void DoFire(Node2D? owner, Vector2 enemyGlobalPosition)
|
||||
{
|
||||
if (_projectiles.IsEmpty())
|
||||
{
|
||||
LogCat.LogError("projectiles_is_empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_projectileCache == null || _projectiles == null || owner == null || _projectileContainer == null ||
|
||||
_marker2D == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Get the first projectile
|
||||
var projectileScene = _projectileCache[_projectiles[0]];
|
||||
var projectile = projectileScene.Instantiate() as ProjectileTemplate;
|
||||
projectile.Owner = owner;
|
||||
var vector2 = (enemyGlobalPosition - _marker2D.GlobalPosition).Normalized() * projectile.Speed;
|
||||
projectile.Velocity = vector2;
|
||||
projectile.Position = _marker2D.GlobalPosition;
|
||||
_projectileContainer.AddChild(projectile);
|
||||
}
|
||||
}
|
||||
//Get the first projectile
|
||||
var projectileScene = _projectileCache[_projectiles[0]];
|
||||
var projectile = projectileScene.Instantiate() as ProjectileTemplate;
|
||||
if (projectile != null)
|
||||
{
|
||||
projectile.Owner = owner;
|
||||
var vector2 = (enemyGlobalPosition - _marker2D.GlobalPosition).Normalized() * projectile.Speed;
|
||||
projectile.Velocity = vector2;
|
||||
projectile.Position = _marker2D.GlobalPosition;
|
||||
}
|
||||
|
||||
_projectileContainer.AddChild(projectile);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.camp;
|
||||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.damage;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.inventory;
|
||||
using Godot;
|
||||
|
||||
|
@ -15,22 +13,22 @@ namespace ColdMint.scripts.weapon;
|
|||
/// </summary>
|
||||
public partial class WeaponTemplate : RigidBody2D, IItem
|
||||
{
|
||||
public float gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
|
||||
public float Gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
|
||||
|
||||
public string Id { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public int MaxStackQuantity { get; set; }
|
||||
public Texture2D Icon { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public Action<IItem> OnUse { get; set; }
|
||||
public Func<IItem, Node> OnInstantiation { get; set; }
|
||||
public Texture2D? Icon { get; set; }
|
||||
public new string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public Action<IItem>? OnUse { get; set; }
|
||||
public Func<IItem, Node>? OnInstantiation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Owner</para>
|
||||
/// <para>主人</para>
|
||||
/// </summary>
|
||||
public Node2D? Owner { get; set; }
|
||||
public new Node2D? Owner { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
@ -60,12 +58,12 @@ public partial class WeaponTemplate : RigidBody2D, IItem
|
|||
|
||||
private Area2D? _area2D;
|
||||
|
||||
protected RayCast2D _rayCast2D;
|
||||
protected RayCast2D? RayCast2D;
|
||||
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_rayCast2D = GetNode<RayCast2D>("RayCast2D");
|
||||
RayCast2D = GetNode<RayCast2D>("RayCast2D");
|
||||
_area2D = GetNode<Area2D>("Area2D");
|
||||
_area2D.BodyEntered += OnBodyEnter;
|
||||
Id = GetMeta("ID", "1").AsString();
|
||||
|
@ -73,7 +71,7 @@ public partial class WeaponTemplate : RigidBody2D, IItem
|
|||
MaxStackQuantity = GetMeta("MaxStackQuantity", Config.MaxStackQuantity).AsInt32();
|
||||
Icon = GetMeta("Icon", "").As<Texture2D>();
|
||||
Name = GetMeta("Name", "").AsString();
|
||||
Description =GetMeta("Description", "").AsString();
|
||||
Description = GetMeta("Description", "").AsString();
|
||||
_firingInterval = TimeSpan.FromMilliseconds(GetMeta("FiringInterval", "100").AsInt64());
|
||||
_minContactInjury = GetMeta("MinContactInjury", "1").AsInt32();
|
||||
_maxContactInjury = GetMeta("MaxContactInjury", "2").AsInt32();
|
||||
|
@ -143,18 +141,21 @@ public partial class WeaponTemplate : RigidBody2D, IItem
|
|||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
base._PhysicsProcess(delta);
|
||||
if (_rayCast2D.IsColliding())
|
||||
if (RayCast2D != null)
|
||||
{
|
||||
//If the weapon hits the ground, we disable physical damage.
|
||||
//如果武器落到地面了,我们禁用物理伤害。
|
||||
EnableContactInjury = false;
|
||||
//Items can be pushed by the player when they are on the ground
|
||||
//当物品在地面上时,可被玩家推动
|
||||
SetCollisionMaskValue(Config.LayerNumber.Player, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCollisionMaskValue(Config.LayerNumber.Player, false);
|
||||
if (RayCast2D.IsColliding())
|
||||
{
|
||||
//If the weapon hits the ground, we disable physical damage.
|
||||
//如果武器落到地面了,我们禁用物理伤害。
|
||||
EnableContactInjury = false;
|
||||
//Items can be pushed by the player when they are on the ground
|
||||
//当物品在地面上时,可被玩家推动
|
||||
SetCollisionMaskValue(Config.LayerNumber.Player, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCollisionMaskValue(Config.LayerNumber.Player, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,7 +174,7 @@ public partial class WeaponTemplate : RigidBody2D, IItem
|
|||
///<para>敌人所在位置</para>
|
||||
/// </param>
|
||||
/// </remarks>
|
||||
public void Fire(Node2D owner, Vector2 enemyGlobalPosition)
|
||||
public void Fire(Node2D? owner, Vector2 enemyGlobalPosition)
|
||||
{
|
||||
var nowTime = DateTime.Now;
|
||||
if (_lastFiringTime == null || nowTime - _lastFiringTime > _firingInterval)
|
||||
|
@ -210,7 +211,7 @@ public partial class WeaponTemplate : RigidBody2D, IItem
|
|||
/// <para>Execute fire</para>
|
||||
/// <para>执行开火</para>
|
||||
/// </summary>
|
||||
protected virtual void DoFire(Node2D owner, Vector2 enemyGlobalPosition)
|
||||
protected virtual void DoFire(Node2D? owner, Vector2 enemyGlobalPosition)
|
||||
{
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user