With the addition of LogLabel, character support is now patrolling between several points.

加入LogLabel,角色支持在几个点之间巡逻了。
This commit is contained in:
Cold-Mint 2024-07-02 23:16:04 +08:00
parent bce36eeee9
commit 4251035a3b
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
17 changed files with 373 additions and 223 deletions

View File

@ -48,3 +48,8 @@ log_state_processor_already_registered,状态处理器已经注册。,State proc
log_state_machine_does_not_specify_processor,状态机没有指定处理器。,The state machine does not specify a processor.,ステートマシンはプロセッサを指定していません。
log_try_to_set_the_same_state,尝试设置相同的状态。,Try to set the same state.,同じ状態を設定しようとしています。
log_state_machine_does_not_specify_active_processor,状态机没有指定活动处理器。,The state machine does not specify an active processor.,ステートマシンはアクティブプロセッサを指定していません。
log_no_points,巡逻路径没有点。,The patrol path has no points.,巡回路にポイントがありません。
log_patrol_to_next_point,下一个点{0},当前位置{1},偏移量{2},距离为{3}。,"Next point {0}, current position {1}, offset {2}, distance {3}.",次のポイント{0}、現在の位置{1}、オフセット{2}、距離{3}。
log_patrol_arrival_point,到达巡逻点{0}。,Arrival at patrol point {0}.,巡回ポイント{0}に到着します。
log_patrol_origin_position,巡逻路径的起始位置是{0}。,The starting position of the patrol path is {0}.,巡回路の開始位置は{0}です。
log_patrol_not_on_floor,不能将初始路径设置在空中。,The initial path cannot be set in the air.,初期パスを空中に設定できません。
1 id zh en ja
48 log_no_points 巡逻路径没有点。 The patrol path has no points. 巡回路にポイントがありません。
49 log_patrol_to_next_point 下一个点{0},当前位置{1},偏移量{2},距离为{3}。 Next point {0}, current position {1}, offset {2}, distance {3}. 次のポイント{0}、現在の位置{1}、オフセット{2}、距離{3}。
50 log_patrol_arrival_point 到达巡逻点{0}。 Arrival at patrol point {0}. 巡回ポイント{0}に到着します。
51 log_patrol_origin_position 巡逻路径的起始位置是{0}。 The starting position of the patrol path is {0}. 巡回路の開始位置は{0}です。
52 log_patrol_not_on_floor 不能将初始路径设置在空中。 The initial path cannot be set in the air. 初期パスを空中に設定できません。
53
54
55

View File

@ -79,3 +79,4 @@ collision_mask = 68
shape = SubResource("CircleShape2D_c61vr")
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
debug_enabled = true

View File

@ -62,7 +62,7 @@ position = Vector2(193, 15)
shape = SubResource("RectangleShape2D_7tsse")
[node name="Marker2D" type="Marker2D" parent="."]
position = Vector2(220, 103)
position = Vector2(260, 87)
script = ExtResource("2_wamhd")
metadata/ResPath = "res://prefab/entitys/DelivererOfDarkMagic.tscn"

View File

