Improve empty references and improve code robustness.

改善空引用,提升代码健壮性。
This commit is contained in:
Cold-Mint 2024-05-08 18:22:04 +08:00
parent 1c116eb820
commit 222d26441e
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
59 changed files with 1050 additions and 949 deletions

View File

@ -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 = "死灵法师"

View File

@ -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>

View File

@ -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; }
}

View File

@ -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()
{

View File

@ -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));
}
}

View File

@ -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)
{

View File

@ -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();
}

View File

@ -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();
}

View File

@ -2,7 +2,7 @@
public interface IBehaviorTree
{
string ID { get; }
string? Id { get; }
IBehaviorTreeNode Root { get; }
IBehaviorTreeNode? Root { get; }
}

View File

@ -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>

View File

@ -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
//有距离最近的敌人

View File

@ -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;

View File

@ -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())
{

View File

@ -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;

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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;
}
}

View 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;
}
}

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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
};
}
}

View File

@ -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;
}

View File

@ -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>

View File

@ -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; }
}

View File

@ -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);
}
}

View File

@ -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; }
}

View File

@ -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);
}
}

View File

@ -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:

View File

@ -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);
}
}
}

View File

@ -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();
}

View File

@ -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);
};
}
}
}

View File

@ -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()
{
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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 =>
{
});
}
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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; }
}

View File

@ -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>

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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; }
}

View File

@ -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)
{

View File

@ -13,7 +13,7 @@ public class RoomHolder : IRoomHolder
return true;
}
public IRoom LastRoom
public IRoom? LastRoom
{
get
{

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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 cellsThe 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时销毁子弹

View File

@ -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;

View File

@ -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));
}
}

View File

@ -1,5 +1,4 @@
using ColdMint.scripts.debug;
using Godot;
using Godot;
namespace ColdMint.scripts.utils;

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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)
{
}
}