mirror of
https://github.com/milimoe/FunGame-Testing.git
synced 2025-12-05 00:06:05 +00:00
移动不再计算硬直
This commit is contained in:
parent
ba70102207
commit
86e4611656
75
Desktop/GameMapTesting/CharacterQueueItemViewModel.cs
Normal file
75
Desktop/GameMapTesting/CharacterQueueItemViewModel.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
||||||
|
{
|
||||||
|
public class CharacterQueueItemViewModel(CharacterQueueItem model, Func<Dictionary<int, List<Skill>>> getTurnRewards) : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
public CharacterQueueItem Model { get; } = model ?? throw new ArgumentNullException(nameof(model));
|
||||||
|
public Character Character => Model.Character;
|
||||||
|
public double ATDelay => Model.ATDelay;
|
||||||
|
|
||||||
|
private int _predictedTurnNumber;
|
||||||
|
public int PredictedTurnNumber
|
||||||
|
{
|
||||||
|
get => _predictedTurnNumber;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_predictedTurnNumber != value)
|
||||||
|
{
|
||||||
|
_predictedTurnNumber = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
// 当回合数变化时,奖励信息可能也变化,因此需要更新
|
||||||
|
UpdateRewardProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _turnRewardSkillName = "";
|
||||||
|
public string TurnRewardSkillName
|
||||||
|
{
|
||||||
|
get => _turnRewardSkillName;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_turnRewardSkillName != value)
|
||||||
|
{
|
||||||
|
_turnRewardSkillName = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(HasTurnReward)); // 奖励名称变化时,通知可见性也可能变化
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasTurnReward => !string.IsNullOrEmpty(TurnRewardSkillName);
|
||||||
|
|
||||||
|
// 用于获取 TurnRewards 字典的委托,避免直接依赖 GameMapViewer
|
||||||
|
private readonly Func<Dictionary<int, List<Skill>>> _getTurnRewards = getTurnRewards ?? throw new ArgumentNullException(nameof(getTurnRewards));
|
||||||
|
|
||||||
|
// 当 PredictedTurnNumber 或 TurnRewards 变化时调用此方法
|
||||||
|
public void UpdateRewardProperties()
|
||||||
|
{
|
||||||
|
Dictionary<int, List<Skill>> turnRewards = _getTurnRewards();
|
||||||
|
if (turnRewards != null && turnRewards.TryGetValue(PredictedTurnNumber, out List<Skill>? rewardSkills))
|
||||||
|
{
|
||||||
|
TurnRewardSkillName = string.Join(";", rewardSkills.Select(s => s.Name.Replace("[R]", "").Trim()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TurnRewardSkillName = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CharacterQueueItem(Character character, double atDelay)
|
||||||
|
{
|
||||||
|
public Character Character { get; set; } = character;
|
||||||
|
public double ATDelay { get; set; } = atDelay;
|
||||||
|
}
|
||||||
|
}
|
||||||
129
Desktop/GameMapTesting/Converters.cs
Normal file
129
Desktop/GameMapTesting/Converters.cs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
||||||
|
{
|
||||||
|
public class FirstCharConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is string s && s.Length > 0)
|
||||||
|
{
|
||||||
|
return s[0].ToString().ToUpper();
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CharacterToStringWithLevelConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is Character character)
|
||||||
|
{
|
||||||
|
string name = character.ToString();
|
||||||
|
if (character.CharacterState == CharacterState.Casting)
|
||||||
|
{
|
||||||
|
name += " - 吟唱";
|
||||||
|
}
|
||||||
|
else if (character.CharacterState == CharacterState.PreCastSuperSkill)
|
||||||
|
{
|
||||||
|
name += " - 爆发技";
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return "[未知角色]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SkillItemFormatterConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
string name = "";
|
||||||
|
if (value is Skill skill)
|
||||||
|
{
|
||||||
|
Character? character = skill.Character;
|
||||||
|
name = $"【{(skill.IsSuperSkill ? "爆发技" : (skill.IsMagic ? "魔法" : "战技"))}】{skill.Name}";
|
||||||
|
if (skill.CurrentCD > 0)
|
||||||
|
{
|
||||||
|
name += $" - 冷却剩余 {skill.CurrentCD:0.##} 秒";
|
||||||
|
}
|
||||||
|
else if (skill.RealEPCost > 0 && skill.RealMPCost > 0 && character != null && character.EP < skill.RealEPCost && character.MP < skill.RealMPCost)
|
||||||
|
{
|
||||||
|
name += $" - 能量/魔法要求 {skill.RealEPCost:0.##} / {skill.RealMPCost:0.##} 点";
|
||||||
|
}
|
||||||
|
else if (skill.RealEPCost > 0 && character != null && character.EP < skill.RealEPCost)
|
||||||
|
{
|
||||||
|
name += $" - 能量不足,要求 {skill.RealEPCost:0.##} 点";
|
||||||
|
}
|
||||||
|
else if (skill.RealMPCost > 0 && character != null && character.MP < skill.RealMPCost)
|
||||||
|
{
|
||||||
|
name += $" - 魔法不足,要求 {skill.RealMPCost:0.##} 点";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (value is Item item)
|
||||||
|
{
|
||||||
|
name = item.Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
name = value?.ToString() ?? name;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组合转换器:判断技能或物品是否可用。
|
||||||
|
/// 接收 Skill/Item 对象和当前 Character 对象。
|
||||||
|
/// </summary>
|
||||||
|
public class SkillUsabilityConverter : IMultiValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
// values[0] 应该是 Skill 或 Item 对象
|
||||||
|
// values[1] 应该是 PlayerCharacter 对象
|
||||||
|
if (values.Length < 2 || values[1] is not Character character)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values[0] is Skill s)
|
||||||
|
{
|
||||||
|
return s.Level > 0 && s.SkillType != SkillType.Passive && s.Enable && !s.IsInEffect && s.CurrentCD == 0 &&
|
||||||
|
((s.SkillType == SkillType.SuperSkill || s.SkillType == SkillType.Skill) && s.RealEPCost <= character.EP || s.SkillType == SkillType.Magic && s.RealMPCost <= character.MP);
|
||||||
|
}
|
||||||
|
else if (values[0] is Item i)
|
||||||
|
{
|
||||||
|
return i.IsActive && i.Skills.Active != null && i.Enable && i.IsInGameItem &&
|
||||||
|
i.Skills.Active.SkillType == SkillType.Item && i.Skills.Active.Enable && !i.Skills.Active.IsInEffect && i.Skills.Active.CurrentCD == 0 &&
|
||||||
|
i.Skills.Active.RealMPCost <= character.MP && i.Skills.Active.RealEPCost <= character.EP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using Milimoe.FunGame.Core.Entity;
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Entity;
|
||||||
using Milimoe.FunGame.Core.Library.Common.Addon;
|
using Milimoe.FunGame.Core.Library.Common.Addon;
|
||||||
using Milimoe.FunGame.Core.Library.Constant;
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
private readonly UserInputRequester<long> _itemSelectionRequester = new();
|
private readonly UserInputRequester<long> _itemSelectionRequester = new();
|
||||||
private readonly UserInputRequester<bool> _continuePromptRequester = new(); // 用于“按任意键继续”提示
|
private readonly UserInputRequester<bool> _continuePromptRequester = new(); // 用于“按任意键继续”提示
|
||||||
|
|
||||||
public void WriteLine(string str = "") => UI.AppendDebugLog(str);
|
public async Task WriteLine(string str = "") => await UI.AppendDebugLog(str);
|
||||||
|
|
||||||
public async Task Start()
|
public async Task Start()
|
||||||
{
|
{
|
||||||
@ -25,9 +26,22 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
await _game.StartGame(false);
|
await _game.StartGame(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SetPreCastSuperSkill(Character character, Skill skill)
|
||||||
|
{
|
||||||
|
if (_game != null)
|
||||||
|
{
|
||||||
|
await _game.SetPreCastSuperSkill(character, skill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPredictCharacter(string name, double ht)
|
||||||
|
{
|
||||||
|
UI.Invoke(() => UI.SetPredictCharacter(name, ht));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<long> RequestCharacterSelection(List<Character> availableCharacters)
|
public async Task<long> RequestCharacterSelection(List<Character> availableCharacters)
|
||||||
{
|
{
|
||||||
WriteLine("请选择你想玩的角色。");
|
await WriteLine("请选择你想玩的角色。");
|
||||||
return await _characterSelectionRequester.RequestInput(
|
return await _characterSelectionRequester.RequestInput(
|
||||||
(callback) => UI.Invoke(() => UI.ShowCharacterSelectionPrompt(availableCharacters, callback))
|
(callback) => UI.Invoke(() => UI.ShowCharacterSelectionPrompt(availableCharacters, callback))
|
||||||
);
|
);
|
||||||
@ -35,17 +49,17 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
|
|
||||||
public async Task<CharacterActionType> RequestActionType(Character character, List<Item> availableItems)
|
public async Task<CharacterActionType> RequestActionType(Character character, List<Item> availableItems)
|
||||||
{
|
{
|
||||||
WriteLine($"现在是 {character.NickName} 的回合,请选择行动。");
|
await WriteLine($"现在是 {character.NickName} 的回合,请选择行动。");
|
||||||
return await _actionTypeRequester.RequestInput(
|
return await _actionTypeRequester.RequestInput(
|
||||||
(callback) => UI.Invoke(() => UI.ShowActionButtons(character, availableItems, callback))
|
(callback) => UI.Invoke(() => UI.ShowActionButtons(character, availableItems, callback))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Character>> RequestTargetSelection(Character actor, List<Character> potentialTargets, long maxTargets, bool canSelectSelf, bool canSelectEnemy, bool canSelectTeammate)
|
public async Task<List<Character>> RequestTargetSelection(Character actor, List<Character> potentialTargets, ISkill skill, long maxTargets, bool canSelectSelf, bool canSelectEnemy, bool canSelectTeammate)
|
||||||
{
|
{
|
||||||
WriteLine($"请为 {actor.NickName} 选择目标 (最多 {maxTargets} 个)。");
|
await WriteLine($"请为 {actor.NickName} 选择目标 (最多 {maxTargets} 个)。");
|
||||||
List<Character> targetIds = await _targetSelectionRequester.RequestInput(
|
List<Character> targetIds = await _targetSelectionRequester.RequestInput(
|
||||||
(callback) => UI.Invoke(() => UI.ShowTargetSelectionUI(actor, potentialTargets, maxTargets, canSelectSelf, canSelectEnemy, canSelectTeammate, callback))
|
(callback) => UI.Invoke(() => UI.ShowTargetSelectionUI(actor, potentialTargets, skill, maxTargets, canSelectSelf, canSelectEnemy, canSelectTeammate, callback))
|
||||||
) ?? [];
|
) ?? [];
|
||||||
if (targetIds == null) return [];
|
if (targetIds == null) return [];
|
||||||
return [.. potentialTargets.Where(targetIds.Contains)];
|
return [.. potentialTargets.Where(targetIds.Contains)];
|
||||||
@ -53,7 +67,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
|
|
||||||
public async Task<Skill?> RequestSkillSelection(Character character, List<Skill> availableSkills)
|
public async Task<Skill?> RequestSkillSelection(Character character, List<Skill> availableSkills)
|
||||||
{
|
{
|
||||||
WriteLine($"请为 {character.NickName} 选择一个技能。");
|
await WriteLine($"请为 {character.NickName} 选择一个技能。");
|
||||||
long? skillId = await _skillSelectionRequester.RequestInput(
|
long? skillId = await _skillSelectionRequester.RequestInput(
|
||||||
(callback) => UI.Invoke(() => UI.ShowSkillSelectionUI(character, callback))
|
(callback) => UI.Invoke(() => UI.ShowSkillSelectionUI(character, callback))
|
||||||
);
|
);
|
||||||
@ -62,7 +76,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
|
|
||||||
public async Task<Item?> RequestItemSelection(Character character, List<Item> availableItems)
|
public async Task<Item?> RequestItemSelection(Character character, List<Item> availableItems)
|
||||||
{
|
{
|
||||||
WriteLine($"请为 {character.NickName} 选择一个物品。");
|
await WriteLine($"请为 {character.NickName} 选择一个物品。");
|
||||||
long? itemId = await _itemSelectionRequester.RequestInput(
|
long? itemId = await _itemSelectionRequester.RequestInput(
|
||||||
(callback) => UI.Invoke(() => UI.ShowItemSelectionUI(character, callback))
|
(callback) => UI.Invoke(() => UI.ShowItemSelectionUI(character, callback))
|
||||||
);
|
);
|
||||||
@ -71,12 +85,21 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
|
|
||||||
public async Task RequestContinuePrompt(string message)
|
public async Task RequestContinuePrompt(string message)
|
||||||
{
|
{
|
||||||
WriteLine(message);
|
await WriteLine(message);
|
||||||
await _continuePromptRequester.RequestInput(
|
await _continuePromptRequester.RequestInput(
|
||||||
(callback) => UI.Invoke(() => UI.ShowContinuePrompt(message, callback))
|
(callback) => UI.Invoke(() => UI.ShowContinuePrompt(message, callback))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task RequestCountDownContinuePrompt(string message, int countdownSeconds = 2)
|
||||||
|
{
|
||||||
|
await WriteLine(message);
|
||||||
|
// 调用 _continuePromptRequester 的 RequestInput 方法,它会等待回调被触发
|
||||||
|
await _continuePromptRequester.RequestInput(
|
||||||
|
(callback) => UI.Invoke(() => UI.StartCountdownForContinue(countdownSeconds, callback))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// --- GameMapViewer 调用这些方法来解决 UI 输入 ---
|
// --- GameMapViewer 调用这些方法来解决 UI 输入 ---
|
||||||
|
|
||||||
public void ResolveCharacterSelection(long characterId)
|
public void ResolveCharacterSelection(long characterId)
|
||||||
@ -115,6 +138,12 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
UI.Invoke(() => UI.HideContinuePrompt());
|
UI.Invoke(() => UI.HideContinuePrompt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ResolveCountDownContinuePrompt()
|
||||||
|
{
|
||||||
|
_continuePromptRequester.ResolveInput(true);
|
||||||
|
UI.Invoke(() => UI.HideContinuePrompt());
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsTeammate(Character actor, Character target)
|
public bool IsTeammate(Character actor, Character target)
|
||||||
{
|
{
|
||||||
if (actor == target) return true;
|
if (actor == target) return true;
|
||||||
@ -156,6 +185,22 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetCurrentRound(int round)
|
||||||
|
{
|
||||||
|
UI.Invoke(() =>
|
||||||
|
{
|
||||||
|
UI.CurrentRound = round;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTurnRewards(Dictionary<int, List<Skill>> rewards)
|
||||||
|
{
|
||||||
|
UI.Invoke(() =>
|
||||||
|
{
|
||||||
|
UI.TurnRewards = rewards;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void SetPlayerCharacter(Character character)
|
public void SetPlayerCharacter(Character character)
|
||||||
{
|
{
|
||||||
UI.Invoke(() =>
|
UI.Invoke(() =>
|
||||||
|
|||||||
@ -39,7 +39,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
List<string> result = [];
|
List<string> result = [];
|
||||||
Msg = "";
|
Msg = "";
|
||||||
List<Character> allCharactersInGame = [.. FunGameConstant.Characters]; // 使用不同的名称以避免与后面的 `characters` 冲突
|
List<Character> allCharactersInGame = [.. FunGameConstant.Characters]; // 使用不同的名称以避免与后面的 `characters` 冲突
|
||||||
Controller.WriteLine("--- 游戏开始 ---");
|
await Controller.WriteLine("--- 游戏开始 ---");
|
||||||
|
|
||||||
int clevel = 60;
|
int clevel = 60;
|
||||||
int slevel = 6;
|
int slevel = 6;
|
||||||
@ -62,7 +62,10 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 显示角色信息
|
// 显示角色信息
|
||||||
characters.ForEach(c => Controller.WriteLine($"角色编号:{c.Id}\r\n{c.GetInfo()}"));
|
foreach (Character c in characters)
|
||||||
|
{
|
||||||
|
await Controller.WriteLine($"角色编号:{c.Id}\r\n{c.GetInfo()}");
|
||||||
|
}
|
||||||
|
|
||||||
// 询问玩家需要选择哪个角色 (通过UI界面选择)
|
// 询问玩家需要选择哪个角色 (通过UI界面选择)
|
||||||
Character? player = null;
|
Character? player = null;
|
||||||
@ -73,7 +76,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
player = characters.FirstOrDefault(c => c.Id == selectedPlayerId);
|
player = characters.FirstOrDefault(c => c.Id == selectedPlayerId);
|
||||||
if (player != null)
|
if (player != null)
|
||||||
{
|
{
|
||||||
Controller.WriteLine($"选择了 [ {player} ]!");
|
await Controller.WriteLine($"选择了 [ {player} ]!");
|
||||||
|
player.Promotion = 200;
|
||||||
Controller.SetCurrentCharacter(player);
|
Controller.SetCurrentCharacter(player);
|
||||||
Controller.SetPlayerCharacter(player);
|
Controller.SetPlayerCharacter(player);
|
||||||
|
|
||||||
@ -137,9 +141,9 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
int qArmor = 5;
|
int qArmor = 5;
|
||||||
int qShoes = 5;
|
int qShoes = 5;
|
||||||
int qAccessory = 5;
|
int qAccessory = 5;
|
||||||
WriteLine($"社区送温暖了,现在随机发放空投!!");
|
await Controller.WriteLine($"社区送温暖了,现在随机发放空投!!");
|
||||||
DropItems(_gamingQueue, qMagicCardPack, qWeapon, qArmor, qShoes, qAccessory);
|
DropItems(_gamingQueue, qMagicCardPack, qWeapon, qArmor, qShoes, qAccessory);
|
||||||
WriteLine("");
|
await Controller.WriteLine("");
|
||||||
if (isWeb) result.Add("=== 空投 ===\r\n" + Msg);
|
if (isWeb) result.Add("=== 空投 ===\r\n" + Msg);
|
||||||
double nextDropItemTime = 40;
|
double nextDropItemTime = 40;
|
||||||
if (qMagicCardPack < 5) qMagicCardPack++;
|
if (qMagicCardPack < 5) qMagicCardPack++;
|
||||||
@ -149,20 +153,23 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
if (qAccessory < 5) qAccessory++;
|
if (qAccessory < 5) qAccessory++;
|
||||||
|
|
||||||
// 显示角色信息
|
// 显示角色信息
|
||||||
characters.ForEach(c => Controller.WriteLine(c.GetInfo()));
|
foreach (Character c in characters)
|
||||||
|
{
|
||||||
|
await Controller.WriteLine(c.GetInfo());
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化队列,准备开始游戏
|
// 初始化队列,准备开始游戏
|
||||||
_gamingQueue.InitActionQueue();
|
_gamingQueue.InitActionQueue();
|
||||||
_gamingQueue.SetCharactersToAIControl(false, characters);
|
_gamingQueue.SetCharactersToAIControl(false, characters);
|
||||||
_gamingQueue.SetCharactersToAIControl(true, player);
|
_gamingQueue.SetCharactersToAIControl(true, player);
|
||||||
_gamingQueue.CustomData.Add("player", player);
|
_gamingQueue.CustomData.Add("player", player);
|
||||||
Controller.WriteLine();
|
await Controller.WriteLine();
|
||||||
|
|
||||||
// 显示初始顺序表
|
// 显示初始顺序表
|
||||||
_gamingQueue.DisplayQueue();
|
_gamingQueue.DisplayQueue();
|
||||||
Controller.WriteLine();
|
await Controller.WriteLine();
|
||||||
|
|
||||||
Controller.WriteLine($"你的角色是 [ {player} ],详细信息:{player.GetInfo()}");
|
await Controller.WriteLine($"你的角色是 [ {player} ],详细信息:{player.GetInfo()}");
|
||||||
|
|
||||||
// 总回合数
|
// 总回合数
|
||||||
int maxRound = 999;
|
int maxRound = 999;
|
||||||
@ -180,6 +187,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
effects.Add(effectID, isActive);
|
effects.Add(effectID, isActive);
|
||||||
}
|
}
|
||||||
_gamingQueue.InitRoundRewards(maxRound, 1, effects, id => FunGameConstant.RoundRewards[(EffectID)id]);
|
_gamingQueue.InitRoundRewards(maxRound, 1, effects, id => FunGameConstant.RoundRewards[(EffectID)id]);
|
||||||
|
Controller.SetTurnRewards(_gamingQueue.RoundRewards);
|
||||||
|
|
||||||
int i = 1;
|
int i = 1;
|
||||||
while (i < maxRound)
|
while (i < maxRound)
|
||||||
@ -187,7 +195,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
Msg = "";
|
Msg = "";
|
||||||
if (i == maxRound - 1)
|
if (i == maxRound - 1)
|
||||||
{
|
{
|
||||||
WriteLine($"=== 终局审判 ===");
|
await Controller.WriteLine($"=== 终局审判 ===");
|
||||||
Dictionary<Character, double> hpPercentage = [];
|
Dictionary<Character, double> hpPercentage = [];
|
||||||
foreach (Character c in characters)
|
foreach (Character c in characters)
|
||||||
{
|
{
|
||||||
@ -195,10 +203,10 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
}
|
}
|
||||||
double max = hpPercentage.Values.Max();
|
double max = hpPercentage.Values.Max();
|
||||||
Character winner = hpPercentage.Keys.Where(c => hpPercentage[c] == max).First();
|
Character winner = hpPercentage.Keys.Where(c => hpPercentage[c] == max).First();
|
||||||
WriteLine("[ " + winner + " ] 成为了天选之人!!");
|
await Controller.WriteLine("[ " + winner + " ] 成为了天选之人!!");
|
||||||
foreach (Character c in characters.Where(c => c != winner && c.HP > 0))
|
foreach (Character c in characters.Where(c => c != winner && c.HP > 0))
|
||||||
{
|
{
|
||||||
WriteLine("[ " + winner + " ] 对 [ " + c + " ] 造成了 99999999999 点真实伤害。");
|
await Controller.WriteLine("[ " + winner + " ] 对 [ " + c + " ] 造成了 99999999999 点真实伤害。");
|
||||||
await _gamingQueue.DeathCalculationAsync(winner, c);
|
await _gamingQueue.DeathCalculationAsync(winner, c);
|
||||||
}
|
}
|
||||||
if (_gamingQueue is MixGamingQueue mix)
|
if (_gamingQueue is MixGamingQueue mix)
|
||||||
@ -217,8 +225,9 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
// 处理回合
|
// 处理回合
|
||||||
if (characterToAct != null)
|
if (characterToAct != null)
|
||||||
{
|
{
|
||||||
WriteLine($"=== 回合 {i++} ===");
|
Controller.SetCurrentRound(i);
|
||||||
WriteLine("现在是 [ " + characterToAct + " ] 的回合!");
|
await Controller.WriteLine($"=== 回合 {i++} ===");
|
||||||
|
await Controller.WriteLine("现在是 [ " + characterToAct + " ] 的回合!");
|
||||||
|
|
||||||
bool isGameEnd = await _gamingQueue.ProcessTurnAsync(characterToAct);
|
bool isGameEnd = await _gamingQueue.ProcessTurnAsync(characterToAct);
|
||||||
|
|
||||||
@ -229,7 +238,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isWeb) _gamingQueue.DisplayQueue();
|
if (isWeb) _gamingQueue.DisplayQueue();
|
||||||
WriteLine("");
|
await Controller.WriteLine("");
|
||||||
}
|
}
|
||||||
|
|
||||||
string roundMsg = "";
|
string roundMsg = "";
|
||||||
@ -243,6 +252,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
double timeLapse = await _gamingQueue.TimeLapse();
|
double timeLapse = await _gamingQueue.TimeLapse();
|
||||||
totalTime += timeLapse;
|
totalTime += timeLapse;
|
||||||
nextDropItemTime -= timeLapse;
|
nextDropItemTime -= timeLapse;
|
||||||
|
Controller.UpdateBottomInfoPanel();
|
||||||
Controller.UpdateQueue();
|
Controller.UpdateQueue();
|
||||||
Controller.UpdateCharacterPositionsOnMap();
|
Controller.UpdateCharacterPositionsOnMap();
|
||||||
|
|
||||||
@ -259,9 +269,9 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
{
|
{
|
||||||
// 空投
|
// 空投
|
||||||
Msg = "";
|
Msg = "";
|
||||||
WriteLine($"社区送温暖了,现在随机发放空投!!");
|
await Controller.WriteLine($"社区送温暖了,现在随机发放空投!!");
|
||||||
DropItems(_gamingQueue, qMagicCardPack, qWeapon, qArmor, qShoes, qAccessory);
|
DropItems(_gamingQueue, qMagicCardPack, qWeapon, qArmor, qShoes, qAccessory);
|
||||||
WriteLine("");
|
await Controller.WriteLine("");
|
||||||
if (isWeb) result.Add("=== 空投 ===\r\n" + Msg);
|
if (isWeb) result.Add("=== 空投 ===\r\n" + Msg);
|
||||||
nextDropItemTime = 40;
|
nextDropItemTime = 40;
|
||||||
if (qMagicCardPack < 5) qMagicCardPack++;
|
if (qMagicCardPack < 5) qMagicCardPack++;
|
||||||
@ -272,8 +282,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.WriteLine("--- 游戏结束 ---");
|
await Controller.WriteLine("--- 游戏结束 ---");
|
||||||
Controller.WriteLine($"总游戏时长:{totalTime:0.##} {_gamingQueue.GameplayEquilibriumConstant.InGameTime}");
|
await Controller.WriteLine($"总游戏时长:{totalTime:0.##} {_gamingQueue.GameplayEquilibriumConstant.InGameTime}");
|
||||||
|
|
||||||
// 赛后统计
|
// 赛后统计
|
||||||
FunGameService.GetCharacterRating(_gamingQueue.CharacterStatistics, false, []);
|
FunGameService.GetCharacterRating(_gamingQueue.CharacterStatistics, false, []);
|
||||||
@ -299,18 +309,18 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
int count = 1;
|
int count = 1;
|
||||||
if (isWeb)
|
if (isWeb)
|
||||||
{
|
{
|
||||||
WriteLine("=== 技术得分排行榜 ===");
|
await Controller.WriteLine("=== 技术得分排行榜 ===");
|
||||||
Msg = $"=== 技术得分排行榜 TOP{top} ===\r\n";
|
Msg = $"=== 技术得分排行榜 TOP{top} ===\r\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder ratingBuilder = new();
|
StringBuilder ratingBuilder = new();
|
||||||
WriteLine("=== 本场比赛最佳角色 ===");
|
await Controller.WriteLine("=== 本场比赛最佳角色 ===");
|
||||||
Msg = $"=== 本场比赛最佳角色 ===\r\n";
|
Msg = $"=== 本场比赛最佳角色 ===\r\n";
|
||||||
WriteLine(mvpBuilder.ToString() + "\r\n\r\n" + ratingBuilder.ToString());
|
await Controller.WriteLine(mvpBuilder.ToString() + "\r\n\r\n" + ratingBuilder.ToString());
|
||||||
|
|
||||||
Controller.WriteLine();
|
await Controller.WriteLine();
|
||||||
Controller.WriteLine("=== 技术得分排行榜 ===");
|
await Controller.WriteLine("=== 技术得分排行榜 ===");
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Character character in _gamingQueue.CharacterStatistics.OrderByDescending(d => d.Value.Rating).Select(d => d.Key))
|
foreach (Character character in _gamingQueue.CharacterStatistics.OrderByDescending(d => d.Value.Rating).Select(d => d.Key))
|
||||||
@ -327,11 +337,11 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
builder.Append($"每秒伤害:{stats.DamagePerSecond:0.##} / 每回合伤害:{stats.DamagePerTurn:0.##}");
|
builder.Append($"每秒伤害:{stats.DamagePerSecond:0.##} / 每回合伤害:{stats.DamagePerTurn:0.##}");
|
||||||
if (count++ <= top)
|
if (count++ <= top)
|
||||||
{
|
{
|
||||||
WriteLine(builder.ToString());
|
await Controller.WriteLine(builder.ToString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Controller.WriteLine(builder.ToString());
|
await Controller.WriteLine(builder.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterStatistics? totalStats = CharacterStatistics.Where(kv => kv.Key.GetName() == character.GetName()).Select(kv => kv.Value).FirstOrDefault();
|
CharacterStatistics? totalStats = CharacterStatistics.Where(kv => kv.Key.GetName() == character.GetName()).Select(kv => kv.Value).FirstOrDefault();
|
||||||
@ -364,7 +374,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Controller.WriteLine(ex.ToString());
|
await Controller.WriteLine(ex.ToString());
|
||||||
return [ex.ToString()];
|
return [ex.ToString()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,26 +390,29 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
|
|
||||||
private async Task GamingQueue_CharacterMove(GamingQueue queue, Character actor, Grid grid)
|
private async Task GamingQueue_CharacterMove(GamingQueue queue, Character actor, Grid grid)
|
||||||
{
|
{
|
||||||
|
Controller.UpdateCharacterPositionsOnMap();
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GamingQueue_QueueUpdated(GamingQueue queue, List<Character> characters, Character character, double hardnessTime, QueueUpdatedReason reason, string msg)
|
private async Task GamingQueue_QueueUpdated(GamingQueue queue, List<Character> characters, Character character, double hardnessTime, QueueUpdatedReason reason, string msg)
|
||||||
{
|
{
|
||||||
|
if (reason != QueueUpdatedReason.Action)
|
||||||
|
{
|
||||||
|
Controller.UpdateQueue();
|
||||||
|
}
|
||||||
if (IsPlayer_OnlyTest(queue, character))
|
if (IsPlayer_OnlyTest(queue, character))
|
||||||
{
|
{
|
||||||
if (reason == QueueUpdatedReason.Action)
|
//if (reason == QueueUpdatedReason.Action)
|
||||||
{
|
//{
|
||||||
queue.SetCharactersToAIControl(false, character);
|
// queue.SetCharactersToAIControl(false, character);
|
||||||
}
|
//}
|
||||||
if (reason == QueueUpdatedReason.PreCastSuperSkill)
|
//if (reason == QueueUpdatedReason.PreCastSuperSkill)
|
||||||
{
|
//{
|
||||||
// 玩家释放爆发技后,需要等待玩家确认
|
// // 玩家释放爆发技后,需要等待玩家确认
|
||||||
await Controller.RequestContinuePrompt("你的下一回合需要选择爆发技目标,知晓请点击继续. . .");
|
// await Controller.RequestContinuePrompt("你的下一回合需要选择爆发技目标,知晓请点击继续. . .");
|
||||||
Controller.ResolveContinuePrompt();
|
// Controller.ResolveContinuePrompt();
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
Controller.UpdateQueue();
|
|
||||||
Controller.UpdateCharacterPositionsOnMap();
|
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,7 +422,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
if (IsPlayer_OnlyTest(queue, character))
|
if (IsPlayer_OnlyTest(queue, character))
|
||||||
{
|
{
|
||||||
// 确保玩家角色在回合开始时取消AI托管,以便玩家可以控制
|
// 确保玩家角色在回合开始时取消AI托管,以便玩家可以控制
|
||||||
queue.SetCharactersToAIControl(cancel: true, character);
|
//queue.SetCharactersToAIControl(cancel: true, character);
|
||||||
}
|
}
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
return true;
|
return true;
|
||||||
@ -428,6 +441,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
List<Character> selectedTargets = await Controller.RequestTargetSelection(
|
List<Character> selectedTargets = await Controller.RequestTargetSelection(
|
||||||
character,
|
character,
|
||||||
potentialTargets,
|
potentialTargets,
|
||||||
|
attack,
|
||||||
attack.CanSelectTargetCount,
|
attack.CanSelectTargetCount,
|
||||||
attack.CanSelectSelf,
|
attack.CanSelectSelf,
|
||||||
attack.CanSelectEnemy,
|
attack.CanSelectEnemy,
|
||||||
@ -461,6 +475,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
List<Character>? selectedTargets = await Controller.RequestTargetSelection(
|
List<Character>? selectedTargets = await Controller.RequestTargetSelection(
|
||||||
caster,
|
caster,
|
||||||
potentialTargets,
|
potentialTargets,
|
||||||
|
skill,
|
||||||
skill.CanSelectTargetCount,
|
skill.CanSelectTargetCount,
|
||||||
skill.CanSelectSelf,
|
skill.CanSelectSelf,
|
||||||
skill.CanSelectEnemy,
|
skill.CanSelectEnemy,
|
||||||
@ -483,6 +498,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
|
|
||||||
private async Task GamingQueue_TurnEnd(GamingQueue queue, Character character)
|
private async Task GamingQueue_TurnEnd(GamingQueue queue, Character character)
|
||||||
{
|
{
|
||||||
|
double ht = queue.HardnessTime[character];
|
||||||
|
Controller.SetPredictCharacter(character.NickName, ht);
|
||||||
Controller.UpdateBottomInfoPanel();
|
Controller.UpdateBottomInfoPanel();
|
||||||
if (IsRoundHasPlayer_OnlyTest(queue, character))
|
if (IsRoundHasPlayer_OnlyTest(queue, character))
|
||||||
{
|
{
|
||||||
@ -490,6 +507,11 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
await Controller.RequestContinuePrompt("你的回合(或与你相关的回合)已结束,请查看本回合日志,然后点击继续. . .");
|
await Controller.RequestContinuePrompt("你的回合(或与你相关的回合)已结束,请查看本回合日志,然后点击继续. . .");
|
||||||
Controller.ResolveContinuePrompt();
|
Controller.ResolveContinuePrompt();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Controller.RequestCountDownContinuePrompt("该角色的回合已结束. . .");
|
||||||
|
Controller.ResolveCountDownContinuePrompt();
|
||||||
|
}
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,7 +529,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
|
|
||||||
private static bool IsPlayer_OnlyTest(GamingQueue queue, Character current)
|
private static bool IsPlayer_OnlyTest(GamingQueue queue, Character current)
|
||||||
{
|
{
|
||||||
return queue.CustomData.TryGetValue("player", out object? value) && value is Character player && player == current;
|
return queue.CustomData.TryGetValue("player", out object? value) && value is Character player && player == current && !queue.IsCharacterInAIControlling(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsRoundHasPlayer_OnlyTest(GamingQueue queue, Character current)
|
private static bool IsRoundHasPlayer_OnlyTest(GamingQueue queue, Character current)
|
||||||
@ -515,10 +537,18 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
return queue.CustomData.TryGetValue("player", out object? value) && value is Character player && (player == current || (current.CharacterState != CharacterState.Casting && queue.LastRound.Targets.Any(c => c == player)));
|
return queue.CustomData.TryGetValue("player", out object? value) && value is Character player && (player == current || (current.CharacterState != CharacterState.Casting && queue.LastRound.Targets.Any(c => c == player)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SetPreCastSuperSkill(Character character, Skill skill)
|
||||||
|
{
|
||||||
|
if (_gamingQueue is GamingQueue queue)
|
||||||
|
{
|
||||||
|
await queue.SetCharacterPreCastSuperSkill(character, skill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void WriteLine(string str)
|
public void WriteLine(string str)
|
||||||
{
|
{
|
||||||
Msg += str + "\r\n";
|
Msg += str + "\r\n";
|
||||||
Controller.WriteLine(str);
|
_ = Controller.WriteLine(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DropItems(GamingQueue queue, int mQuality, int wQuality, int aQuality, int sQuality, int acQuality)
|
public static void DropItems(GamingQueue queue, int mQuality, int wQuality, int aQuality, int sQuality, int acQuality)
|
||||||
@ -561,6 +591,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
Item consumable = consumables[Random.Shared.Next(consumables.Length)].Copy();
|
Item consumable = consumables[Random.Shared.Next(consumables.Length)].Copy();
|
||||||
|
consumable.Character = character;
|
||||||
character.Items.Add(consumable);
|
character.Items.Add(consumable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -137,20 +137,35 @@
|
|||||||
<Border Grid.Column="0" Grid.Row="0" BorderBrush="LightGray" BorderThickness="1" Margin="5" Padding="5">
|
<Border Grid.Column="0" Grid.Row="0" BorderBrush="LightGray" BorderThickness="1" Margin="5" Padding="5">
|
||||||
<StackPanel x:Name="LeftQueuePanel" MinWidth="180" Background="#FFF0F8FF">
|
<StackPanel x:Name="LeftQueuePanel" MinWidth="180" Background="#FFF0F8FF">
|
||||||
<!-- AliceBlue -->
|
<!-- AliceBlue -->
|
||||||
<TextBlock Text="行动顺序表" Margin="0,0,0,10" FontWeight="Bold" FontSize="14"/>
|
<TextBlock x:Name="QueueTitle" Text="行动顺序表" Margin="0,0,0,10" FontWeight="Bold" FontSize="14"/>
|
||||||
|
|
||||||
<!-- 动态内容将在此处添加 -->
|
<!-- 动态内容将在此处添加 -->
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Height="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Height="Auto">
|
||||||
<ItemsControl x:Name="CharacterQueueItemsControl" ItemsSource="{Binding CharacterQueueItems, RelativeSource={RelativeSource AncestorType=UserControl}}">
|
<ItemsControl x:Name="CharacterQueueItemsControl" ItemsSource="{Binding CharacterQueueDisplayItems, RelativeSource={RelativeSource AncestorType=UserControl}}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Border BorderBrush="LightGray" BorderThickness="0,0,0,1" Margin="0,2,0,2" Padding="2">
|
<Border BorderThickness="0,0,0,1" Margin="0,2,0,2" Padding="2">
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Setter Property="BorderBrush" Value="LightGray"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding Character.Promotion}" Value="200">
|
||||||
|
<Setter Property="Background" Value="Bisque"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding Character.Promotion}" Value="1800">
|
||||||
|
<Setter Property="Background" Value="#FFDDA0DD"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<!-- 图标列 -->
|
<!-- 图标列 -->
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<!-- 文本信息列 -->
|
<!-- 文本信息列 -->
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<!-- 新增:回合奖励列 -->
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
@ -158,9 +173,19 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- 角色图标 (用大字代替) -->
|
<!-- 角色图标 (用大字代替) -->
|
||||||
<Border Grid.Column="0" Grid.RowSpan="2" Width="25" Height="25" BorderBrush="DarkGray" BorderThickness="1" CornerRadius="3" Margin="0,0,5,0"
|
<Border Grid.Column="0" Grid.RowSpan="2" Width="25" Height="25" BorderBrush="DarkGray" BorderThickness="1" CornerRadius="3" Margin="0,0,5,0">
|
||||||
Background="#FF6A5ACD">
|
|
||||||
<!-- 默认背景色,可根据角色动态改变 -->
|
<!-- 默认背景色,可根据角色动态改变 -->
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<!-- 默认背景色 -->
|
||||||
|
<Setter Property="Background" Value="#FF6A5ACD"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding Character.Promotion}" Value="1800">
|
||||||
|
<Setter Property="Background" Value="#FF228B22"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
<TextBlock Text="{Binding Character.NickName, Converter={StaticResource FirstCharConverter}}"
|
<TextBlock Text="{Binding Character.NickName, Converter={StaticResource FirstCharConverter}}"
|
||||||
Foreground="White"
|
Foreground="White"
|
||||||
FontSize="10"
|
FontSize="10"
|
||||||
@ -176,6 +201,17 @@
|
|||||||
<!-- ATDelay -->
|
<!-- ATDelay -->
|
||||||
<TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding ATDelay, StringFormat=AT Delay: {0:0.##}}"
|
<TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding ATDelay, StringFormat=AT Delay: {0:0.##}}"
|
||||||
FontSize="11" Foreground="DimGray"/>
|
FontSize="11" Foreground="DimGray"/>
|
||||||
|
|
||||||
|
<!-- 新增:回合奖励显示 -->
|
||||||
|
<StackPanel Grid.Column="2" Grid.RowSpan="2" VerticalAlignment="Center" Margin="5,0,0,0">
|
||||||
|
<!-- 直接绑定到 ViewModel 的 PredictedTurnNumber -->
|
||||||
|
<TextBlock FontSize="9" Foreground="Gray" HorizontalAlignment="Right"
|
||||||
|
Text="{Binding PredictedTurnNumber, StringFormat=回合: {0}}" />
|
||||||
|
|
||||||
|
<!-- 直接绑定到 ViewModel 的 TurnRewardSkillName -->
|
||||||
|
<TextBlock FontSize="10" Foreground="DarkGreen" FontWeight="SemiBold" HorizontalAlignment="Right"
|
||||||
|
Text="{Binding TurnRewardSkillName}" />
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -407,6 +443,19 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
<TextBlock x:Name="CountdownTextBlock"
|
||||||
|
Panel.ZIndex="200"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Margin="15"
|
||||||
|
FontSize="18"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="White"
|
||||||
|
Background="#88000000"
|
||||||
|
Padding="8"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Text="0 秒后继续..." />
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
@ -643,6 +692,7 @@
|
|||||||
<Button x:Name="SkillButton" Content="战技/魔法" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.PreCastSkill}" IsEnabled="False"/>
|
<Button x:Name="SkillButton" Content="战技/魔法" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.PreCastSkill}" IsEnabled="False"/>
|
||||||
<Button x:Name="UseItemButton" Content="使用物品" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.UseItem}" IsEnabled="False"/>
|
<Button x:Name="UseItemButton" Content="使用物品" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.UseItem}" IsEnabled="False"/>
|
||||||
<Button x:Name="EndTurnButton" Content="结束回合" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.EndTurn}" IsEnabled="False"/>
|
<Button x:Name="EndTurnButton" Content="结束回合" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.EndTurn}" IsEnabled="False"/>
|
||||||
|
<Button x:Name="PreCastButton" Content="爆发技插队" Width="100" Height="30" Margin="5" Click="PreCastSkillButton_Click" Tag="" IsEnabled="False"/>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
|
|
||||||
<!-- 新增:数据统计 -->
|
<!-- 新增:数据统计 -->
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Globalization;
|
using System.Collections.Specialized;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Data;
|
|
||||||
using System.Windows.Documents;
|
using System.Windows.Documents;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using System.Xml.Linq;
|
||||||
using Milimoe.FunGame.Core.Api.Utility;
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
using Milimoe.FunGame.Core.Entity;
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Entity;
|
||||||
using Milimoe.FunGame.Core.Library.Common.Addon;
|
using Milimoe.FunGame.Core.Library.Common.Addon;
|
||||||
using Milimoe.FunGame.Core.Library.Constant;
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
using Oshima.FunGame.OshimaServers.Service;
|
using Oshima.FunGame.OshimaServers.Service;
|
||||||
@ -23,125 +25,6 @@ using UserControl = System.Windows.Controls.UserControl;
|
|||||||
|
|
||||||
namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
||||||
{
|
{
|
||||||
public class CharacterQueueItem(Character character, double atDelay)
|
|
||||||
{
|
|
||||||
public Character Character { get; set; } = character;
|
|
||||||
public double ATDelay { get; set; } = atDelay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FirstCharConverter : IValueConverter
|
|
||||||
{
|
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
if (value is string s && s.Length > 0)
|
|
||||||
{
|
|
||||||
return s[0].ToString().ToUpper();
|
|
||||||
}
|
|
||||||
return "?";
|
|
||||||
}
|
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CharacterToStringWithLevelConverter : IValueConverter
|
|
||||||
{
|
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
if (value is Character character)
|
|
||||||
{
|
|
||||||
return character.ToString();
|
|
||||||
}
|
|
||||||
return "[未知角色]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SkillItemFormatterConverter : IValueConverter
|
|
||||||
{
|
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
string name = "";
|
|
||||||
if (value is Skill skill)
|
|
||||||
{
|
|
||||||
Character? character = skill.Character;
|
|
||||||
name = $"【{(skill.IsSuperSkill ? "爆发技" : (skill.IsMagic ? "魔法" : "战技"))}】{skill.Name}";
|
|
||||||
if (skill.CurrentCD > 0)
|
|
||||||
{
|
|
||||||
name += $" - 冷却剩余 {skill.CurrentCD:0.##} 秒";
|
|
||||||
}
|
|
||||||
else if (skill.RealEPCost > 0 && skill.RealMPCost > 0 && character != null && character.EP < skill.RealEPCost && character.MP < skill.RealMPCost)
|
|
||||||
{
|
|
||||||
name += $" - 能量/魔法要求 {skill.RealEPCost:0.##} / {skill.RealMPCost:0.##} 点";
|
|
||||||
}
|
|
||||||
else if (skill.RealEPCost > 0 && character != null && character.EP < skill.RealEPCost)
|
|
||||||
{
|
|
||||||
name += $" - 能量不足,要求 {skill.RealEPCost:0.##} 点";
|
|
||||||
}
|
|
||||||
else if (skill.RealMPCost > 0 && character != null && character.MP < skill.RealMPCost)
|
|
||||||
{
|
|
||||||
name += $" - 魔法不足,要求 {skill.RealMPCost:0.##} 点";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (value is Item item)
|
|
||||||
{
|
|
||||||
name = item.Name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
name = value?.ToString() ?? name;
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 组合转换器:判断技能或物品是否可用。
|
|
||||||
/// 接收 Skill/Item 对象和当前 Character 对象。
|
|
||||||
/// </summary>
|
|
||||||
public class SkillUsabilityConverter : IMultiValueConverter
|
|
||||||
{
|
|
||||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
// values[0] 应该是 Skill 或 Item 对象
|
|
||||||
// values[1] 应该是 CurrentCharacter 对象
|
|
||||||
if (values.Length < 2 || values[1] is not Character character)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values[0] is Skill s)
|
|
||||||
{
|
|
||||||
return s.Level > 0 && s.SkillType != SkillType.Passive && s.Enable && !s.IsInEffect && s.CurrentCD == 0 &&
|
|
||||||
((s.SkillType == SkillType.SuperSkill || s.SkillType == SkillType.Skill) && s.RealEPCost <= character.EP || s.SkillType == SkillType.Magic && s.RealMPCost <= character.MP);
|
|
||||||
}
|
|
||||||
else if (values[0] is Item i)
|
|
||||||
{
|
|
||||||
return i.IsActive && i.Skills.Active != null && i.Enable && i.IsInGameItem &&
|
|
||||||
i.Skills.Active.SkillType == SkillType.Item && i.Skills.Active.Enable && !i.Skills.Active.IsInEffect && i.Skills.Active.CurrentCD == 0 &&
|
|
||||||
i.Skills.Active.RealMPCost <= character.MP && i.Skills.Active.RealEPCost <= character.EP;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GameMapViewer.xaml 的交互逻辑
|
/// GameMapViewer.xaml 的交互逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -159,12 +42,14 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
// 新增:存储UI元素(Border)到Character对象的反向关联
|
// 新增:存储UI元素(Border)到Character对象的反向关联
|
||||||
private readonly Dictionary<Border, Character> _uiElementToCharacter = [];
|
private readonly Dictionary<Border, Character> _uiElementToCharacter = [];
|
||||||
|
|
||||||
// 新增:用于左侧动态队列的ObservableCollection
|
|
||||||
public ObservableCollection<CharacterQueueItem> CharacterQueueItems { get; set; }
|
|
||||||
|
|
||||||
// 新增:用于目标选择UI的ObservableCollection
|
// 新增:用于目标选择UI的ObservableCollection
|
||||||
public ObservableCollection<Character> SelectedTargets { get; set; } = [];
|
public ObservableCollection<Character> SelectedTargets { get; set; } = [];
|
||||||
|
|
||||||
|
// 新增:倒计时相关的字段
|
||||||
|
private readonly DispatcherTimer _countdownTimer;
|
||||||
|
private int _remainingCountdownSeconds;
|
||||||
|
private Action<bool>? _currentContinueCallback;
|
||||||
|
|
||||||
// 回调Action,用于将UI选择结果传递给Controller
|
// 回调Action,用于将UI选择结果传递给Controller
|
||||||
private Action<long>? _resolveCharacterSelection;
|
private Action<long>? _resolveCharacterSelection;
|
||||||
private Action<CharacterActionType>? _resolveActionType;
|
private Action<CharacterActionType>? _resolveActionType;
|
||||||
@ -179,13 +64,21 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
private long _maxTargetsForSelection;
|
private long _maxTargetsForSelection;
|
||||||
private bool _canSelectSelf, _canSelectEnemy, _canSelectTeammate;
|
private bool _canSelectSelf, _canSelectEnemy, _canSelectTeammate;
|
||||||
private bool _isSelectingTargets = false; // 标记当前是否处于目标选择模式
|
private bool _isSelectingTargets = false; // 标记当前是否处于目标选择模式
|
||||||
|
private readonly CharacterQueueItem _selectionPredictCharacter = new(Factory.GetCharacter(), 0); // 选择时进行下轮预测(用于行动顺序表显示)
|
||||||
|
|
||||||
public GameMapViewer()
|
public GameMapViewer()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
CharacterQueueItems = [];
|
CharacterQueueItems = [];
|
||||||
|
_selectionPredictCharacter.Character.Promotion = 1800;
|
||||||
this.DataContext = this; // 将UserControl自身设置为DataContext,以便ItemsControl可以绑定到CharacterQueueItems和SelectedTargets属性
|
this.DataContext = this; // 将UserControl自身设置为DataContext,以便ItemsControl可以绑定到CharacterQueueItems和SelectedTargets属性
|
||||||
|
|
||||||
|
_countdownTimer = new()
|
||||||
|
{
|
||||||
|
Interval = TimeSpan.FromSeconds(1) // 每秒触发一次
|
||||||
|
};
|
||||||
|
_countdownTimer.Tick += CountdownTimer_Tick; // 绑定事件处理器
|
||||||
|
|
||||||
// 初始化 SelectedTargetsItemsControl 的 ItemsSource
|
// 初始化 SelectedTargetsItemsControl 的 ItemsSource
|
||||||
SelectedTargetsItemsControl.ItemsSource = SelectedTargets;
|
SelectedTargetsItemsControl.ItemsSource = SelectedTargets;
|
||||||
|
|
||||||
@ -208,11 +101,47 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
set { SetValue(CurrentGameMapProperty, value); }
|
set { SetValue(CurrentGameMapProperty, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当前回合
|
||||||
|
public static readonly DependencyProperty CurrentRoundProperty =
|
||||||
|
DependencyProperty.Register("CurrentRound", typeof(int), typeof(GameMapViewer),
|
||||||
|
new PropertyMetadata(0, OnCurrentRoundChanged));
|
||||||
|
|
||||||
|
public int CurrentRound
|
||||||
|
{
|
||||||
|
get { return (int)GetValue(CurrentRoundProperty); }
|
||||||
|
set { SetValue(CurrentRoundProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回合奖励
|
||||||
|
public static readonly DependencyProperty TurnRewardsProperty =
|
||||||
|
DependencyProperty.Register("TurnRewards", typeof(Dictionary<int, List<Skill>>), typeof(GameMapViewer),
|
||||||
|
new PropertyMetadata(new Dictionary<int, List<Skill>>(), OnTurnRewardsChanged));
|
||||||
|
|
||||||
|
public Dictionary<int, List<Skill>> TurnRewards
|
||||||
|
{
|
||||||
|
get { return (Dictionary<int, List<Skill>>)GetValue(TurnRewardsProperty); }
|
||||||
|
set { SetValue(TurnRewardsProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
// 新增 CurrentCharacter 依赖属性:用于显示当前玩家角色的信息
|
// 新增 CurrentCharacter 依赖属性:用于显示当前玩家角色的信息
|
||||||
public static readonly DependencyProperty CurrentCharacterProperty =
|
public static readonly DependencyProperty CurrentCharacterProperty =
|
||||||
DependencyProperty.Register("CurrentCharacter", typeof(Character), typeof(GameMapViewer),
|
DependencyProperty.Register("CurrentCharacter", typeof(Character), typeof(GameMapViewer),
|
||||||
new PropertyMetadata(null, OnCurrentCharacterChanged));
|
new PropertyMetadata(null, OnCurrentCharacterChanged));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty CharacterQueueItemsProperty =
|
||||||
|
DependencyProperty.Register("CharacterQueueItems", typeof(ObservableCollection<CharacterQueueItem>), typeof(GameMapViewer),
|
||||||
|
new PropertyMetadata(null, OnCharacterQueueItemsChanged));
|
||||||
|
|
||||||
|
// 用于左侧动态队列的ObservableCollection
|
||||||
|
public ObservableCollection<CharacterQueueItem> CharacterQueueItems
|
||||||
|
{
|
||||||
|
get { return (ObservableCollection<CharacterQueueItem>)GetValue(CharacterQueueItemsProperty); }
|
||||||
|
set { SetValue(CharacterQueueItemsProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于 UI 绑定的 ViewModel 集合
|
||||||
|
public ObservableCollection<CharacterQueueItemViewModel> CharacterQueueDisplayItems { get; } = [];
|
||||||
|
|
||||||
public Character? CurrentCharacter
|
public Character? CurrentCharacter
|
||||||
{
|
{
|
||||||
get { return (Character)GetValue(CurrentCharacterProperty); }
|
get { return (Character)GetValue(CurrentCharacterProperty); }
|
||||||
@ -307,6 +236,51 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
viewer.UpdateCharacterPositionsOnMap();
|
viewer.UpdateCharacterPositionsOnMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentRound
|
||||||
|
private static void OnCurrentRoundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
GameMapViewer viewer = (GameMapViewer)d;
|
||||||
|
viewer.CurrentRoundChanged();
|
||||||
|
viewer.UpdateCharacterQueueDisplayItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnCharacterQueueItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (d is GameMapViewer viewer)
|
||||||
|
{
|
||||||
|
// 解除旧集合的事件订阅
|
||||||
|
if (e.OldValue is ObservableCollection<CharacterQueueItem> oldCollection)
|
||||||
|
{
|
||||||
|
oldCollection.CollectionChanged -= viewer.CharacterQueueItems_CollectionChanged;
|
||||||
|
}
|
||||||
|
// 订阅新集合的事件
|
||||||
|
if (e.NewValue is ObservableCollection<CharacterQueueItem> newCollection)
|
||||||
|
{
|
||||||
|
newCollection.CollectionChanged += viewer.CharacterQueueItems_CollectionChanged;
|
||||||
|
}
|
||||||
|
// 立即更新显示队列
|
||||||
|
viewer.UpdateCharacterQueueDisplayItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当原始队列 CharacterQueueItems 内部发生变化时 (添加、删除、移动、替换)
|
||||||
|
private void CharacterQueueItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// 任何集合内容的变动或排序变动,都需要重新计算 PredictedTurnNumber 和奖励
|
||||||
|
// 因为索引变了,PredictedTurnNumber 就会变,进而可能影响奖励
|
||||||
|
UpdateCharacterQueueDisplayItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TurnRewards
|
||||||
|
private static void OnTurnRewardsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
GameMapViewer viewer = (GameMapViewer)d;
|
||||||
|
foreach (CharacterQueueItemViewModel vm in viewer.CharacterQueueDisplayItems)
|
||||||
|
{
|
||||||
|
vm.UpdateRewardProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 当CurrentCharacter属性改变时,更新底部信息面板
|
// 当CurrentCharacter属性改变时,更新底部信息面板
|
||||||
private static void OnCurrentCharacterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
private static void OnCurrentCharacterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
@ -344,16 +318,16 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
/// 向调试日志文本框添加一条消息。
|
/// 向调试日志文本框添加一条消息。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">要添加的日志消息。</param>
|
/// <param name="message">要添加的日志消息。</param>
|
||||||
public void AppendDebugLog(string message)
|
public async Task AppendDebugLog(string message)
|
||||||
{
|
{
|
||||||
// 检查当前线程是否是UI线程
|
// 检查当前线程是否是UI线程
|
||||||
if (!this.Dispatcher.CheckAccess())
|
if (!this.Dispatcher.CheckAccess())
|
||||||
{
|
{
|
||||||
this.Dispatcher.BeginInvoke(new Action(() => AppendDebugLog(message)));
|
await this.Dispatcher.BeginInvoke(new Action(async () => await AppendDebugLog(message)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxLines = 1000;
|
int maxLines = 300;
|
||||||
|
|
||||||
// 获取 FlowDocument
|
// 获取 FlowDocument
|
||||||
FlowDocument doc = DebugLogRichTextBox.Document;
|
FlowDocument doc = DebugLogRichTextBox.Document;
|
||||||
@ -387,6 +361,11 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
DebugLogScrollViewer.ScrollToEnd();
|
DebugLogScrollViewer.ScrollToEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CurrentRoundChanged()
|
||||||
|
{
|
||||||
|
QueueTitle.Text = $"行动顺序表{(CurrentRound > 0 ? $" - 第 {CurrentRound} 回合" : "")}";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 渲染地图:根据CurrentGameMap对象在Canvas上绘制所有格子
|
/// 渲染地图:根据CurrentGameMap对象在Canvas上绘制所有格子
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -401,7 +380,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
|
|
||||||
if (CurrentGameMap == null)
|
if (CurrentGameMap == null)
|
||||||
{
|
{
|
||||||
AppendDebugLog("地图未加载。");
|
_ = AppendDebugLog("地图未加载。");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,7 +459,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
Border characterBorder = new()
|
Border characterBorder = new()
|
||||||
{
|
{
|
||||||
Style = (Style)this.FindResource("CharacterIconStyle"),
|
Style = (Style)this.FindResource("CharacterIconStyle"),
|
||||||
ToolTip = character.ToStringWithLevel(),
|
ToolTip = character.GetInfo(),
|
||||||
IsHitTestVisible = true // 确保角色图标可以被点击
|
IsHitTestVisible = true // 确保角色图标可以被点击
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -614,6 +593,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
EpProgressBar.Value = character.EP;
|
EpProgressBar.Value = character.EP;
|
||||||
EpProgressBar.Maximum = GameplayEquilibriumConstant.MaxEP;
|
EpProgressBar.Maximum = GameplayEquilibriumConstant.MaxEP;
|
||||||
EpValueTextBlock.Text = $"{character.EP:0.##}/{GameplayEquilibriumConstant.MaxEP}";
|
EpValueTextBlock.Text = $"{character.EP:0.##}/{GameplayEquilibriumConstant.MaxEP}";
|
||||||
|
PreCastButton.IsEnabled = character.CharacterState == CharacterState.Actionable || character.CharacterState == CharacterState.AttackRestricted ||
|
||||||
|
character.CharacterState == CharacterState.Casting || character.HP > 0 || character.EP >= 100;
|
||||||
|
|
||||||
// --- 更新装备槽位 ---
|
// --- 更新装备槽位 ---
|
||||||
EquipSlot equipSlot = character.EquipSlot;
|
EquipSlot equipSlot = character.EquipSlot;
|
||||||
@ -888,7 +869,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
this.CurrentCharacter = this.PlayerCharacter;
|
this.CurrentCharacter = this.PlayerCharacter;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendDebugLog($"选中格子: ID={grid.Id}, 坐标=({grid.X},{grid.Y},{grid.Z})");
|
_ = AppendDebugLog($"选中格子: ID={grid.Id}, 坐标=({grid.X},{grid.Y},{grid.Z})");
|
||||||
e.Handled = true; // 标记事件已处理,防止冒泡到Canvas
|
e.Handled = true; // 标记事件已处理,防止冒泡到Canvas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -922,7 +903,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
}
|
}
|
||||||
UpdateGridInfoPanel(grid);
|
UpdateGridInfoPanel(grid);
|
||||||
}
|
}
|
||||||
AppendDebugLog($"选中角色: {character.ToStringWithLevel()} (通过点击图标)");
|
_ = AppendDebugLog($"选中角色: {character.ToStringWithLevel()} (通过点击图标)");
|
||||||
}
|
}
|
||||||
e.Handled = true; // 阻止事件冒泡到下方的Grid_MouseLeftButtonDown 或 GameMapCanvas_MouseLeftButtonDown
|
e.Handled = true; // 阻止事件冒泡到下方的Grid_MouseLeftButtonDown 或 GameMapCanvas_MouseLeftButtonDown
|
||||||
}
|
}
|
||||||
@ -939,7 +920,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
// 只有当点击事件的原始源是Canvas本身时才处理,这意味着没有点击到任何子元素(如格子或角色图标)
|
// 只有当点击事件的原始源是Canvas本身时才处理,这意味着没有点击到任何子元素(如格子或角色图标)
|
||||||
if (e.OriginalSource == GameMapCanvas)
|
if (e.OriginalSource == GameMapCanvas)
|
||||||
{
|
{
|
||||||
AppendDebugLog("点击了地图空白区域。");
|
_ = AppendDebugLog("点击了地图空白区域。");
|
||||||
// 调用关闭格子信息面板的逻辑,它现在也会重置描述和高亮
|
// 调用关闭格子信息面板的逻辑,它现在也会重置描述和高亮
|
||||||
CloseGridInfoButton_Click(new(), new());
|
CloseGridInfoButton_Click(new(), new());
|
||||||
// 将当前角色设置回玩家角色
|
// 将当前角色设置回玩家角色
|
||||||
@ -999,12 +980,12 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
SetRichTextBoxText(DescriptionRichTextBox, item.ToString());
|
SetRichTextBoxText(DescriptionRichTextBox, item.ToString());
|
||||||
AppendDebugLog($"查看装备: {item.Name}");
|
_ = AppendDebugLog($"查看装备: {item.Name}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetRichTextBoxText(DescriptionRichTextBox, "此槽位未装备物品。");
|
SetRichTextBoxText(DescriptionRichTextBox, "此槽位未装备物品。");
|
||||||
AppendDebugLog("查看空装备槽位。");
|
_ = AppendDebugLog("查看空装备槽位。");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1039,7 +1020,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
clickedBorder.BorderThickness = new Thickness(1.5);
|
clickedBorder.BorderThickness = new Thickness(1.5);
|
||||||
|
|
||||||
SetRichTextBoxText(DescriptionRichTextBox, effect.ToString());
|
SetRichTextBoxText(DescriptionRichTextBox, effect.ToString());
|
||||||
AppendDebugLog($"查看状态: {effect.GetType().Name}");
|
_ = AppendDebugLog($"查看状态: {effect.GetType().Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1081,6 +1062,29 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 预释放爆发技的特殊处理。
|
||||||
|
/// </summary>
|
||||||
|
private async void PreCastSkillButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (PlayerCharacter != null)
|
||||||
|
{
|
||||||
|
Skill? skill = PlayerCharacter.Skills.FirstOrDefault(s => s.IsSuperSkill && s.Enable && s.CurrentCD == 0 && s.RealEPCost <= PlayerCharacter.EP);
|
||||||
|
if (skill != null)
|
||||||
|
{
|
||||||
|
await _controller.SetPreCastSuperSkill(PlayerCharacter, skill);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await AppendDebugLog("当前无法预释放爆发技,因为找不到可用的爆发技。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await AppendDebugLog("找不到角色。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- UI 提示方法 (由 GameMapController 调用) ---
|
// --- UI 提示方法 (由 GameMapController 调用) ---
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1220,15 +1224,30 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
if (sender is Border border)
|
if (sender is Border border)
|
||||||
{
|
{
|
||||||
string details = "";
|
string details = "";
|
||||||
|
double hardnessTime = 0;
|
||||||
|
string name = "";
|
||||||
if (border.Tag is Skill hoveredSkill)
|
if (border.Tag is Skill hoveredSkill)
|
||||||
{
|
{
|
||||||
details = hoveredSkill.ToString();
|
details = hoveredSkill.ToString();
|
||||||
|
if (!hoveredSkill.IsMagic) hardnessTime = hoveredSkill.RealHardnessTime;
|
||||||
|
else hardnessTime = hoveredSkill.RealCastTime;
|
||||||
|
name = hoveredSkill.Character?.NickName ?? "未知角色";
|
||||||
}
|
}
|
||||||
else if (border.Tag is Item hoveredItem)
|
else if (border.Tag is Item hoveredItem)
|
||||||
{
|
{
|
||||||
details = hoveredItem.ToString();
|
details = hoveredItem.ToString();
|
||||||
|
if (hoveredItem.Skills.Active != null)
|
||||||
|
{
|
||||||
|
hardnessTime = hoveredItem.Skills.Active.RealHardnessTime;
|
||||||
|
}
|
||||||
|
if (hardnessTime == 0)
|
||||||
|
{
|
||||||
|
hardnessTime = 5;
|
||||||
|
}
|
||||||
|
name = hoveredItem.Character?.NickName ?? "未知角色";
|
||||||
}
|
}
|
||||||
SetRichTextBoxText(SkillItemDetailsRichTextBox, details);
|
SetRichTextBoxText(SkillItemDetailsRichTextBox, details);
|
||||||
|
SetPredictCharacter(name, hardnessTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1247,6 +1266,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
SkillItemDescription.Text = "";
|
SkillItemDescription.Text = "";
|
||||||
_resolveItemSelection?.Invoke(-1);
|
_resolveItemSelection?.Invoke(-1);
|
||||||
}
|
}
|
||||||
|
CharacterQueueItems.Remove(_selectionPredictCharacter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1274,12 +1294,13 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="actor">发起行动的角色。</param>
|
/// <param name="actor">发起行动的角色。</param>
|
||||||
/// <param name="potentialTargets">所有潜在的可选目标列表。</param>
|
/// <param name="potentialTargets">所有潜在的可选目标列表。</param>
|
||||||
|
/// <param name="skill">请求选择目标的技能。</param>
|
||||||
/// <param name="maxTargets">最大可选目标数量。</param>
|
/// <param name="maxTargets">最大可选目标数量。</param>
|
||||||
/// <param name="canSelectSelf">是否可选择自身。</param>
|
/// <param name="canSelectSelf">是否可选择自身。</param>
|
||||||
/// <param name="canSelectEnemy">是否可选择敌方。</param>
|
/// <param name="canSelectEnemy">是否可选择敌方。</param>
|
||||||
/// <param name="canSelectTeammate">是否可选择友方。</param>
|
/// <param name="canSelectTeammate">是否可选择友方。</param>
|
||||||
/// <param name="callback">选择完成后调用的回调函数。</param>
|
/// <param name="callback">选择完成后调用的回调函数。</param>
|
||||||
public void ShowTargetSelectionUI(Character actor, List<Character> potentialTargets, long maxTargets, bool canSelectSelf, bool canSelectEnemy, bool canSelectTeammate, Action<List<Character>> callback)
|
public void ShowTargetSelectionUI(Character actor, List<Character> potentialTargets, ISkill skill, long maxTargets, bool canSelectSelf, bool canSelectEnemy, bool canSelectTeammate, Action<List<Character>> callback)
|
||||||
{
|
{
|
||||||
_resolveTargetSelection = callback;
|
_resolveTargetSelection = callback;
|
||||||
_actingCharacterForTargetSelection = actor;
|
_actingCharacterForTargetSelection = actor;
|
||||||
@ -1294,6 +1315,11 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
TargetSelectionTitle.Text = $"选择 {actor.NickName} 的目标 (最多 {maxTargets} 个)";
|
TargetSelectionTitle.Text = $"选择 {actor.NickName} 的目标 (最多 {maxTargets} 个)";
|
||||||
TargetSelectionOverlay.Visibility = Visibility.Visible;
|
TargetSelectionOverlay.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
if (!CharacterQueueItems.Contains(_selectionPredictCharacter))
|
||||||
|
{
|
||||||
|
SetPredictCharacter(actor.NickName, skill.RealHardnessTime);
|
||||||
|
}
|
||||||
|
|
||||||
// 更新地图上角色的高亮,以显示潜在目标和已选目标
|
// 更新地图上角色的高亮,以显示潜在目标和已选目标
|
||||||
UpdateCharacterHighlights();
|
UpdateCharacterHighlights();
|
||||||
}
|
}
|
||||||
@ -1307,7 +1333,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
// 检查是否是潜在目标
|
// 检查是否是潜在目标
|
||||||
if (_potentialTargetsForSelection == null || !_potentialTargetsForSelection.Contains(clickedCharacter))
|
if (_potentialTargetsForSelection == null || !_potentialTargetsForSelection.Contains(clickedCharacter))
|
||||||
{
|
{
|
||||||
AppendDebugLog($"无法选择 {clickedCharacter.NickName}:不是潜在目标。");
|
_ = AppendDebugLog($"无法选择 {clickedCharacter.NickName}:不是潜在目标。");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1332,7 +1358,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
|
|
||||||
if (!isValidTarget)
|
if (!isValidTarget)
|
||||||
{
|
{
|
||||||
AppendDebugLog($"无法选择 {clickedCharacter.NickName}:不符合目标选择规则。");
|
_ = AppendDebugLog($"无法选择 {clickedCharacter.NickName}:不符合目标选择规则。");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1344,7 +1370,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AppendDebugLog($"已达到最大目标数量 ({_maxTargetsForSelection})。");
|
_ = AppendDebugLog($"已达到最大目标数量 ({_maxTargetsForSelection})。");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UpdateCharacterHighlights(); // 更新地图上的高亮显示
|
UpdateCharacterHighlights(); // 更新地图上的高亮显示
|
||||||
@ -1416,6 +1442,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
private void CancelTargetSelection_Click(object sender, RoutedEventArgs e)
|
private void CancelTargetSelection_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_resolveTargetSelection?.Invoke([]); // 返回空表示取消
|
_resolveTargetSelection?.Invoke([]); // 返回空表示取消
|
||||||
|
CharacterQueueItems.Remove(_selectionPredictCharacter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1461,6 +1488,45 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
_resolveContinuePrompt = null;
|
_resolveContinuePrompt = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CountdownTimer_Tick(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_remainingCountdownSeconds--;
|
||||||
|
|
||||||
|
if (_remainingCountdownSeconds > 0)
|
||||||
|
{
|
||||||
|
// 更新倒计时文本
|
||||||
|
CountdownTextBlock.Text = $"{_remainingCountdownSeconds} 秒后继续...";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 倒计时结束
|
||||||
|
_countdownTimer.Stop(); // 停止计时器
|
||||||
|
CountdownTextBlock.Visibility = Visibility.Collapsed; // 隐藏倒计时文本
|
||||||
|
|
||||||
|
// 触发继续回调
|
||||||
|
_currentContinueCallback?.Invoke(true);
|
||||||
|
_currentContinueCallback = null; // 清除回调,防止重复触发
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动倒计时,并在倒计时结束后自动触发继续。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="seconds">倒计时秒数。</param>
|
||||||
|
/// <param name="callback">倒计时结束后调用的回调函数。</param>
|
||||||
|
public void StartCountdownForContinue(int seconds, Action<bool> callback)
|
||||||
|
{
|
||||||
|
_remainingCountdownSeconds = seconds;
|
||||||
|
_currentContinueCallback = callback;
|
||||||
|
|
||||||
|
// 显示倒计时文本并设置初始值
|
||||||
|
CountdownTextBlock.Text = $"{_remainingCountdownSeconds} 秒后继续...";
|
||||||
|
CountdownTextBlock.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
// 启动计时器
|
||||||
|
_countdownTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 辅助方法:将 System.Drawing.Color 转换为 System.Windows.Media.SolidColorBrush
|
/// 辅助方法:将 System.Drawing.Color 转换为 System.Windows.Media.SolidColorBrush
|
||||||
/// WPF UI元素使用System.Windows.Media.Brush,而Grid类使用System.Drawing.Color
|
/// WPF UI元素使用System.Windows.Media.Brush,而Grid类使用System.Drawing.Color
|
||||||
@ -1478,7 +1544,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
if (sender is Border clickedBorder && e.OriginalSource == clickedBorder)
|
if (sender is Border clickedBorder && e.OriginalSource == clickedBorder)
|
||||||
{
|
{
|
||||||
ResetDescriptionAndHighlights();
|
ResetDescriptionAndHighlights();
|
||||||
AppendDebugLog("点击了装备/状态区域空白处。");
|
_ = AppendDebugLog("点击了装备/状态区域空白处。");
|
||||||
e.Handled = true; // 标记事件已处理
|
e.Handled = true; // 标记事件已处理
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1500,5 +1566,104 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
|
|||||||
richTextBox.Document.Blocks.Clear();
|
richTextBox.Document.Blocks.Clear();
|
||||||
richTextBox.Document.Blocks.Add(new Paragraph(new Run(text)) { Margin = new Thickness(0) });
|
richTextBox.Document.Blocks.Add(new Paragraph(new Run(text)) { Margin = new Thickness(0) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void InsertSorted<T>(ObservableCollection<T> collection, T item, Func<T, double> keySelector)
|
||||||
|
{
|
||||||
|
// 处理空集合情况
|
||||||
|
if (collection.Count == 0)
|
||||||
|
{
|
||||||
|
collection.Add(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 二分查找插入位置
|
||||||
|
int low = 0;
|
||||||
|
int high = collection.Count - 1;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while (low <= high)
|
||||||
|
{
|
||||||
|
int mid = (low + high) / 2;
|
||||||
|
double midValue = keySelector(collection[mid]);
|
||||||
|
double newValue = keySelector(item);
|
||||||
|
|
||||||
|
if (Math.Abs(midValue - newValue) < double.Epsilon) // 处理浮点精度
|
||||||
|
{
|
||||||
|
index = mid + 1; // 相同值插入后面
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (midValue < newValue)
|
||||||
|
{
|
||||||
|
low = mid + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
high = mid - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (low > high) index = low;
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.Insert(index, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCharacterQueueDisplayItems()
|
||||||
|
{
|
||||||
|
if (CharacterQueueItems == null)
|
||||||
|
{
|
||||||
|
CharacterQueueDisplayItems.Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 创建一个新的 ViewModel 列表,同时尝试重用现有实例
|
||||||
|
List<CharacterQueueItemViewModel> newDisplayItems = [];
|
||||||
|
// 使用字典快速查找现有 ViewModel
|
||||||
|
Dictionary<CharacterQueueItem, CharacterQueueItemViewModel> existingVmMap = CharacterQueueDisplayItems.ToDictionary(vm => vm.Model);
|
||||||
|
|
||||||
|
for (int i = 0; i < CharacterQueueItems.Count; i++)
|
||||||
|
{
|
||||||
|
CharacterQueueItem rawItem = CharacterQueueItems[i];
|
||||||
|
CharacterQueueItemViewModel vm;
|
||||||
|
|
||||||
|
// 尝试从现有 ViewModel 映射中获取
|
||||||
|
if (existingVmMap.TryGetValue(rawItem, out CharacterQueueItemViewModel? existingVm))
|
||||||
|
{
|
||||||
|
vm = existingVm;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果没有,则创建新的 ViewModel
|
||||||
|
vm = new CharacterQueueItemViewModel(rawItem, () => TurnRewards);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 更新 ViewModel 的派生属性
|
||||||
|
// 预测回合数会因为队列顺序或 CurrentRound 变化而变化
|
||||||
|
int predictedTurn = CurrentRound + i;
|
||||||
|
if (vm.PredictedTurnNumber != predictedTurn) // 只有当实际变化时才设置,触发 INPC
|
||||||
|
{
|
||||||
|
vm.PredictedTurnNumber = predictedTurn;
|
||||||
|
}
|
||||||
|
// 奖励信息可能因为 PredictedTurnNumber 变化而变化 (即使 TurnRewards 字典本身不变)
|
||||||
|
vm.UpdateRewardProperties(); // 会在内部检查 TurnRewardSkillName 是否变化并触发 INPC
|
||||||
|
|
||||||
|
newDisplayItems.Add(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 高效同步 CharacterQueueDisplayItems
|
||||||
|
// 这是一个简单的同步策略:清空并重新添加。
|
||||||
|
CharacterQueueDisplayItems.Clear();
|
||||||
|
foreach (CharacterQueueItemViewModel vm in newDisplayItems)
|
||||||
|
{
|
||||||
|
CharacterQueueDisplayItems.Add(vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPredictCharacter(string name, double ht)
|
||||||
|
{
|
||||||
|
CharacterQueueItems.Remove(_selectionPredictCharacter);
|
||||||
|
_selectionPredictCharacter.Character.NickName = $"{name} [ 下轮预测 ]";
|
||||||
|
_selectionPredictCharacter.ATDelay = ht;
|
||||||
|
InsertSorted(CharacterQueueItems, _selectionPredictCharacter, cq => cq.ATDelay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user