diff --git a/locals/Log.csv b/locals/Log.csv
index ec89ed7..d6d8e73 100644
--- a/locals/Log.csv
+++ b/locals/Log.csv
@@ -28,9 +28,7 @@ log_loot_list_has_no_entries,ID为{0}的战利品表,没有指定条目。,"Lo
log_not_within_the_loot_spawn_range,给定的数值{0}没有在战利品{1}的生成范围{2}内。,The given value {0} is not within the spawn range {2} of loot {1}.,与えられた数値{0}は戦利品{1}の生成範囲{2}内にありません。
log_loot_data_quantity,有{0}个战利品数据被返回。,{0} loot data was returned.,{0}個の戦利品データが返されます。
log_loot_data_add,生成战利品{0},Add loot {0},戦利品{0}を生成する
-
log_warning_node_cannot_cast_to,创建的物品{0}无法被转型为类型{1},Created items {0} cannot be cast into type {1},作成されたアイテム {0} をタイプ {1} にキャストすることはできません。
-
log_start_item_register_from_file,开始从文件注册物品信息,Start registering item information from files,アイテム情報をファイルから登録開始
log_found_files,找到{0}个文件,Found {0} files,{0}ファイルが見つかりました
log_found_item_types,从文件中找到{0}个物品类型,Found {0} item types in files,ファイルから{0}個のアイテム・タイプが見つかった
@@ -83,4 +81,9 @@ log_weapon_detected,检测到武器。,Weapon detected.,武器が検出されま
log_no_weapon_detected,没有检测到武器。,No weapon detected.,武器が検出されません。
log_weapon_lost,武器丢失。,Weapon lost.,武器が失われました。
log_nearest_node_is_null,最近的节点为空。,The nearest node is null.,最も近いノードが空です。
-log_node_is_not_WeaponTemplate,节点不是WeaponTemplate。,The node is not a WeaponTemplate.,ノードはWeaponTemplateではありません。
\ No newline at end of file
+log_node_is_not_WeaponTemplate,节点不是WeaponTemplate。,The node is not a WeaponTemplate.,ノードはWeaponTemplateではありません。
+log_weapon_not_in_pickup_range,武器不在拾取范围内。,The weapon is not within the pickup range.,武器は拾い取り範囲内にありません。
+log_weapon_picked_up,武器被拾取。,Weapon picked up.,武器が拾い取られました。
+log_weapon_pickup_failed,武器拾取失败。,Weapon pickup failed.,武器の拾い取りに失敗しました。
+log_enter_the_picking_range_body,进入拾取范围。,Enter the picking range.,拾い取り範囲に入ります。
+log_search_for_weapon_timeout,搜索武器超时。,Search for weapon timeout.,武器の検索がタイムアウトしました。
\ No newline at end of file
diff --git a/prefab/entitys/Character.tscn b/prefab/entitys/Character.tscn
index 463593b..0837ada 100644
--- a/prefab/entitys/Character.tscn
+++ b/prefab/entitys/Character.tscn
@@ -26,7 +26,6 @@ animations = [{
collision_layer = 4
collision_mask = 34
script = ExtResource("1_1dlls")
-LootListId = null
metadata/CampId = "Default"
metadata/MaxHp = 32
diff --git a/prefab/entitys/DelivererOfDarkMagic.tscn b/prefab/entitys/DelivererOfDarkMagic.tscn
index feedaf1..2df9169 100644
--- a/prefab/entitys/DelivererOfDarkMagic.tscn
+++ b/prefab/entitys/DelivererOfDarkMagic.tscn
@@ -10,8 +10,8 @@
radius = 20.0
height = 52.0
-[sub_resource type="CircleShape2D" id="CircleShape2D_vmqbt"]
-radius = 34.5398
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_a3myh"]
+size = Vector2(46, 65)
[sub_resource type="SpriteFrames" id="SpriteFrames_qumby"]
animations = [{
@@ -25,10 +25,10 @@ animations = [{
}]
[sub_resource type="CircleShape2D" id="CircleShape2D_c61vr"]
-radius = 185.132
+radius = 153.0
[sub_resource type="CircleShape2D" id="CircleShape2D_fowd5"]
-radius = 233.808
+radius = 172.29
[node name="DelivererOfDarkMagic" type="CharacterBody2D"]
collision_layer = 64
@@ -48,7 +48,8 @@ collision_layer = 0
collision_mask = 8
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2DPickingArea"]
-shape = SubResource("CircleShape2D_vmqbt")
+position = Vector2(0, 5.5)
+shape = SubResource("RectangleShape2D_a3myh")
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
sprite_frames = SubResource("SpriteFrames_qumby")
diff --git a/scripts/Config.cs b/scripts/Config.cs
index d626cbb..1278cff 100644
--- a/scripts/Config.cs
+++ b/scripts/Config.cs
@@ -8,19 +8,6 @@ namespace ColdMint.scripts;
public static class Config
{
- ///
- /// ID of the behavior tree
- /// 行为树的ID
- ///
- public static class BehaviorTreeId
- {
- ///
- /// 巡逻
- /// Patrol
- ///
- public const string Patrol = "Patrol";
- }
-
///
/// Loot table ID
/// 战利品表ID
@@ -34,31 +21,6 @@ public static class Config
public const string Test = "test";
}
- ///
- /// BehaviorTreeResult
- /// 行为树的结果
- ///
- public static class BehaviorTreeResult
- {
- ///
- /// Running
- /// 运行中
- ///
- public const int Running = 0;
-
- ///
- /// Success
- /// 成功
- ///
- public const int Success = 1;
-
- ///
- /// Failure
- /// 失败
- ///
- public const int Failure = 2;
- }
-
///
/// Camp ID
diff --git a/scripts/character/AiCharacter.cs b/scripts/character/AiCharacter.cs
index c63eab4..505e06b 100644
--- a/scripts/character/AiCharacter.cs
+++ b/scripts/character/AiCharacter.cs
@@ -348,7 +348,10 @@ public sealed partial class AiCharacter : CharacterTemplate
{
if (node is WeaponTemplate weaponTemplate)
{
- _weaponInTheScoutRange?.Add(weaponTemplate);
+ if (CanPickItem(weaponTemplate))
+ {
+ _weaponInTheScoutRange?.Add(weaponTemplate);
+ }
}
CanCauseHarmNode(node, (canCause, characterTemplate) =>
diff --git a/scripts/character/CharacterTemplate.cs b/scripts/character/CharacterTemplate.cs
index baf6e6f..b0f7f89 100644
--- a/scripts/character/CharacterTemplate.cs
+++ b/scripts/character/CharacterTemplate.cs
@@ -240,14 +240,12 @@ public partial class CharacterTemplate : CharacterBody2D
foreach (var pickingRangeBody in PickingRangeBodies)
{
if (pickingRangeBody is not WeaponTemplate weaponTemplate) continue;
- if (weaponTemplate.Owner != null)
+ if (weaponTemplate.Picked)
{
continue;
}
-
weaponTemplates.Add(weaponTemplate);
}
-
return weaponTemplates.ToArray();
}
@@ -308,7 +306,7 @@ public partial class CharacterTemplate : CharacterBody2D
///
///
///
- private bool CanPickItem(Node node)
+ protected bool CanPickItem(Node node)
{
if (_currentItem != null && node == _currentItem)
{
@@ -336,7 +334,7 @@ public partial class CharacterTemplate : CharacterBody2D
///Whether successfully picked up
///是否成功拾起
///
- protected bool PickItem(Node2D? pickAbleItemNode2D)
+ public bool PickItem(Node2D? pickAbleItemNode2D)
{
//Empty reference checking is implicitly performed here.
//此处隐式的执行了空引用检查。
@@ -585,12 +583,12 @@ public partial class CharacterTemplate : CharacterBody2D
if (damageTemplate.Attacker is CharacterTemplate characterTemplate &&
!string.IsNullOrEmpty(characterTemplate.CharacterName))
{
- LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat,CharacterName,
+ LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat, CharacterName,
characterTemplate.CharacterName);
}
else
{
- LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat,CharacterName,
+ LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, LogCat.UploadFormat, CharacterName,
damageTemplate.Attacker.Name);
}
}
@@ -612,6 +610,7 @@ public partial class CharacterTemplate : CharacterBody2D
return;
}
+ LogCat.Log("enter_the_picking_range_body");
PickingRangeBodiesList?.Add(node);
}
diff --git a/scripts/character/Player.cs b/scripts/character/Player.cs
index 384161b..3977a58 100644
--- a/scripts/character/Player.cs
+++ b/scripts/character/Player.cs
@@ -48,7 +48,7 @@ public partial class Player : CharacterTemplate
{
base._Ready();
CharacterName = TranslationServerUtils.Translate("default_player_name");
- LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, LogCat.UploadFormat,ReadOnlyCharacterName,
+ LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, LogCat.UploadFormat, ReadOnlyCharacterName,
GlobalPosition);
var floatLabelPackedScene = GD.Load("res://prefab/ui/FloatLabel.tscn");
//Initializes the float label.
diff --git a/scripts/projectile/ProjectileTemplate.cs b/scripts/projectile/ProjectileTemplate.cs
index 80a3ad1..92b444e 100644
--- a/scripts/projectile/ProjectileTemplate.cs
+++ b/scripts/projectile/ProjectileTemplate.cs
@@ -14,17 +14,17 @@ namespace ColdMint.scripts.projectile;
///
public partial class ProjectileTemplate : CharacterBody2D
{
- protected long Life;
+ private long _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;
+ private double _durability;
- protected int MaxDamage;
- protected int MinDamage;
- protected int DamageType;
+ private int _maxDamage;
+ private int _minDamage;
+ private int _damageType;
///
/// After this time destroy the projectile
@@ -37,7 +37,7 @@ public partial class ProjectileTemplate : CharacterBody2D
/// The impact area of the bullet
/// 子弹的碰撞区域
///
- protected Area2D? Area2D;
+ private Area2D? _area2D;
///
/// knockback
@@ -47,7 +47,7 @@ public partial class ProjectileTemplate : CharacterBody2D
///How much force does it have when hitting the character? Unit: Number of cells,The X direction of the force is inferred automatically.
///当击中角色时带有多大的力?单位:格数,力的X方向是自动推断的。
///
- protected Vector2 KnockbackForce;
+ private Vector2 _knockbackForce;
public float Speed => GetMeta("Speed", "15").AsSingle();
@@ -61,26 +61,26 @@ public partial class ProjectileTemplate : CharacterBody2D
{
//The bullet's impact detection area
//子弹的碰撞检测区域
- Area2D = GetNode("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("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(ms)
//子弹的存在时间(毫秒)
- Life = GetMeta("Life", "10000").AsInt64();
+ _life = GetMeta("Life", "10000").AsInt64();
//If the existence time is less than or equal to 0, then it is set to exist for 10 seconds, and projectiles that exist indefinitely are prohibited
//如果存在时间小于等于0,那么设置为存在10秒,禁止无限期存在的抛射体
- if (Life <= 0)
+ if (_life <= 0)
{
- Life = 10000;
+ _life = 10000;
}
- _destructionTime = DateTime.Now.AddMilliseconds(Life);
+ _destructionTime = DateTime.Now.AddMilliseconds(_life);
}
@@ -148,19 +148,19 @@ public partial class ProjectileTemplate : CharacterBody2D
var damage = new Damage
{
Attacker = owner,
- MaxDamage = MaxDamage,
- MinDamage = MinDamage
+ MaxDamage = _maxDamage,
+ 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
@@ -169,18 +169,18 @@ 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);
}
}
else if (target is PickAbleTemplate pickAbleTemplate)
{
- 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
@@ -189,7 +189,7 @@ public partial class ProjectileTemplate : CharacterBody2D
}
force.X = forceX * Config.CellSize;
- force.Y = KnockbackForce.Y * Config.CellSize;
+ force.Y = _knockbackForce.Y * Config.CellSize;
pickAbleTemplate.ApplyImpulse(force);
}
}
@@ -215,8 +215,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时,销毁子弹
diff --git a/scripts/stateMachine/PatrolStateMachine.cs b/scripts/stateMachine/PatrolStateMachine.cs
index 6286b1f..7929438 100644
--- a/scripts/stateMachine/PatrolStateMachine.cs
+++ b/scripts/stateMachine/PatrolStateMachine.cs
@@ -26,5 +26,7 @@ public class PatrolStateMachine : StateMachineTemplate
RegisterProcessor(chaseStateProcessor);
var lookForWeaponProcessor = new LookForWeaponProcessor();
RegisterProcessor(lookForWeaponProcessor);
+ var fleeProcessor = new FleeProcessor();
+ RegisterProcessor(fleeProcessor);
}
}
\ No newline at end of file
diff --git a/scripts/stateMachine/StateProcessor/FleeProcessor.cs b/scripts/stateMachine/StateProcessor/FleeProcessor.cs
new file mode 100644
index 0000000..f3f0b73
--- /dev/null
+++ b/scripts/stateMachine/StateProcessor/FleeProcessor.cs
@@ -0,0 +1,63 @@
+using System;
+using ColdMint.scripts.character;
+using Godot;
+
+namespace ColdMint.scripts.stateMachine.StateProcessor;
+
+///
+/// Escape state processor
+/// 逃跑状态处理器
+///
+public class FleeProcessor : StateProcessorTemplate
+{
+ ///
+ /// When to return to enemy free status
+ /// 何时恢复到没有敌人的状态
+ ///
+ private DateTime? _endTime;
+
+ ///
+ /// When away from the enemy, how long to return to normal state
+ /// 当远离敌人后,多长时间恢复到正常状态
+ ///
+ public TimeSpan RecoveryTimeSpan { get; set; } = TimeSpan.FromMilliseconds(300);
+
+ protected override void OnExecute(StateContext context, Node owner)
+ {
+ if (owner is not AiCharacter aiCharacter)
+ {
+ return;
+ }
+
+ var enemy = aiCharacter.GetFirstEnemyInScoutArea();
+ if (enemy == null)
+ {
+ //There are no enemies left.
+ //没有敌人了
+ if (_endTime == null)
+ {
+ _endTime = DateTime.Now + RecoveryTimeSpan;
+ return;
+ }
+
+ if (DateTime.Now > _endTime)
+ {
+ //Recovery time, end status.
+ //恢复时间,结束状态。
+ context.CurrentState = State.Patrol;
+ }
+ }
+ else
+ {
+ //Enemies
+ //有敌人
+ //To calculate the escape direction, the vector of the enemy pointing to the character is the escape direction.
+ //计算逃跑方向,敌人指向角色的向量为逃跑方向。
+ _endTime = null;
+ var direction = aiCharacter.GlobalPosition - enemy.GlobalPosition;
+ aiCharacter.SetTargetPosition(aiCharacter.GlobalPosition + direction);
+ }
+ }
+
+ public override State State => State.Flee;
+}
\ No newline at end of file
diff --git a/scripts/stateMachine/StateProcessor/LookForWeaponProcessor.cs b/scripts/stateMachine/StateProcessor/LookForWeaponProcessor.cs
index 1eee7b3..da7acb0 100644
--- a/scripts/stateMachine/StateProcessor/LookForWeaponProcessor.cs
+++ b/scripts/stateMachine/StateProcessor/LookForWeaponProcessor.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using ColdMint.scripts.character;
using ColdMint.scripts.debug;
using ColdMint.scripts.utils;
@@ -13,7 +14,37 @@ namespace ColdMint.scripts.stateMachine.StateProcessor;
///
public class LookForWeaponProcessor : StateProcessorTemplate
{
- protected WeaponTemplate? TargetWeapon;
+ private WeaponTemplate? _targetWeapon;
+
+ ///
+ ///
+ ///停止寻找武器的时间
+ ///
+ ///
+ /// If you don't find a weapon in a while, give up looking
+ /// 如果在一段时间内没找到武器,那么放弃寻找
+ ///
+ private DateTime _endTime;
+
+ ///
+ /// When no weapon was found, how long did they give up looking
+ /// 当没有找到武器,多长时间放弃寻找
+ ///
+ public TimeSpan RecoveryTimeSpan { get; set; } = TimeSpan.FromMilliseconds(150);
+
+ public override void Enter(StateContext context)
+ {
+ UpdateEndTime();
+ }
+
+ ///
+ /// Update end time
+ /// 更新结束时间
+ ///
+ private void UpdateEndTime()
+ {
+ _endTime = DateTime.Now + RecoveryTimeSpan;
+ }
protected override void OnExecute(StateContext context, Node owner)
{
@@ -23,11 +54,77 @@ public class LookForWeaponProcessor : StateProcessorTemplate
return;
}
- if (TargetWeapon != null)
+ if (_targetWeapon == null)
{
+ if (DateTime.Now > _endTime)
+ {
+ //The search for the weapon ran out of time
+ //寻找武器时超时
+ LogCat.Log("search_for_weapon_timeout", LogCat.LogLabel.LookForWeaponProcessor);
+ context.CurrentState = State.Flee;
+ return;
+ }
+ }
+ else
+ {
+ if (_targetWeapon.Picked)
+ {
+ //If the weapon we're looking for gets picked up, we find a new one.
+ //如果我们要拿的武器被别人捡了,那么重新找新的武器。
+ _targetWeapon = null;
+ UpdateEndTime();
+ return;
+ }
+
//If the nearest weapon is found, move the character to the weapon.
//如果有最近的武器被找到了,那么将角色移动到武器旁边。
- aiCharacter.SetTargetPosition(TargetWeapon.GlobalPosition);
+ var weaponTemplates = aiCharacter.GetCanPickedWeapon();
+ //Weapons are not in the range of the pickup.
+ //武器没在拾捡范围内。
+ if (weaponTemplates.Length == 0)
+ {
+ LogCat.Log("weapon_not_in_pickup_range", LogCat.LogLabel.LookForWeaponProcessor);
+ aiCharacter.SetTargetPosition(_targetWeapon.GlobalPosition);
+ }
+ else
+ {
+ var haveWeapon = false;
+ foreach (var weaponTemplate in weaponTemplates)
+ {
+ if (weaponTemplate == _targetWeapon)
+ {
+ haveWeapon = true;
+ }
+ }
+
+ if (haveWeapon)
+ {
+ var pickResult = aiCharacter.PickItem(_targetWeapon);
+ if (pickResult)
+ {
+ context.CurrentState = context.PreviousState;
+ //Successfully picked up the weapon.
+ //成功拾起武器。
+ LogCat.Log("weapon_picked_up", LogCat.LogLabel.LookForWeaponProcessor);
+ }
+ else
+ {
+ _targetWeapon = null;
+ UpdateEndTime();
+ //Weapon failed to pick up.
+ //武器捡起时失败。
+ LogCat.Log("weapon_pickup_failed", LogCat.LogLabel.LookForWeaponProcessor);
+ }
+ }
+ else
+ {
+ //No weapons are included in the pickup area.
+ //拾捡范围内不包含武器。
+ LogCat.Log("weapon_not_in_pickup_range", LogCat.LogLabel.LookForWeaponProcessor);
+ aiCharacter.SetTargetPosition(_targetWeapon.GlobalPosition);
+ }
+ }
+
return;
}
@@ -67,7 +164,7 @@ public class LookForWeaponProcessor : StateProcessorTemplate
if (node is WeaponTemplate weaponTemplate)
{
- TargetWeapon = weaponTemplate;
+ _targetWeapon = weaponTemplate;
}
else
{