From 61618c13a9cbc2e15b5141dcc2c76f3d0847058d Mon Sep 17 00:00:00 2001 From: Cold-Mint Date: Wed, 10 Jul 2024 23:23:04 +0800 Subject: [PATCH] =?UTF-8?q?AI=20characters=20can=20set=20default=20weapons?= =?UTF-8?q?.=20The=20AI=20will=20try=20to=20attack=20and=20kill=20the=20en?= =?UTF-8?q?emy=20now.=20Fixed=20an=20issue=20where=20bubbles=20would=20not?= =?UTF-8?q?=20display=20properly.=20AI=E8=A7=92=E8=89=B2=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=BB=98=E8=AE=A4=E6=AD=A6=E5=99=A8=E3=80=82?= =?UTF-8?q?AI=E4=BC=9A=E5=B0=9D=E8=AF=95=E6=94=BB=E5=87=BB=E5=B9=B6?= =?UTF-8?q?=E6=9D=80=E6=AD=BB=E6=95=8C=E4=BA=BA=E4=BA=86=E3=80=82=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=B0=94=E6=B3=A1=E4=B8=8D=E8=83=BD=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locals/DeathInfo.csv | 5 +- locals/Log.csv | 5 +- prefab/entitys/Character.tscn | 2 +- prefab/entitys/DelivererOfDarkMagic.tscn | 4 +- prefab/weapons/staffOfTheUndead.tscn | 5 ++ scripts/bubble/BubbleMarker.cs | 18 +++--- scripts/character/AiCharacter.cs | 50 +++++++++++++-- scripts/character/CharacterTemplate.cs | 1 - scripts/deathInfo/DeathInfoGenerator.cs | 2 +- scripts/debug/LogCat.cs | 6 ++ .../StateProcessor/ChaseStateProcessor.cs | 19 +++++- .../StateProcessor/LookForWeaponProcessor.cs | 44 +++++++++++++ .../StateProcessor/PatrolStateProcessor.cs | 12 +++- scripts/utils/NodeUtils.cs | 61 ++++++++++++++++++- 14 files changed, 211 insertions(+), 23 deletions(-) create mode 100644 scripts/stateMachine/StateProcessor/LookForWeaponProcessor.cs diff --git a/locals/DeathInfo.csv b/locals/DeathInfo.csv index b9e1a1d..f54fa09 100644 --- a/locals/DeathInfo.csv +++ b/locals/DeathInfo.csv @@ -2,4 +2,7 @@ id,zh,en,ja #suicide #自杀 death_info_self_1,{0}误伤了自己。,{0} accidentally shot themself.,{0}誤って自分を傷つけました。 -death_info_self_2,{0}忘记了瞄准。,{0} forgot to aim.,{0}照準を忘れました。 \ No newline at end of file +death_info_self_2,{0}忘记了瞄准。,{0} forgot to aim.,{0}照準を忘れました。 +#Default death message +#默认死亡信息 +death_info_default,{0}被{1}杀死了。,{0} is killed by {1}.,{0}は{1}に殺されました。 \ No newline at end of file diff --git a/locals/Log.csv b/locals/Log.csv index 606a13a..a23ea4d 100644 --- a/locals/Log.csv +++ b/locals/Log.csv @@ -69,4 +69,7 @@ log_camp_is_null,阵营为空。,Camp is null.,陣営が空です。 log_set_default_camp,设置默认阵营{0}。,Set default camp {0}.,デフォルトの陣営{0}を設定します。 log_state_change,状态变更从{0}到{1}。,State change from {0} to {1}.,状態が{0}から{1}に変更されます。 log_state_processor_not_found,找不到状态处理器{0}。,State processor {0} not found.,ステートプロセッサ{0}が見つかりません。 -log_chase_no_enemy,追逐没有敌人。,Chase no enemy.,敵がいない追跡です。 \ No newline at end of file +log_chase_no_enemy,追逐没有敌人。,Chase no enemy.,敵がいない追跡です。 +log_bubble_not_found,找不到气泡{0}。,Bubble {0} not found.,バブル{0}が見つかりません。 +log_owner_is_not_AiCharacter,所有者不是AiCharacter。,Owner is not AiCharacter.,所有者はAiCharacterではありません。 +log_weaponContainer_is_null,武器容器为空。,Weapon container is null.,武器コンテナが空です。 \ No newline at end of file diff --git a/prefab/entitys/Character.tscn b/prefab/entitys/Character.tscn index 7053706..463593b 100644 --- a/prefab/entitys/Character.tscn +++ b/prefab/entitys/Character.tscn @@ -28,7 +28,7 @@ collision_mask = 34 script = ExtResource("1_1dlls") LootListId = null metadata/CampId = "Default" -metadata/MaxHp = 12 +metadata/MaxHp = 32 [node name="CollisionShape2D" type="CollisionShape2D" parent="."] shape = SubResource("CapsuleShape2D_bb8wt") diff --git a/prefab/entitys/DelivererOfDarkMagic.tscn b/prefab/entitys/DelivererOfDarkMagic.tscn index 210854d..1b6b35d 100644 --- a/prefab/entitys/DelivererOfDarkMagic.tscn +++ b/prefab/entitys/DelivererOfDarkMagic.tscn @@ -25,7 +25,7 @@ animations = [{ }] [sub_resource type="CircleShape2D" id="CircleShape2D_c61vr"] -radius = 82.2192 +radius = 185.132 [sub_resource type="CircleShape2D" id="CircleShape2D_fowd5"] radius = 233.808 @@ -34,6 +34,7 @@ radius = 233.808 collision_layer = 64 collision_mask = 38 script = ExtResource("1_ubaid") +InitWeaponRes = "res://prefab/weapons/staffOfTheUndead.tscn" LootListId = "test" metadata/CampId = "Mazoku" metadata/MaxHp = 50 @@ -83,7 +84,6 @@ collision_mask = 68 shape = SubResource("CircleShape2D_c61vr") [node name="NavigationAgent2D" type="NavigationAgent2D" parent="."] -debug_enabled = true [node name="BubbleMarker" type="Marker2D" parent="."] position = Vector2(0, -79) diff --git a/prefab/weapons/staffOfTheUndead.tscn b/prefab/weapons/staffOfTheUndead.tscn index c666b57..dc701d2 100644 --- a/prefab/weapons/staffOfTheUndead.tscn +++ b/prefab/weapons/staffOfTheUndead.tscn @@ -16,7 +16,12 @@ collision_mask = 34 script = ExtResource("1_w8hhv") ProjectileScenes = [ExtResource("2_34250")] FiringIntervalAsMillisecond = 300 +_recoil = null Id = "staff_of_the_undead" +UniqueName = null +UniqueDescription = null +_minContactInjury = null +_maxContactInjury = null [node name="DamageArea2D" type="Area2D" parent="."] collision_layer = 8 diff --git a/scripts/bubble/BubbleMarker.cs b/scripts/bubble/BubbleMarker.cs index 65eee77..263f72b 100644 --- a/scripts/bubble/BubbleMarker.cs +++ b/scripts/bubble/BubbleMarker.cs @@ -1,5 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using ColdMint.scripts.debug; using ColdMint.scripts.utils; using Godot; @@ -11,7 +11,7 @@ namespace ColdMint.scripts.bubble; /// public partial class BubbleMarker : Marker2D { - private readonly Dictionary _bubbleDictionary = []; + private readonly Dictionary _bubbleDictionary = []; /// /// Add bubbles @@ -20,14 +20,14 @@ public partial class BubbleMarker : Marker2D /// /// /// - public bool AddBubble(int id, Node2D node) + public bool AddBubble(int id, Node node) { if (!_bubbleDictionary.TryAdd(id, node)) { return false; } - node.Hide(); + NodeUtils.HideNode(node); NodeUtils.CallDeferredAddChild(this, node); return true; } @@ -45,9 +45,11 @@ public partial class BubbleMarker : Marker2D { if (!_bubbleDictionary.TryGetValue(id, out var value)) { + LogCat.LogErrorWithFormat("bubble_not_found", LogCat.LogLabel.BubbleMarker, id); return; } - value.Show(); + + NodeUtils.ShowNode(value); } /// @@ -58,8 +60,10 @@ public partial class BubbleMarker : Marker2D { if (!_bubbleDictionary.TryGetValue(id, out var value)) { + LogCat.LogErrorWithFormat("bubble_not_found", LogCat.LogLabel.BubbleMarker, id); return; } - value.Hide(); + + NodeUtils.HideNode(value); } } \ No newline at end of file diff --git a/scripts/character/AiCharacter.cs b/scripts/character/AiCharacter.cs index e199b61..ca16c6e 100644 --- a/scripts/character/AiCharacter.cs +++ b/scripts/character/AiCharacter.cs @@ -2,8 +2,10 @@ using System; using System.Collections.Generic; using ColdMint.scripts.bubble; using ColdMint.scripts.camp; +using ColdMint.scripts.inventory; using ColdMint.scripts.stateMachine; using ColdMint.scripts.utils; +using ColdMint.scripts.weapon; using Godot; namespace ColdMint.scripts.character; @@ -93,6 +95,12 @@ public sealed partial class AiCharacter : CharacterTemplate /// private BubbleMarker? _bubbleMarker; + /// + /// The initial weapons scene + /// 初始的武器场景 + /// + [Export] public string? InitWeaponRes; + public override void _Ready() { base._Ready(); @@ -116,14 +124,14 @@ public sealed partial class AiCharacter : CharacterTemplate if (_bubbleMarker != null) { using var plaintScene = GD.Load("res://prefab/ui/plaint.tscn"); - var plaint = NodeUtils.InstantiatePackedScene(plaintScene); + var plaint = NodeUtils.InstantiatePackedScene(plaintScene); if (plaint != null) { _bubbleMarker.AddBubble(plaintBubbleId, plaint); } using var queryScene = GD.Load("res://prefab/ui/query.tscn"); - var query = NodeUtils.InstantiatePackedScene(queryScene); + var query = NodeUtils.InstantiatePackedScene(queryScene); if (query != null) { _bubbleMarker.AddBubble(queryBubbleId, query); @@ -170,8 +178,42 @@ public sealed partial class AiCharacter : CharacterTemplate { StateMachine.Start(); } + + //You must create an item container for the character before you can pick up the item. + //必须为角色创建物品容器后才能拾起物品。 + var universalItemContainer = new UniversalItemContainer(); + var itemSlotNode = universalItemContainer.AddItemSlot(this); + itemSlotNode?.Hide(); + ProtectedItemContainer = universalItemContainer; + //Add initial weapon + //添加初始武器 + AddInitialWeapon(InitWeaponRes); } + /// + /// Adds an initial weapon to the character + /// 为角色添加初始的武器 + /// + private void AddInitialWeapon(string? initWeaponRes) + { + if (initWeaponRes == null) + { + return; + } + + //Set the resource path of the initial weapon and try to create the object of the initial weapon. + //设置了初始武器的资源路径,尝试创建初始武器的对象。 + var packedScene = GD.Load(initWeaponRes); + var weaponTemplate = NodeUtils.InstantiatePackedScene(packedScene); + if (weaponTemplate == null) + { + return; + } + NodeUtils.CallDeferredAddChild(this, weaponTemplate); + PickItem(weaponTemplate); + } + + /// /// Display exclamation marks /// 显示感叹号 @@ -194,8 +236,8 @@ public sealed partial class AiCharacter : CharacterTemplate { _bubbleMarker?.ShowBubble(queryBubbleId); } - - public void HiddenQuery() + + public void HideQuery() { _bubbleMarker?.HideBubble(queryBubbleId); } diff --git a/scripts/character/CharacterTemplate.cs b/scripts/character/CharacterTemplate.cs index 92e5df1..cb7417f 100644 --- a/scripts/character/CharacterTemplate.cs +++ b/scripts/character/CharacterTemplate.cs @@ -264,7 +264,6 @@ public partial class CharacterTemplate : CharacterBody2D CharacterName = GetMeta("Name", Name).AsString(); CampId = GetMeta("CampId", Config.CampId.Default).AsString(); MaxHp = GetMeta("MaxHp", Config.DefaultMaxHp).AsInt32(); - // var lootListId = GetMeta("LootListId", string.Empty).AsString(); if (MaxHp <= 0) { diff --git a/scripts/deathInfo/DeathInfoGenerator.cs b/scripts/deathInfo/DeathInfoGenerator.cs index b829ec4..6457950 100644 --- a/scripts/deathInfo/DeathInfoGenerator.cs +++ b/scripts/deathInfo/DeathInfoGenerator.cs @@ -76,6 +76,6 @@ public static class DeathInfoGenerator /// private static string? GenerateDefaultDeathInfo(string victimName, string killerName) { - return TranslationServerUtils.TranslateWithFormat("death_info", victimName, killerName); + return TranslationServerUtils.TranslateWithFormat("death_info_default", victimName, killerName); } } \ No newline at end of file diff --git a/scripts/debug/LogCat.cs b/scripts/debug/LogCat.cs index e3829d5..b59e859 100644 --- a/scripts/debug/LogCat.cs +++ b/scripts/debug/LogCat.cs @@ -45,6 +45,12 @@ public static class LogCat /// 追击敌人处理器 /// public const string ChaseStateProcessor = "ChaseStateProcessor"; + + /// + /// BubbleMarker + /// 气泡标记 + /// + public static string BubbleMarker = "BubbleMarker"; } diff --git a/scripts/stateMachine/StateProcessor/ChaseStateProcessor.cs b/scripts/stateMachine/StateProcessor/ChaseStateProcessor.cs index 96f68dc..7419f69 100644 --- a/scripts/stateMachine/StateProcessor/ChaseStateProcessor.cs +++ b/scripts/stateMachine/StateProcessor/ChaseStateProcessor.cs @@ -24,17 +24,32 @@ public class ChaseStateProcessor : StateProcessorTemplate { //No more enemies. Return to previous status. //没有敌人了,返回上一个状态。 - aiCharacter.HiddenQuery(); + aiCharacter.HidePlaint(); + aiCharacter.HideQuery(); aiCharacter.SetTargetPosition(aiCharacter.GlobalPosition); LogCat.Log("chase_no_enemy", label: LogCat.LogLabel.ChaseStateProcessor); context.CurrentState = context.PreviousState; } else { + var canAttackEnemy = aiCharacter.GetFirstEnemyInAttackArea(); + if (canAttackEnemy == null) + { + aiCharacter.HidePlaint(); + aiCharacter.DispladyQuery(); + } + else + { + //TODO:转到攻击状态。 + aiCharacter.HideQuery(); + aiCharacter.DispladyPlaint(); + aiCharacter.UseItem(enemy.GlobalPosition); + } + //Set the position of the enemy entering the range to the position we are going to. //将进入范围的敌人位置设置为我们要前往的位置。 aiCharacter.SetTargetPosition(enemy.GlobalPosition); - aiCharacter.DispladyQuery(); + aiCharacter.AimTheCurrentItemAtAPoint(enemy.GlobalPosition); } } diff --git a/scripts/stateMachine/StateProcessor/LookForWeaponProcessor.cs b/scripts/stateMachine/StateProcessor/LookForWeaponProcessor.cs new file mode 100644 index 0000000..de24a74 --- /dev/null +++ b/scripts/stateMachine/StateProcessor/LookForWeaponProcessor.cs @@ -0,0 +1,44 @@ +using ColdMint.scripts.character; +using ColdMint.scripts.debug; +using ColdMint.scripts.utils; +using ColdMint.scripts.weapon; +using Godot; + +namespace ColdMint.scripts.stateMachine.StateProcessor; + +/// +/// Weapon seeking condition +/// 寻找武器状态 +/// +public class LookForWeaponProcessor : StateProcessorTemplate +{ + protected WeaponTemplate weaponTemplate; + protected override void OnExecute(StateContext context, Node owner) + { + //Find weapons around your character. + //查找角色周围的武器。 + if (owner is not AiCharacter aiCharacter) + { + LogCat.LogError("owner_is_not_AiCharacter"); + return; + } + + if (GameSceneNodeHolder.WeaponContainer == null) + { + LogCat.LogError("weaponContainer_is_null"); + return; + } + + NodeUtils.ForEachNode(GameSceneNodeHolder.WeaponContainer, template => + { + if (template.GlobalPosition.DistanceTo(aiCharacter.GlobalPosition) > 100) + { + weaponTemplate = template; + return true; + } + return false; + }); + } + + public override State State => State.LookForWeapon; +} \ No newline at end of file diff --git a/scripts/stateMachine/StateProcessor/PatrolStateProcessor.cs b/scripts/stateMachine/StateProcessor/PatrolStateProcessor.cs index 3d7c9cd..0068e17 100644 --- a/scripts/stateMachine/StateProcessor/PatrolStateProcessor.cs +++ b/scripts/stateMachine/StateProcessor/PatrolStateProcessor.cs @@ -43,9 +43,17 @@ public class PatrolStateProcessor : StateProcessorTemplate if (aiCharacter.ScoutEnemyDetected()) { - //Seeing that the enemy had entered the reconnaissance area, we gave chase immediately. - //发现敌人进入侦察范围,我们立即追击。 + //Once the enemy enters the reconnaissance range, we first see if the character has a weapon, if so, then pursue the enemy, otherwise, the patrol state changes to looking for weapons. + //发现敌人进入侦察范围后,我们先看角色是否持有武器,如果有,那么追击敌人,否则,巡逻状态转换为寻找武器。 + // if (aiCharacter.CurrentItem is WeaponTemplate weaponTemplate) + // { context.CurrentState = State.Chase; + // } + // else + // { + // context.CurrentState = State.LookForWeapon; + // } + LogCat.Log("patrol_enemy_detected", label: LogCat.LogLabel.PatrolStateProcessor); return; } diff --git a/scripts/utils/NodeUtils.cs b/scripts/utils/NodeUtils.cs index 20d6942..2d8e121 100644 --- a/scripts/utils/NodeUtils.cs +++ b/scripts/utils/NodeUtils.cs @@ -48,7 +48,66 @@ public static class NodeUtils { childNode.CallDeferred("reparent", parentNode); } - + + + /// + /// ShowNode + /// 显示节点 + /// + /// + ///node + ///节点 + /// + /// + ///Is it displayed successfully? + ///是否显示成功 + /// + public static bool ShowNode(Node node) + { + if (node is Node2D node2D) + { + node2D.Show(); + return true; + } + + if (node is CanvasItem canvasItem) + { + canvasItem.Show(); + return true; + } + + return false; + } + + /// + /// hidden node + /// 隐藏节点 + /// + /// + ///Node to hide + ///要隐藏的节点 + /// + /// + ///Hide success or not + ///是否隐藏成功 + /// + public static bool HideNode(Node node) + { + if (node is Node2D node2D) + { + node2D.Hide(); + return true; + } + + if (node is CanvasItem canvasItem) + { + canvasItem.Hide(); + return true; + } + + return false; + } + /// /// Sets child nodes for a node