@ -63,7 +63,7 @@ position = Vector2(0, 17)
shape = SubResource("RectangleShape2D_131jn")
[node name="Marker2D" type="Marker2D" parent="."]
position = Vector2(183, 72)
position = Vector2(237, 69)
script = ExtResource("2_7q101")
metadata/ResPath = "res://prefab/entitys/DelivererOfDarkMagic.tscn"

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using ColdMint.scripts.debug;
using ColdMint.scripts.stateMachine;
using Godot;
@ -42,7 +43,6 @@ public sealed partial class AiCharacter : CharacterTemplate
/// </remarks>
private RayCast2D? _attackObstacleDetection;
private float _horizontalMoveVelocity;
/// <summary>
/// <para>Navigation agent</para>
@ -96,7 +96,12 @@ public sealed partial class AiCharacter : CharacterTemplate
protected override void HookPhysicsProcess(ref Vector2 velocity, double delta)
{
StateMachine?.Execute();
velocity.X = _horizontalMoveVelocity;
if (NavigationAgent2D != null && IsOnFloor())
{
var nextPathPosition = NavigationAgent2D.GetNextPathPosition();
var direction = (nextPathPosition - GlobalPosition).Normalized();
velocity = direction * Config.CellSize * Speed;
}
}
private void EnterTheAttackArea(Node node)
@ -109,58 +114,22 @@ public sealed partial class AiCharacter : CharacterTemplate
_nodesInTheAttackRange?.Remove(node);
}
/// <summary>
/// <para>Move left</para>
/// <para>向左移动</para>
/// <para>Set target location</para>
/// <para>设置目标位置</para>
/// </summary>
public void MoveLeft()
/// <param name="targetPosition"></param>
public void SetTargetPosition(Vector2 targetPosition)
{
if (!IsOnFloor())
if (NavigationAgent2D == null)
{
return;
}
_horizontalMoveVelocity = -Speed * Config.CellSize;
NavigationAgent2D.TargetPosition = targetPosition;
}
/// <summary>
/// <para>Move right</para>
/// <para>向右移动</para>
/// </summary>
public void MoveRight()
{
if (!IsOnFloor())
{
return;
}
_horizontalMoveVelocity = Speed;
}
/// <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()
{
_horizontalMoveVelocity = 0;
}
public override void _ExitTree()
{

View File

@ -27,6 +27,7 @@ public partial class CharacterTemplate : CharacterBody2D
// Get the gravity from the project settings to be synced with RigidBody nodes.
// 从项目设置中获取与RigidBody节点同步的重力。
protected float Gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
/// <summary>
/// <para>How fast the character moves</para>
/// <para>角色的移动速度</para>
@ -532,11 +533,13 @@ public partial class CharacterTemplate : CharacterBody2D
if (damageTemplate.Attacker is CharacterTemplate characterTemplate &&
!string.IsNullOrEmpty(characterTemplate.CharacterName))
{
LogCat.LogWithFormat("death_info", CharacterName, characterTemplate.CharacterName);
LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, CharacterName,
characterTemplate.CharacterName);
}
else
{
LogCat.LogWithFormat("death_info", CharacterName, damageTemplate.Attacker.Name);
LogCat.LogWithFormat("death_info", LogCat.LogLabel.Default, CharacterName,
damageTemplate.Attacker.Name);
}
}
@ -734,6 +737,7 @@ public partial class CharacterTemplate : CharacterBody2D
{
return;
}
//We continuously set the position of the items to prevent them from changing as we zoom in and out of the window.
//我们持续设置物品的位置,为了防止放大缩小窗口时物品位置的变化。
if (_currentItem != null)

View File

