AI characters can set default weapons. The AI will try to attack and kill the enemy now. Fixed an issue where bubbles would not display properly.
AI角色支持设置默认武器。AI会尝试攻击并杀死敌人了。修复气泡不能正常显示的问题。
This commit is contained in:
parent
adee87429e
commit
61618c13a9
|
@ -3,3 +3,6 @@ id,zh,en,ja
|
|||
#自杀
|
||||
death_info_self_1,{0}误伤了自己。,{0} accidentally shot themself.,{0}誤って自分を傷つけました。
|
||||
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}に殺されました。
|
|
|
@ -70,3 +70,6 @@ log_set_default_camp,设置默认阵营{0}。,Set default camp {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.,敵がいない追跡です。
|
||||
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.,武器コンテナが空です。
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
|||
/// </summary>
|
||||
public partial class BubbleMarker : Marker2D
|
||||
{
|
||||
private readonly Dictionary<int, Node2D> _bubbleDictionary = [];
|
||||
private readonly Dictionary<int, Node> _bubbleDictionary = [];
|
||||
|
||||
/// <summary>
|
||||
/// <para>Add bubbles</para>
|
||||
|
@ -20,14 +20,14 @@ public partial class BubbleMarker : Marker2D
|
|||
/// <param name="id"></param>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|||
/// </remarks>
|
||||
private BubbleMarker? _bubbleMarker;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The initial weapons scene</para>
|
||||
/// <para>初始的武器场景</para>
|
||||
/// </summary>
|
||||
[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<PackedScene>("res://prefab/ui/plaint.tscn");
|
||||
var plaint = NodeUtils.InstantiatePackedScene<Node2D>(plaintScene);
|
||||
var plaint = NodeUtils.InstantiatePackedScene<Control>(plaintScene);
|
||||
if (plaint != null)
|
||||
{
|
||||
_bubbleMarker.AddBubble(plaintBubbleId, plaint);
|
||||
}
|
||||
|
||||
using var queryScene = GD.Load<PackedScene>("res://prefab/ui/query.tscn");
|
||||
var query = NodeUtils.InstantiatePackedScene<Node2D>(queryScene);
|
||||
var query = NodeUtils.InstantiatePackedScene<Control>(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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Adds an initial weapon to the character</para>
|
||||
/// <para>为角色添加初始的武器</para>
|
||||
/// </summary>
|
||||
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<PackedScene>(initWeaponRes);
|
||||
var weaponTemplate = NodeUtils.InstantiatePackedScene<WeaponTemplate>(packedScene);
|
||||
if (weaponTemplate == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
NodeUtils.CallDeferredAddChild(this, weaponTemplate);
|
||||
PickItem(weaponTemplate);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Display exclamation marks</para>
|
||||
/// <para>显示感叹号</para>
|
||||
|
@ -195,7 +237,7 @@ public sealed partial class AiCharacter : CharacterTemplate
|
|||
_bubbleMarker?.ShowBubble(queryBubbleId);
|
||||
}
|
||||
|
||||
public void HiddenQuery()
|
||||
public void HideQuery()
|
||||
{
|
||||
_bubbleMarker?.HideBubble(queryBubbleId);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -76,6 +76,6 @@ public static class DeathInfoGenerator
|
|||
/// <returns></returns>
|
||||
private static string? GenerateDefaultDeathInfo(string victimName, string killerName)
|
||||
{
|
||||
return TranslationServerUtils.TranslateWithFormat("death_info", victimName, killerName);
|
||||
return TranslationServerUtils.TranslateWithFormat("death_info_default", victimName, killerName);
|
||||
}
|
||||
}
|
|
@ -45,6 +45,12 @@ public static class LogCat
|
|||
/// <para>追击敌人处理器</para>
|
||||
/// </summary>
|
||||
public const string ChaseStateProcessor = "ChaseStateProcessor";
|
||||
|
||||
/// <summary>
|
||||
/// <para>BubbleMarker</para>
|
||||
/// <para>气泡标记</para>
|
||||
/// </summary>
|
||||
public static string BubbleMarker = "BubbleMarker";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Weapon seeking condition</para>
|
||||
/// <para>寻找武器状态</para>
|
||||
/// </summary>
|
||||
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<WeaponTemplate>(GameSceneNodeHolder.WeaponContainer, template =>
|
||||
{
|
||||
if (template.GlobalPosition.DistanceTo(aiCharacter.GlobalPosition) > 100)
|
||||
{
|
||||
weaponTemplate = template;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public override State State => State.LookForWeapon;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,65 @@ public static class NodeUtils
|
|||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>ShowNode</para>
|
||||
/// <para>显示节点</para>
|
||||
/// </summary>
|
||||
/// <param name="node">
|
||||
///<para>node</para>
|
||||
///<para>节点</para>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
///<para>Is it displayed successfully?</para>
|
||||
///<para>是否显示成功</para>
|
||||
/// </returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>hidden node</para>
|
||||
/// <para>隐藏节点</para>
|
||||
/// </summary>
|
||||
/// <param name="node">
|
||||
///<para>Node to hide</para>
|
||||
///<para>要隐藏的节点</para>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
///<para>Hide success or not</para>
|
||||
///<para>是否隐藏成功</para>
|
||||
/// </returns>
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Sets child nodes for a node</para>
|
||||
/// <para>为某个节点设置子节点</para>
|
||||
|
|
Loading…
Reference in New Issue
Block a user