@ -50,7 +50,8 @@ public partial class Player : CharacterTemplate
{
base._Ready();
CharacterName = TranslationServerUtils.Translate("default_player_name");
LogCat.LogWithFormat("player_spawn_debug", ReadOnlyCharacterName, GlobalPosition);
LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, ReadOnlyCharacterName,
GlobalPosition);
_floatLabelPackedScene = GD.Load<PackedScene>("res://prefab/ui/FloatLabel.tscn");
_parabola = GetNode<Line2D>("Parabola");
_platformDetectionRayCast2D = GetNode<RayCast2D>("PlatformDetectionRayCast");

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using ColdMint.scripts.utils;
using Godot;
@ -7,6 +8,22 @@ namespace ColdMint.scripts.debug;
public static class LogCat
{
public static class LogLabel
{
/// <summary>
/// <para>Default log label</para>
/// <para>默认的日志标签</para>
/// </summary>
public const string Default = "Default";
/// <summary>
/// <para>PatrolStateProcessor</para>
/// <para>巡逻状态处理器</para>
/// </summary>
public const string PatrolStateProcessor = "PatrolStateProcessor";
}
/// <summary>
/// <para>Information log level</para>
/// <para>信息日志等级</para>
@ -49,8 +66,65 @@ public static class LogCat
private static readonly StringBuilder StringBuilder = new StringBuilder();
/// <summary>
/// <para>Disabled log label</para>
/// <para>禁用的日志标签</para>
/// </summary>
private static HashSet<string> DisabledLogLabels { get; } = [];
private static StringBuilder HandleMessage(int level, string message)
/// <summary>
/// <para>Disable log Label</para>
/// <para>禁用某个日志标签</para>
/// </summary>
/// <param name="label">
///<para>label</para>
///<para>标签名称</para>
/// </param>
/// <returns>
///<para>Returns whether the function is disabled successfully</para>
///<para>返回是否禁用成功</para>
/// </returns>
public static bool DisableLogLabel(string label)
{
return DisabledLogLabels.Add(label);
}
/// <summary>
/// <para>Whether a label is enabled</para>
/// <para>某个标签是否处于启用状态</para>
/// </summary>
/// <param name="label">
///<para>label</para>
///<para>标签名称</para>
/// </param>
/// <returns>
///<para>Whether enabled</para>
///<para>是否处于启用</para>
/// </returns>
public static bool IsEnabledLogLabel(string label)
{
return !DisabledLogLabels.Contains(label);
}
/// <summary>
/// <para>EnableLogLabel</para>
/// <para>启用某个日志标签</para>
/// </summary>
/// <param name="label">
///<para>label</para>
/// <para>标签名称</para>
/// </param>
/// <returns>
///<para>Returns whether the function is enabled successfully</para>
///<para>返回是否启用成功</para>
/// </returns>
public static bool EnableLogLabel(string label)
{
return DisabledLogLabels.Remove(label);
}
private static StringBuilder HandleMessage(int level, string message, string label)
{
StringBuilder.Clear();
switch (level)
@ -69,7 +143,9 @@ public static class LogCat
break;
}
StringBuilder.Append(DateTime.Now.ToString(" yyyy-M-d HH:mm:ss : "));
StringBuilder.Append(DateTime.Now.ToString(" yyyy-M-d HH:mm:ss "));
StringBuilder.Append(label);
StringBuilder.Append(" :");
var key = $"log_{message}";
var translationResult = TranslationServerUtils.Translate(key);
StringBuilder.Append(translationResult == key ? message : translationResult);
@ -86,14 +162,21 @@ public static class LogCat
/// <para>This message supports localized output, assuming there is already a translation key, Hello = 你好, passing hello will output 你好.</para>
/// <para>这个消息支持本地化输出假设已存在翻译keyHello = 你好传入Hello则会输出你好。</para>
/// </param>
public static void Log(string message)
/// <param name="label">
/// </param>
public static void Log(string message, string label = LogLabel.Default)
{
if (!IsEnabledLogLabel(label))
{
return;
}
if (_minLogLevel > InfoLogLevel)
{
return;
}
GD.Print(HandleMessage(InfoLogLevel, message));
GD.Print(HandleMessage(InfoLogLevel, message, label));
}
/// <summary>
@ -106,55 +189,81 @@ public static class LogCat
/// <para>This message supports localized output, assuming there is already a translation key, Hello = 你好, passing hello will output 你好.</para>
/// <para>这个消息支持本地化输出假设已存在翻译keyHello = 你好传入Hello则会输出你好。</para>
/// </param>
public static void LogError(string message)
/// <param name="label"></param>
public static void LogError(string message, string label = LogLabel.Default)
{
if (!IsEnabledLogLabel(label))
{
return;
}
if (_minLogLevel > ErrorLogLevel)
{
return;
}
GD.PrintErr(HandleMessage(ErrorLogLevel, message));
GD.PrintErr(HandleMessage(ErrorLogLevel, message, label));
}
public static void LogWarning(string message)
public static void LogWarning(string message, string label = LogLabel.Default)
{
if (!IsEnabledLogLabel(label))
{
return;
}
if (_minLogLevel > WarningLogLevel)
{
return;
}
GD.Print(HandleMessage(WarningLogLevel, message));
GD.Print(HandleMessage(WarningLogLevel, message, label));
}
public static void LogErrorWithFormat(string message, params object?[] args)
public static void LogErrorWithFormat(string message, string label, params object?[] args)
{
if (!IsEnabledLogLabel(label))
{
return;
}
if (_minLogLevel > ErrorLogLevel)
{
return;
}
GD.PrintErr(string.Format(HandleMessage(ErrorLogLevel, message).ToString(), args));
GD.PrintErr(string.Format(HandleMessage(ErrorLogLevel, message, label).ToString(), args));
}
public static void LogWithFormat(string message, params object?[] args)
public static void LogWithFormat(string message, string label, params object?[] args)
{
if (!IsEnabledLogLabel(label))
{
return;
}
if (_minLogLevel > InfoLogLevel)
{
return;
}
GD.Print(string.Format(HandleMessage(InfoLogLevel, message).ToString(), args));
GD.Print(string.Format(HandleMessage(InfoLogLevel, message, label).ToString(), args));
}
public static void LogWarningWithFormat(string message, params object?[] args)
public static void LogWarningWithFormat(string message, string label, params object?[] args)
{
if (!IsEnabledLogLabel(label))
{
return;
}
if (_minLogLevel > InfoLogLevel)
{
return;
}
GD.Print(string.Format(HandleMessage(WarningLogLevel, message).ToString(), args));
GD.Print(string.Format(HandleMessage(WarningLogLevel, message, label).ToString(), args));
}
/// <summary>
@ -162,10 +271,16 @@ public static class LogCat
/// <para>当捕获异常后调用此方法</para>
/// </summary>
/// <param name="e"></param>
public static void WhenCaughtException(Exception e)
/// <param name="label"></param>
public static void WhenCaughtException(Exception e, string label = LogLabel.Default)
{
if (!IsEnabledLogLabel(label))
{
return;
}
//Log an exception here or send it to the server.
//请在这里记录异常或将异常发送至服务器。
GD.PrintErr(HandleMessage(ErrorLogLevel, e.Message).Append('\n').Append(e.StackTrace));
GD.PrintErr(HandleMessage(ErrorLogLevel, e.Message, label).Append('\n').Append(e.StackTrace));
}
}

View File

@ -43,16 +43,16 @@ public static class ItemTypeRegister
var files = itemRegsDir.GetFiles();
if (files == null)
{
LogCat.LogWithFormat("found_files", 0);
LogCat.LogWithFormat("found_files", LogCat.LogLabel.Default, 0);
return;
}
LogCat.LogWithFormat("found_files", files.Length);
LogCat.LogWithFormat("found_files", LogCat.LogLabel.Default, files.Length);
//将文件解析为项目类型信息
//parse files to item type infos
IEnumerable<ItemTypeInfo> typeInfos =
files.SelectMany(file => ParseFile($"{itemRegsDirPath}/{file}")).ToList();
LogCat.LogWithFormat("found_item_types", typeInfos.Count());
LogCat.LogWithFormat("found_item_types", LogCat.LogLabel.Default, typeInfos.Count());
//遍历类型信息并注册它们。
//traverse type infos and register them.
@ -119,7 +119,7 @@ public static class ItemTypeRegister
},
icon, typeInfo.MaxStackValue);
var succeed = ItemTypeManager.Register(itemType);
LogCat.LogWithFormat("register_item", itemType.Id, succeed);
LogCat.LogWithFormat("register_item", label: LogCat.LogLabel.Default, itemType.Id, succeed);
}
//Use for yaml deserialization
@ -150,7 +150,7 @@ public static class ItemTypeRegister
var ss = s.Split(',');
if (ss.Length != 2)
{
LogCat.LogErrorWithFormat("wrong_custom_arg", "Vector2", s);
LogCat.LogErrorWithFormat("wrong_custom_arg", LogCat.LogLabel.Default, "Vector2", s);
return Vector2.Zero;
}

View File

@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Text;
using ColdMint.scripts.camp;
using ColdMint.scripts.contribute;
using ColdMint.scripts.deathInfo;
@ -11,7 +10,6 @@ using ColdMint.scripts.loot;
using ColdMint.scripts.map;
using ColdMint.scripts.map.roomInjectionProcessor;
using ColdMint.scripts.utils;
using Godot;
namespace ColdMint.scripts.loader.uiLoader;
@ -47,8 +45,6 @@ public partial class MainMenuLoader : UiLoaderTemplate
//在发行版禁用所有日志。
LogCat.MinLogLevel = LogCat.DisableAllLogLevel;
}
ContributorDataManager.RegisterAllContributorData();
DeathInfoGenerator.RegisterDeathInfoHandler(new SelfDeathInfoHandler());
MapGenerator.RegisterRoomInjectionProcessor(new ChanceRoomInjectionProcessor());
@ -135,6 +131,7 @@ public partial class MainMenuLoader : UiLoaderTemplate
{
return;
}
GetTree().ChangeSceneToPacked(_gameScene);
};
}
@ -147,6 +144,7 @@ public partial class MainMenuLoader : UiLoaderTemplate
{
return;
}
GetTree().ChangeSceneToPacked(_contributor);
};
}
@ -160,6 +158,7 @@ public partial class MainMenuLoader : UiLoaderTemplate
{
return;
}
GetTree().ChangeSceneToPacked(_levelGraphEditor);
};
}

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using ColdMint.scripts.debug;
using ColdMint.scripts.utils;
@ -26,7 +25,7 @@ public readonly struct LootList(string id, IList<LootGroup> groups)
{
if (Groups is [])
{
LogCat.LogWithFormat("loot_list_has_no_entries", Id);
LogCat.LogWithFormat("loot_list_has_no_entries", LogCat.LogLabel.Default, Id);
return [];
}
@ -42,10 +41,10 @@ public readonly struct LootList(string id, IList<LootGroup> groups)
//我们为每个战利品条目生成一个战利品数据。
var datum = group.GenerateLootData();
lootDataList.Add(datum);
LogCat.LogWithFormat("loot_data_add", datum);
LogCat.LogWithFormat("loot_data_add", LogCat.LogLabel.Default, datum);
}
LogCat.LogWithFormat("loot_data_quantity", lootDataList.Count);
LogCat.LogWithFormat("loot_data_quantity", LogCat.LogLabel.Default, lootDataList.Count);
return lootDataList.ToArray();
}
}

View File

@ -241,7 +241,7 @@ public static class MapGenerator
//If the room injection processor cannot be found, a print error occurs.
//如果找不到房间注入处理器,那么打印错误。
LogCat.LogErrorWithFormat("room_injection_processor_does_not_exist",
injectionProcessorData.Id);
LogCat.LogLabel.Default, injectionProcessorData.Id);
continue;
}
@ -277,7 +277,8 @@ public static class MapGenerator
roomNodeData);
if (roomPlacementData == null)
{
LogCat.LogWithFormat("failed_to_calculate_the_room_location", roomNodeData.Id);
LogCat.LogWithFormat("failed_to_calculate_the_room_location", LogCat.LogLabel.Default,
roomNodeData.Id);
continue;
}
@ -323,18 +324,19 @@ public static class MapGenerator
if (dictionary.ContainsKey(roomNodeDataId))
{
LogCat.LogWithFormat("place_existing_rooms", roomNodeDataId);
LogCat.LogWithFormat("place_existing_rooms", LogCat.LogLabel.Default, roomNodeDataId);
return false;
}
if (!await _roomPlacementStrategy.PlaceRoom(_mapRoot, roomPlacementData))
{
LogCat.LogWarningWithFormat("room_placement_failed", roomNodeDataId);
LogCat.LogWarningWithFormat("room_placement_failed", LogCat.LogLabel.Default, roomNodeDataId);
return false;
}
dictionary.Add(roomNodeDataId, roomPlacementData.Room);
LogCat.LogWithFormat("room_placement_information", roomNodeDataId, roomPlacementData.Position.ToString());
LogCat.LogWithFormat("room_placement_information", LogCat.LogLabel.Default, roomNodeDataId,
roomPlacementData.Position.ToString());
return true;
}
}

View File

@ -36,7 +36,7 @@ public class Room
{
if (_rootNode != null)
{
LogCat.LogWithFormat("enter_the_room_debug", node.Name, _rootNode.Name);
LogCat.LogWithFormat("enter_the_room_debug", LogCat.LogLabel.Default, node.Name, _rootNode.Name);
}
if (string.IsNullOrEmpty(EnterRoomEventHandlerId))
@ -57,7 +57,7 @@ public class Room
{
if (_rootNode != null)
{
LogCat.LogWithFormat("exit_the_room_debug", node.Name, _rootNode.Name);
LogCat.LogWithFormat("exit_the_room_debug", LogCat.LogLabel.Default, node.Name, _rootNode.Name);
}
if (string.IsNullOrEmpty(ExitRoomEventHandlerId))
@ -108,6 +108,7 @@ public class Room
{
return;
}
var node2D = NodeUtils.InstantiatePackedScene<Node2D>(packedScene);
if (node2D == null)
{

View File

@ -10,6 +10,17 @@ public class PatrolStateMachine : StateMachineTemplate
{
protected override void OnStart(StateContext context)
{
RegisterProcessor(new PatrolStateProcessor());
var patrolStateProcessor = new PatrolStateProcessor
{
Points =
[
new Godot.Vector2(100, 0),
new Godot.Vector2(-100, 0),
new Godot.Vector2(50, 0),
new Godot.Vector2(-50, 0),
new Godot.Vector2(0, 0)
]
};
RegisterProcessor(patrolStateProcessor);
}
}

View File

@ -10,6 +10,11 @@ namespace ColdMint.scripts.stateMachine.StateProcessor;
/// </summary>
public class PatrolStateProcessor : StateProcessorTemplate
{
public Vector2[]? Points { get; set; }
private int _index;
private Vector2? _originPosition;
protected override void OnExecute(StateContext context, Node owner)
{
if (owner is not AiCharacter aiCharacter)
@ -17,8 +22,45 @@ public class PatrolStateProcessor : StateProcessorTemplate
return;
}
aiCharacter.MoveLeft();
if (Points == null || Points.Length == 0)
{
LogCat.LogError("no_points", label: LogCat.LogLabel.PatrolStateProcessor);
return;
}
if (_originPosition == null)
{
if (!aiCharacter.IsOnFloor())
{
LogCat.LogWarning("patrol_not_on_floor", LogCat.LogLabel.PatrolStateProcessor);
return;
}
_originPosition = aiCharacter.GlobalPosition;
LogCat.LogWithFormat("patrol_origin_position", LogCat.LogLabel.PatrolStateProcessor,
_originPosition);
}
var point = _originPosition + Points[_index];
var distance = aiCharacter.GlobalPosition.DistanceTo(point.Value);
if (distance < 10)
{
LogCat.LogWithFormat("patrol_arrival_point", LogCat.LogLabel.PatrolStateProcessor, point);
_index++;
if (_index >= Points.Length)
{
_index = 0;
}
}
else
{
LogCat.LogWithFormat("patrol_to_next_point", label: LogCat.LogLabel.PatrolStateProcessor, point,
aiCharacter.GlobalPosition, Points[_index],
distance);
aiCharacter.SetTargetPosition(point.Value);
}
}
public override State State => State.Patrol;
}

View File

@ -220,7 +220,7 @@ public static class NodeUtils
if (node is T result) return result;
// If the transformation fails, release the created node
//如果转型失败,释放所创建的节点
LogCat.LogWarningWithFormat("warning_node_cannot_cast_to", node, nameof(T));
LogCat.LogWarningWithFormat("warning_node_cannot_cast_to", LogCat.LogLabel.Default, node, nameof(T));
node.QueueFree();
return null;
}

View File

@ -33,7 +33,8 @@ public static class TimeUtils
var compNum1 = DateTime.Compare(dateTime, dtStartTime);
var compNum2 = DateTime.Compare(dateTime, dtEndTime);
var result = compNum1 >= 0 && compNum2 <= 0;
LogCat.LogWithFormat("time_range_debug", dateTime, dtStartTime, dtEndTime, result);
LogCat.LogWithFormat("time_range_debug", LogCat.LogLabel.Default, dateTime, dtStartTime, dtEndTime,
result);
return result;
}
}