UI添加了更多信息,添加团队模式

This commit is contained in:
milimoe 2025-09-04 01:48:58 +08:00
parent 1fc654df06
commit 6a23672af9
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
6 changed files with 818 additions and 329 deletions

View File

@ -1,10 +1,25 @@
using System.Globalization;
using System.Windows.Data;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Entity;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
public class ToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// 如果值为 null返回空字符串否则返回其 ToString() 结果
return value?.ToString()?.Trim() ?? string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class FirstCharConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
@ -111,6 +126,10 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
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 NormalAttack a)
{
return a.Level > 0 && a.Enable;
}
else if (values[0] is Item i)
{
return i.IsActive && i.Skills.Active != null && i.Enable && i.IsInGameItem &&

View File

@ -24,7 +24,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
public async Task Start()
{
_game = new(this);
await _game.StartGame(false);
await _game.StartGame(false, true);
}
public async Task SetPreCastSuperSkill(Character character, Skill skill)
@ -35,16 +35,26 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
}
}
public void SetPredictCharacter(string name, double ht)
public void SetAutoMode(bool cancel, Character character)
{
UI.Invoke(() => UI.SetPredictCharacter(name, ht));
_game?.SetAutoMode(cancel, character);
}
public void SetFastMode(bool on)
{
_game?.SetFastMode(on);
}
public async Task SetPredictCharacter(string name, double ht)
{
await UI.InvokeAsync(() => UI.SetPredictCharacter(name, ht));
}
public async Task<long> RequestCharacterSelection(List<Character> availableCharacters)
{
await WriteLine("请选择你想玩的角色。");
return await _characterSelectionRequester.RequestInput(
(callback) => UI.Invoke(() => UI.ShowCharacterSelectionPrompt(availableCharacters, callback))
async (callback) => await UI.InvokeAsync(() => UI.ShowCharacterSelectionPrompt(availableCharacters, callback))
);
}
@ -52,7 +62,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
await WriteLine($"现在是 {character.NickName} 的回合,请选择行动。");
return await _actionTypeRequester.RequestInput(
(callback) => UI.Invoke(() => UI.ShowActionButtons(character, availableItems, callback))
async (callback) => await UI.InvokeAsync(() => UI.ShowActionButtons(character, availableItems, callback))
);
}
@ -60,17 +70,17 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
await WriteLine($"请为 {character.NickName} 选择目标 (最多 {skill.CanSelectTargetCount} 个)。");
List<Character> targetIds = await _targetSelectionRequester.RequestInput(
(callback) => UI.Invoke(() => UI.ShowTargetSelectionUI(character, skill, enemys, teammates, callback))
async (callback) => await UI.InvokeAsync(() => UI.ShowTargetSelectionUI(character, skill, enemys, teammates, callback))
) ?? [];
if (targetIds == null) return [];
return [.. enemys.Where(targetIds.Contains), .. teammates.Where(targetIds.Contains)];
return [.. enemys.Union(teammates).Union([character]).Where(targetIds.Contains)];
}
public async Task<Skill?> RequestSkillSelection(Character character, List<Skill> availableSkills)
{
await WriteLine($"请为 {character.NickName} 选择一个技能。");
long? skillId = await _skillSelectionRequester.RequestInput(
(callback) => UI.Invoke(() => UI.ShowSkillSelectionUI(character, callback))
async (callback) => await UI.InvokeAsync(() => UI.ShowSkillSelectionUI(character, callback))
);
return skillId.HasValue ? availableSkills.FirstOrDefault(s => s.Id == skillId.Value) : null;
}
@ -79,7 +89,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
await WriteLine($"请为 {character.NickName} 选择一个物品。");
long? itemId = await _itemSelectionRequester.RequestInput(
(callback) => UI.Invoke(() => UI.ShowItemSelectionUI(character, callback))
async (callback) => await UI.InvokeAsync(() => UI.ShowItemSelectionUI(character, callback))
);
return itemId.HasValue ? availableItems.FirstOrDefault(i => i.Id == itemId.Value) : null;
}
@ -88,7 +98,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
await WriteLine(message);
await _continuePromptRequester.RequestInput(
(callback) => UI.Invoke(() => UI.ShowContinuePrompt(message, callback))
async (callback) => await UI.InvokeAsync(() => UI.ShowContinuePrompt(message, callback))
);
}
@ -97,52 +107,52 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
await WriteLine(message);
// 调用 _continuePromptRequester 的 RequestInput 方法,它会等待回调被触发
await _continuePromptRequester.RequestInput(
(callback) => UI.Invoke(() => UI.StartCountdownForContinue(countdownSeconds, callback))
async (callback) => await UI.InvokeAsync(() => UI.StartCountdownForContinue(countdownSeconds, callback))
);
}
// --- GameMapViewer 调用这些方法来解决 UI 输入 ---
public void ResolveCharacterSelection(long characterId)
public async Task ResolveCharacterSelection(long characterId)
{
_characterSelectionRequester.ResolveInput(characterId);
UI.Invoke(() => UI.HideCharacterSelectionPrompt());
await UI.InvokeAsync(() => UI.HideCharacterSelectionPrompt());
}
public void ResolveActionType(CharacterActionType actionType)
public async Task ResolveActionType(CharacterActionType actionType)
{
_actionTypeRequester.ResolveInput(actionType);
UI.Invoke(() => UI.HideActionButtons());
await UI.InvokeAsync(() => UI.HideActionButtons());
}
public void ResolveTargetSelection(List<Character> targetIds)
public async Task ResolveTargetSelection(List<Character> targetIds)
{
_targetSelectionRequester.ResolveInput(targetIds);
UI.Invoke(() => UI.HideTargetSelectionUI());
await UI.InvokeAsync(() => UI.HideTargetSelectionUI());
}
public void ResolveSkillSelection(long skillId)
public async Task ResolveSkillSelection(long skillId)
{
_skillSelectionRequester.ResolveInput(skillId);
UI.Invoke(() => UI.HideSkillSelectionUI());
await UI.InvokeAsync(() => UI.HideSkillSelectionUI());
}
public void ResolveItemSelection(long itemId)
public async Task ResolveItemSelection(long itemId)
{
_itemSelectionRequester.ResolveInput(itemId);
UI.Invoke(() => UI.HideItemSelectionUI());
await UI.InvokeAsync(() => UI.HideItemSelectionUI());
}
public void ResolveContinuePrompt()
public async Task ResolveContinuePrompt()
{
_continuePromptRequester.ResolveInput(true); // 任何值都可以只要完成Task
UI.Invoke(() => UI.HideContinuePrompt());
await UI.InvokeAsync(() => UI.HideContinuePrompt());
}
public void ResolveCountDownContinuePrompt()
public async Task ResolveCountDownContinuePrompt()
{
_continuePromptRequester.ResolveInput(true);
UI.Invoke(() => UI.HideContinuePrompt());
await UI.InvokeAsync(() => UI.HideContinuePrompt());
}
public bool IsTeammate(Character actor, Character target)
@ -157,70 +167,70 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
public async Task UpdateBottomInfoPanel()
{
await UI.Invoke(UI.UpdateBottomInfoPanel);
await UI.InvokeAsync(UI.UpdateBottomInfoPanel);
}
public async Task UpdateQueue()
{
await UI.Invoke(UI.UpdateLeftQueuePanel);
await UI.InvokeAsync(UI.UpdateLeftQueuePanelGrid);
}
public async Task UpdateCharacterPositionsOnMap()
{
await UI.Invoke(UI.UpdateCharacterPositionsOnMap);
await UI.InvokeAsync(UI.UpdateCharacterPositionsOnMap);
}
public void SetQueue(Dictionary<Character, double> dict)
public async Task SetQueue(Dictionary<Character, double> dict)
{
UI.Invoke(() =>
await UI.InvokeAsync(() =>
{
UI.CharacterQueueData = dict;
});
}
public void SetGameMap(GameMap map)
public async Task SetGameMap(GameMap map)
{
UI.Invoke(() =>
await UI.InvokeAsync(() =>
{
UI.CurrentGameMap = map;
});
}
public void SetCurrentRound(int round)
public async Task SetCurrentRound(int round)
{
UI.Invoke(() =>
await UI.InvokeAsync(() =>
{
UI.CurrentRound = round;
});
}
public void SetTurnRewards(Dictionary<int, List<Skill>> rewards)
public async Task SetTurnRewards(Dictionary<int, List<Skill>> rewards)
{
UI.Invoke(() =>
await UI.InvokeAsync(() =>
{
UI.TurnRewards = rewards;
});
}
public void SetPlayerCharacter(Character character)
public async Task SetPlayerCharacter(Character character)
{
UI.Invoke(() =>
await UI.InvokeAsync(() =>
{
UI.PlayerCharacter = character;
});
}
public void SetCurrentCharacter(Character character)
public async Task SetCurrentCharacter(Character character)
{
UI.Invoke(() =>
await UI.InvokeAsync(() =>
{
UI.CurrentCharacter = character;
});
}
public void SetCharacterStatistics(Dictionary<Character, CharacterStatistics> stats)
public async Task SetCharacterStatistics(Dictionary<Character, CharacterStatistics> stats)
{
UI.Invoke(() =>
await UI.InvokeAsync(() =>
{
UI.CharacterStatistics = stats;
});

View File

@ -1,5 +1,4 @@
using System.Text;
// using System.Windows; // 不再需要,因为移除了 InputDialog
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Addon;
@ -18,12 +17,16 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
public GamingQueue? GamingQueue => _gamingQueue;
public GameMapController Controller { get; }
public Dictionary<Character, CharacterStatistics> CharacterStatistics { get; } = [];
public PluginConfig StatsConfig { get; } = new(nameof(FunGameSimulation), nameof(CharacterStatistics));
public static Dictionary<Character, CharacterStatistics> TeamCharacterStatistics { get; } = [];
public PluginConfig StatsConfig { get; } = new(nameof(GameMapTesting), nameof(CharacterStatistics));
public PluginConfig TeamStatsConfig { get; } = new(nameof(GameMapTesting), nameof(TeamCharacterStatistics));
public GameMap GameMap { get; } = new TestMap();
public bool IsWeb { get; set; } = false;
public bool TeamMode { get; set; } = false;
public string Msg { get; set; } = "";
private GamingQueue? _gamingQueue = null;
private bool _fastMode = false;
public GameMapTesting(GameMapController controller)
{
@ -31,15 +34,18 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
InitCharacter(); // 初始化角色统计数据
}
public async Task<List<string>> StartGame(bool isWeb = false)
public async Task<List<string>> StartGame(bool isWeb = false, bool isTeam = false)
{
IsWeb = isWeb;
TeamMode = isTeam;
try
{
List<string> result = [];
Msg = "";
List<Character> allCharactersInGame = [.. FunGameConstant.Characters]; // 使用不同的名称以避免与后面的 `characters` 冲突
List<Character> allCharactersInGame = [.. FunGameConstant.Characters];
await Controller.WriteLine("--- 游戏开始 ---");
TeamGamingQueue? tgq = null;
MixGamingQueue? mgq = null;
int clevel = 60;
int slevel = 6;
@ -70,7 +76,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
// 询问玩家需要选择哪个角色 (通过UI界面选择)
Character? player = null;
long selectedPlayerId = await Controller.RequestCharacterSelection(characters); // 异步等待UI选择
Controller.ResolveCharacterSelection(selectedPlayerId);
await Controller.ResolveCharacterSelection(selectedPlayerId);
if (selectedPlayerId != -1)
{
player = characters.FirstOrDefault(c => c.Id == selectedPlayerId);
@ -78,9 +84,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
await Controller.WriteLine($"选择了 [ {player} ]");
player.Promotion = 200;
Controller.SetCurrentCharacter(player);
Controller.SetPlayerCharacter(player);
await Controller.SetCurrentCharacter(player);
await Controller.SetPlayerCharacter(player);
}
}
@ -90,10 +95,64 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
}
// 创建顺序表并排序
_gamingQueue = new MixGamingQueue(characters, WriteLine)
if (isTeam)
{
GameplayEquilibriumConstant = OshimaGameModuleConstant.GameplayEquilibriumConstant
};
tgq = new(characters, WriteLine)
{
GameplayEquilibriumConstant = OshimaGameModuleConstant.GameplayEquilibriumConstant
};
_gamingQueue = tgq;
await Controller.WriteLine("=== 团队模式随机分组 ===");
// 打乱角色列表
List<Character> shuffledCharacters = [.. characters.OrderBy(c => Random.Shared.Next())];
// 创建两个团队
List<Character> group1 = [];
List<Character> group2 = [];
// 将角色交替分配到两个团队中
for (int cid = 0; cid < shuffledCharacters.Count; cid++)
{
Character thisCharacter = shuffledCharacters[cid];
if (cid % 2 == 0)
{
group1.Add(thisCharacter);
}
else
{
group2.Add(thisCharacter);
}
}
// 添加到团队字典,第一个为队长
tgq.AddTeam($"{group1.First().NickName}的小队", group1);
tgq.AddTeam($"{group2.First().NickName}的小队", group2);
foreach (Team team in tgq.Teams.Values)
{
await Controller.WriteLine($"团队【{team.Name}】的成员:\r\n{string.Join("\r\n", team.Members)}\r\n");
if (team.IsOnThisTeam(player))
{
foreach (Character c in team.Members.Except([player]))
{
c.Promotion = 300;
}
}
else
{
team.Members.ForEach(c => c.Promotion = 400);
}
}
}
else
{
mgq = new MixGamingQueue(characters, WriteLine)
{
GameplayEquilibriumConstant = OshimaGameModuleConstant.GameplayEquilibriumConstant
};
_gamingQueue = mgq;
}
// 加载地图和绑定事件
_gamingQueue.LoadGameMap(GameMap);
@ -114,14 +173,14 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
allocated.Add(grid);
map.SetCharacterCurrentGrid(character, grid);
}
Controller.SetGameMap(map);
await Controller.SetGameMap(map);
_gamingQueue.SelectTargetGrid += GamingQueue_SelectTargetGrid;
_gamingQueue.CharacterMove += GamingQueue_CharacterMove;
}
// 绑定事件
Controller.SetQueue(_gamingQueue.HardnessTime);
Controller.SetCharacterStatistics(_gamingQueue.CharacterStatistics);
await Controller.SetQueue(_gamingQueue.HardnessTime);
await Controller.SetCharacterStatistics(_gamingQueue.CharacterStatistics);
_gamingQueue.TurnStart += GamingQueue_TurnStart;
_gamingQueue.DecideAction += GamingQueue_DecideAction;
_gamingQueue.SelectNormalAttackTargets += GamingQueue_SelectNormalAttackTargets;
@ -172,7 +231,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
await Controller.WriteLine($"你的角色是 [ {player} ],详细信息:{player.GetInfo()}");
// 总回合数
int maxRound = 999;
int maxRound = isTeam ? 9999 : 999;
// 随机回合奖励
Dictionary<long, bool> effects = [];
@ -187,7 +246,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
effects.Add(effectID, isActive);
}
_gamingQueue.InitRoundRewards(maxRound, 1, effects, id => FunGameConstant.RoundRewards[(EffectID)id]);
Controller.SetTurnRewards(_gamingQueue.RoundRewards);
await Controller.SetTurnRewards(_gamingQueue.RoundRewards);
int i = 1;
while (i < maxRound)
@ -195,23 +254,31 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
Msg = "";
if (i == maxRound - 1)
{
await Controller.WriteLine($"=== 终局审判 ===");
Dictionary<Character, double> hpPercentage = [];
foreach (Character c in characters)
if (isTeam)
{
hpPercentage.TryAdd(c, c.HP / c.MaxHP);
WriteLine("两队打到天昏地暗,流局了!!");
break;
}
double max = hpPercentage.Values.Max();
Character winner = hpPercentage.Keys.Where(c => hpPercentage[c] == max).First();
await Controller.WriteLine("[ " + winner + " ] 成为了天选之人!!");
foreach (Character c in characters.Where(c => c != winner && c.HP > 0))
else
{
await Controller.WriteLine("[ " + winner + " ] 对 [ " + c + " ] 造成了 99999999999 点真实伤害。");
await _gamingQueue.DeathCalculationAsync(winner, c);
}
if (_gamingQueue is MixGamingQueue mix)
{
await mix.EndGameInfo(winner);
await Controller.WriteLine($"=== 终局审判 ===");
Dictionary<Character, double> hpPercentage = [];
foreach (Character c in characters)
{
hpPercentage.TryAdd(c, c.HP / c.MaxHP);
}
double max = hpPercentage.Values.Max();
Character winner = hpPercentage.Keys.Where(c => hpPercentage[c] == max).First();
await Controller.WriteLine("[ " + winner + " ] 成为了天选之人!!");
foreach (Character c in characters.Where(c => c != winner && c.HP > 0))
{
await Controller.WriteLine("[ " + winner + " ] 对 [ " + c + " ] 造成了 99999999999 点真实伤害。");
await _gamingQueue.DeathCalculationAsync(winner, c);
}
if (mgq != null)
{
await mgq.EndGameInfo(winner);
}
}
result.Add(Msg);
break;
@ -225,7 +292,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
// 处理回合
if (characterToAct != null)
{
Controller.SetCurrentRound(i);
await Controller.SetCurrentRound(i);
await Controller.WriteLine($"=== 回合 {i++} ===");
await Controller.WriteLine("现在是 [ " + characterToAct + " ] 的回合!");
@ -286,7 +353,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
await Controller.WriteLine($"总游戏时长:{totalTime:0.##} {_gamingQueue.GameplayEquilibriumConstant.InGameTime}");
// 赛后统计
FunGameService.GetCharacterRating(_gamingQueue.CharacterStatistics, false, []);
FunGameService.GetCharacterRating(_gamingQueue.CharacterStatistics, isTeam, tgq != null ? tgq.EliminatedTeams : []);
// 统计技术得分,评选 MVP
Character? mvp = _gamingQueue.CharacterStatistics.OrderByDescending(d => d.Value.Rating).Select(d => d.Key).FirstOrDefault();
@ -295,7 +362,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
CharacterStatistics stats = _gamingQueue.CharacterStatistics[mvp];
stats.MVPs++;
mvpBuilder.AppendLine($"[ {mvp.ToStringWithLevel()} ]");
mvpBuilder.AppendLine($"{(tgq != null ? "[ " + tgq.GetTeamFromEliminated(mvp)?.Name + " ] " : "")}[ {mvp.ToStringWithLevel()} ]");
mvpBuilder.AppendLine($"技术得分:{stats.Rating:0.0#} / 击杀数:{stats.Kills} / 助攻数:{stats.Assists}{(_gamingQueue.MaxRespawnTimes != 0 ? " / " + stats.Deaths : "")}");
mvpBuilder.AppendLine($"存活时长:{stats.LiveTime:0.##} / 存活回合数:{stats.LiveRound} / 行动回合数:{stats.ActionTurn}");
mvpBuilder.AppendLine($"控制时长:{stats.ControlTime:0.##} / 总计治疗:{stats.TotalHeal:0.##} / 护盾抵消:{stats.TotalShield:0.##}");
@ -327,7 +394,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
StringBuilder builder = new();
CharacterStatistics stats = _gamingQueue.CharacterStatistics[character];
builder.AppendLine($"{(isWeb ? count + ". " : "")}[ {character.ToStringWithLevel()} ]");
builder.AppendLine($"{(isWeb ? count + ". " : "")}[ {character.ToStringWithLevel()}{(tgq != null ? "" + tgq.GetTeamFromEliminated(character)?.Name + "" : "")} ]");
builder.AppendLine($"技术得分:{stats.Rating:0.0#} / 击杀数:{stats.Kills} / 助攻数:{stats.Assists}{(_gamingQueue.MaxRespawnTimes != 0 ? " / " + stats.Deaths : "")}");
builder.AppendLine($"存活时长:{stats.LiveTime:0.##} / 存活回合数:{stats.LiveRound} / 行动回合数:{stats.ActionTurn}");
builder.AppendLine($"控制时长:{stats.ControlTime:0.##} / 总计治疗:{stats.TotalHeal:0.##} / 护盾抵消:{stats.TotalShield:0.##}");
@ -344,7 +411,9 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
await Controller.WriteLine(builder.ToString());
}
CharacterStatistics? totalStats = CharacterStatistics.Where(kv => kv.Key.GetName() == character.GetName()).Select(kv => kv.Value).FirstOrDefault();
CharacterStatistics? totalStats = isTeam ?
TeamCharacterStatistics.Where(kv => kv.Key.GetName() == character.GetName()).Select(kv => kv.Value).FirstOrDefault() :
CharacterStatistics.Where(kv => kv.Key.GetName() == character.GetName()).Select(kv => kv.Value).FirstOrDefault();
if (totalStats != null)
{
UpdateStatistics(totalStats, stats);
@ -361,13 +430,27 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
}
}
lock (StatsConfig)
if (isTeam)
{
foreach (Character c in CharacterStatistics.Keys)
lock (TeamStatsConfig)
{
StatsConfig.Add(c.ToStringWithOutUser(), CharacterStatistics[c]);
foreach (Character c in TeamCharacterStatistics.Keys)
{
TeamStatsConfig.Add(c.ToStringWithOutUser(), TeamCharacterStatistics[c]);
}
TeamStatsConfig.SaveConfig();
}
}
else
{
lock (StatsConfig)
{
foreach (Character c in CharacterStatistics.Keys)
{
StatsConfig.Add(c.ToStringWithOutUser(), CharacterStatistics[c]);
}
StatsConfig.SaveConfig();
}
StatsConfig.SaveConfig();
}
return result;
@ -400,31 +483,11 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
await Controller.UpdateQueue();
}
if (IsPlayer_OnlyTest(queue, character))
{
//if (reason == QueueUpdatedReason.Action)
//{
// queue.SetCharactersToAIControl(false, character);
//}
//if (reason == QueueUpdatedReason.PreCastSuperSkill)
//{
// // 玩家释放爆发技后,需要等待玩家确认
// await Controller.RequestContinuePrompt("你的下一回合需要选择爆发技目标,知晓请点击继续. . .");
// Controller.ResolveContinuePrompt();
//}
}
await Task.CompletedTask;
}
private async Task<bool> GamingQueue_TurnStart(GamingQueue queue, Character character, List<Character> enemys, List<Character> teammates, List<Skill> skills, List<Item> items)
{
await Controller.UpdateBottomInfoPanel();
if (IsPlayer_OnlyTest(queue, character))
{
// 确保玩家角色在回合开始时取消AI托管以便玩家可以控制
//queue.SetCharactersToAIControl(cancel: true, character);
}
await Task.CompletedTask;
return true;
}
@ -439,7 +502,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
enemys,
teammates
);
Controller.ResolveTargetSelection(selectedTargets);
await Controller.ResolveTargetSelection(selectedTargets);
return selectedTargets ?? []; // 如果取消,返回空列表
}
@ -450,7 +513,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
// 通过UI请求物品选择
Item? selectedItem = await Controller.RequestItemSelection(character, items);
Controller.ResolveItemSelection(selectedItem?.Id ?? 0);
await Controller.ResolveItemSelection(selectedItem?.Id ?? 0);
return selectedItem;
}
@ -470,7 +533,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
enemys,
teammates
);
Controller.ResolveTargetSelection(selectedTargets);
await Controller.ResolveTargetSelection(selectedTargets);
return selectedTargets ?? []; // 如果取消,返回空列表
}
@ -481,27 +544,30 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
// 通过UI请求技能选择
Skill? selectedSkill = await Controller.RequestSkillSelection(character, skills);
Controller.ResolveSkillSelection(selectedSkill?.Id ?? 0);
await Controller.ResolveSkillSelection(selectedSkill?.Id ?? 0);
return selectedSkill;
}
private async Task GamingQueue_TurnEnd(GamingQueue queue, Character character)
{
double ht = queue.HardnessTime[character];
Controller.SetPredictCharacter(character.NickName, ht);
await Controller.SetPredictCharacter(character.NickName, ht);
await Controller.UpdateBottomInfoPanel();
if (IsRoundHasPlayer_OnlyTest(queue, character))
if (!_fastMode)
{
// 玩家回合结束,等待玩家确认
await Controller.RequestContinuePrompt("你的回合(或与你相关的回合)已结束,请查看本回合日志,然后点击继续. . .");
Controller.ResolveContinuePrompt();
if (IsRoundHasPlayer_OnlyTest(queue, character))
{
// 玩家回合结束,等待玩家确认
await Controller.RequestContinuePrompt("你的回合(或与你相关的回合)已结束,请查看本回合日志,然后点击继续. . .");
await Controller.ResolveContinuePrompt();
}
else
{
await Controller.RequestCountDownContinuePrompt("该角色的回合已结束. . .");
await Controller.ResolveCountDownContinuePrompt();
}
}
else
{
await Controller.RequestCountDownContinuePrompt("该角色的回合已结束. . .");
Controller.ResolveCountDownContinuePrompt();
}
await Task.CompletedTask;
else await Task.Delay(100);
}
private async Task<CharacterActionType> GamingQueue_DecideAction(GamingQueue queue, Character character, List<Character> enemys, List<Character> teammates, List<Skill> skills, List<Item> items)
@ -510,7 +576,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
// 通过UI按钮请求行动类型
CharacterActionType actionType = await Controller.RequestActionType(character, items);
Controller.ResolveActionType(actionType);
await Controller.ResolveActionType(actionType);
return actionType;
}
return CharacterActionType.None; // 非玩家角色由AI处理或默认None
@ -534,6 +600,16 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
}
}
public void SetAutoMode(bool cancel, Character character)
{
_gamingQueue?.SetCharactersToAIControl(cancel, character);
}
public void SetFastMode(bool on)
{
_fastMode = on;
}
public void WriteLine(string str)
{
Msg += str + "\r\n";
@ -592,9 +668,11 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
foreach (Character c in FunGameConstant.Characters)
{
CharacterStatistics.Add(c, new());
TeamCharacterStatistics.Add(c, new());
}
StatsConfig.LoadConfig();
TeamStatsConfig.LoadConfig();
foreach (Character character in CharacterStatistics.Keys)
{
if (StatsConfig.ContainsKey(character.ToStringWithOutUser()))
@ -602,6 +680,13 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
CharacterStatistics[character] = StatsConfig.Get<CharacterStatistics>(character.ToStringWithOutUser()) ?? CharacterStatistics[character];
}
}
foreach (Character character in TeamCharacterStatistics.Keys)
{
if (TeamStatsConfig.ContainsKey(character.ToStringWithOutUser()))
{
TeamCharacterStatistics[character] = TeamStatsConfig.Get<CharacterStatistics>(character.ToStringWithOutUser()) ?? TeamCharacterStatistics[character];
}
}
}
public static void UpdateStatistics(CharacterStatistics totalStats, CharacterStatistics stats)

View File

@ -69,6 +69,8 @@
<local:SkillItemFormatterConverter x:Key="SkillItemFormatterConverter"/>
<!-- 新增:技能/物品可用性判断转换器 -->
<local:SkillUsabilityConverter x:Key="SkillUsabilityConverter"/>
<!-- 新增通用ToString转换器 -->
<local:ToStringConverter x:Key="ToStringConverter"/>
<!-- 新增:角色图标的样式 (圆形) -->
<Style x:Key="CharacterIconStyle" TargetType="Border">
@ -119,28 +121,35 @@
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"/>
<!-- 左侧动态队列 -->
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="0.3*"/>
<!-- 地图区域 -->
<ColumnDefinition Width="1*"/>
<!-- 右侧调试日志 -->
<ColumnDefinition Width="0.8*"/>
<!-- 新增:右侧调试日志 -->
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<!-- 地图、左侧队列、右侧日志占据的行 -->
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<!-- 底部信息界面占据的行 -->
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 左侧动态更新队列面板 -->
<Border Grid.Column="0" Grid.Row="0" BorderBrush="LightGray" BorderThickness="1" Margin="5" Padding="5">
<StackPanel x:Name="LeftQueuePanel" MinWidth="180" Background="#FFF0F8FF">
<!-- AliceBlue -->
<TextBlock x:Name="QueueTitle" Text="行动顺序表" Margin="0,0,0,10" FontWeight="Bold" FontSize="14"/>
<!-- 将 StackPanel 替换为 Grid -->
<Grid x:Name="LeftQueuePanelGrid" MinWidth="180" Background="#FFF0F8FF">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<!-- 标题占据所需高度 -->
<RowDefinition Height="*"/>
<!-- ScrollViewer 占据剩余所有高度 -->
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="QueueTitle" Text="行动顺序表" Margin="0,0,0,10" FontWeight="Bold" FontSize="14"/>
<!-- 动态内容将在此处添加 -->
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Height="Auto">
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<ItemsControl x:Name="CharacterQueueItemsControl" ItemsSource="{Binding CharacterQueueDisplayItems, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
@ -149,9 +158,16 @@
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="LightGray"/>
<Style.Triggers>
<!-- 200自己300队友400敌人1800下轮预测 -->
<DataTrigger Binding="{Binding Character.Promotion}" Value="200">
<Setter Property="Background" Value="Bisque"/>
</DataTrigger>
<DataTrigger Binding="{Binding Character.Promotion}" Value="300">
<Setter Property="Background" Value="LightCyan"/>
</DataTrigger>
<DataTrigger Binding="{Binding Character.Promotion}" Value="400">
<Setter Property="Background" Value="MistyRose"/>
</DataTrigger>
<DataTrigger Binding="{Binding Character.Promotion}" Value="1800">
<Setter Property="Background" Value="#FFDDA0DD"/>
</DataTrigger>
@ -178,39 +194,45 @@
<Border.Style>
<Style TargetType="Border">
<!-- 默认背景色 -->
<Setter Property="Background" Value="#FF6A5ACD"/>
<Setter Property="Background" Value="Salmon"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Character.Promotion}" Value="200">
<Setter Property="Background" Value="BurlyWood"/>
</DataTrigger>
<DataTrigger Binding="{Binding Character.Promotion}" Value="300">
<Setter Property="Background" Value="SkyBlue"/>
</DataTrigger>
<DataTrigger Binding="{Binding Character.Promotion}" Value="1800">
<Setter Property="Background" Value="#FF228B22"/>
<Setter Property="Background" Value="Orchid"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding Character.NickName, Converter={StaticResource FirstCharConverter}}"
Foreground="White"
FontSize="10"
FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
Foreground="White"
FontSize="10"
FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<!-- 角色名称和等级 -->
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Character, Converter={StaticResource CharacterToStringWithLevelConverter}}"
FontWeight="SemiBold" FontSize="10" Margin="0,0,0,2"/>
FontWeight="SemiBold" FontSize="10" Margin="0,0,0,2"/>
<!-- ATDelay -->
<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}}" />
Text="{Binding PredictedTurnNumber, StringFormat=回合: {0}}" />
<!-- 直接绑定到 ViewModel 的 TurnRewardSkillName -->
<TextBlock FontSize="10" Foreground="DarkGreen" FontWeight="SemiBold" HorizontalAlignment="Right"
Text="{Binding TurnRewardSkillName}" />
Text="{Binding TurnRewardSkillName}" />
</StackPanel>
</Grid>
</Border>
@ -218,12 +240,12 @@
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</Grid>
</Border>
<!-- 地图区域 (使用ScrollViewer支持大地图滚动) -->
<ScrollViewer Grid.Column="1" Grid.Row="0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"
Background="#FFE0FFFF">
Background="#FFE0FFFF">
<Border BorderBrush="DarkGray" BorderThickness="1" Margin="5">
<!-- 新增一个Grid来容纳Canvas和调试文本框实现叠加效果 -->
<Grid>
@ -235,18 +257,18 @@
<!-- 格子信息面板叠加在Canvas之上位于左侧 -->
<Border x:Name="GridInfoPanel"
Panel.ZIndex="100"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10"
Width="250"
MaxHeight="300"
Background="#CCFFFFFF"
BorderBrush="DarkGray"
BorderThickness="1"
CornerRadius="5"
Padding="10"
Visibility="Collapsed">
Panel.ZIndex="100"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10"
Width="250"
MaxHeight="300"
Background="#CCFFFFFF"
BorderBrush="DarkGray"
BorderThickness="1"
CornerRadius="5"
Padding="10"
Visibility="Collapsed">
<!-- 初始状态为隐藏 -->
<StackPanel>
<TextBlock Text="格子信息" FontWeight="Bold" FontSize="14" Margin="0,0,0,5"/>
@ -256,15 +278,42 @@
<TextBlock x:Name="GridCoordTextBlock" Text="坐标: " Margin="0,2"/>
<TextBlock x:Name="GridColorTextBlock" Text="颜色: " Margin="0,2"/>
<!-- 新增:格子上的角色列表 -->
<TextBlock Text="格子上的角色:" FontWeight="SemiBold" Margin="0,5,0,2"/>
<TextBlock Text="格子上的角色" FontWeight="SemiBold" Margin="0,5,0,2"/>
<ItemsControl x:Name="GridCharactersInfoItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="LightGray" BorderThickness="0,0,0,1" Padding="2" Margin="0,2">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding NickName}" FontWeight="SemiBold" Margin="0,0,5,0"/>
<StackPanel>
<!--<TextBlock Text="{Binding NickName}" FontWeight="SemiBold" Margin="0,0,5,0"/>
<TextBlock Text="{Binding HP, StringFormat=HP:{0:0.##}}"/>
<TextBlock Text="{Binding MaxHP, StringFormat=/{0:0.##}}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding MaxHP, StringFormat=/{0:0.##}}" Margin="0,0,5,0"/>-->
<!-- 角色名称 -->
<TextBlock Text="{Binding NickName}" FontWeight="SemiBold" Margin="0,0,5,0" FontSize="11"/>
<!-- HP 条 -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,1">
<TextBlock Text="HP:" VerticalAlignment="Center" Width="25" FontSize="9"/>
<ProgressBar Value="{Binding HP}" Maximum="{Binding MaxHP}" Height="8" Width="100" Background="LightGray" BorderBrush="Gray" BorderThickness="0.5" Foreground="Green"/>
<TextBlock Text="{Binding HP, StringFormat={}{0:0.##}}" Margin="5,0,0,0" VerticalAlignment="Center" FontSize="9"/>
<TextBlock Text="{Binding MaxHP, StringFormat=/{0:0.##}}" VerticalAlignment="Center" FontSize="9"/>
</StackPanel>
<!-- MP 条 -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,1">
<TextBlock Text="MP:" VerticalAlignment="Center" Width="25" FontSize="9"/>
<ProgressBar Value="{Binding MP}" Maximum="{Binding MaxMP}" Height="8" Width="100" Background="LightGray" BorderBrush="Gray" BorderThickness="0.5" Foreground="Blue"/>
<TextBlock Text="{Binding MP, StringFormat={}{0:0.##}}" Margin="5,0,0,0" VerticalAlignment="Center" FontSize="9"/>
<TextBlock Text="{Binding MaxMP, StringFormat=/{0:0.##}}" VerticalAlignment="Center" FontSize="9"/>
</StackPanel>
<!-- EP 条 -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,1">
<TextBlock Text="EP:" VerticalAlignment="Center" Width="25" FontSize="9"/>
<ProgressBar Value="{Binding EP}" Maximum="{Binding GameplayEquilibriumConstant.MaxEP}" Height="8" Width="100" Background="LightGray" BorderBrush="Gray" BorderThickness="0.5" Foreground="Orange"/>
<TextBlock Text="{Binding EP, StringFormat={}{0:0.##}}" Margin="5,0,0,0" VerticalAlignment="Center" FontSize="9"/>
<TextBlock Text="{Binding GameplayEquilibriumConstant.MaxEP, StringFormat=/{0:0.##}}" VerticalAlignment="Center" FontSize="9"/>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
@ -281,17 +330,17 @@
<!-- 新增:角色选择面板 (Overlay) -->
<Border x:Name="CharacterSelectionOverlay"
Panel.ZIndex="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="400"
MaxHeight="600"
Background="#DDFFFFFF"
BorderBrush="DarkBlue"
BorderThickness="2"
CornerRadius="10"
Padding="20"
Visibility="Collapsed">
Panel.ZIndex="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="400"
MaxHeight="600"
Background="#DDFFFFFF"
BorderBrush="DarkBlue"
BorderThickness="2"
CornerRadius="10"
Padding="20"
Visibility="Collapsed">
<StackPanel>
<TextBlock Text="请选择你的角色" FontWeight="Bold" FontSize="16" Margin="0,0,0,10" HorizontalAlignment="Center"/>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="300">
@ -299,9 +348,9 @@
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource SelectionItemStyle}"
Tag="{Binding}"
MouseLeftButtonDown="CharacterSelectionItem_MouseLeftButtonDown"
MouseEnter="CharacterSelectionItem_MouseEnter">
Tag="{Binding}"
MouseLeftButtonDown="CharacterSelectionItem_MouseLeftButtonDown"
MouseEnter="CharacterSelectionItem_MouseEnter">
<TextBlock Text="{Binding NickName}" FontSize="14" FontWeight="SemiBold"/>
</Border>
</DataTemplate>
@ -313,14 +362,14 @@
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="200">
<!-- 限制详情区域高度 -->
<RichTextBox x:Name="CharacterDetailsRichTextBox"
IsReadOnly="True"
BorderThickness="0"
Background="Transparent"
Foreground="DimGray"
FontSize="11"
AcceptsReturn="True"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
IsReadOnly="True"
BorderThickness="0"
Background="Transparent"
Foreground="DimGray"
FontSize="11"
AcceptsReturn="True"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
<FlowDocument>
<Paragraph Margin="0">将鼠标悬停在角色名称上以查看详情。</Paragraph>
</FlowDocument>
@ -331,17 +380,17 @@
<!-- 新增:技能/物品选择面板 (Overlay) -->
<Border x:Name="SkillItemSelectionOverlay"
Panel.ZIndex="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="400"
MaxHeight="600"
Background="#DDFFFFFF"
BorderBrush="DarkGreen"
BorderThickness="2"
CornerRadius="10"
Padding="20"
Visibility="Collapsed">
Panel.ZIndex="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="400"
MaxHeight="600"
Background="#DDFFFFFF"
BorderBrush="DarkGreen"
BorderThickness="2"
CornerRadius="10"
Padding="20"
Visibility="Collapsed">
<StackPanel>
<TextBlock x:Name="SkillItemSelectionTitle" Text="请选择技能/物品" FontWeight="Bold" FontSize="16" Margin="0,0,0,10" HorizontalAlignment="Center"/>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="300">
@ -349,9 +398,9 @@
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource SelectionItemStyle}"
Tag="{Binding}"
MouseLeftButtonDown="SkillItemSelectionItem_MouseLeftButtonDown"
MouseEnter="SkillItemSelectionItem_MouseEnter">
Tag="{Binding}"
MouseLeftButtonDown="SkillItemSelectionItem_MouseLeftButtonDown"
MouseEnter="SkillItemSelectionItem_MouseEnter">
<Border.IsEnabled>
<MultiBinding Converter="{StaticResource SkillUsabilityConverter}">
<Binding Path="."/>
@ -369,14 +418,14 @@
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="200">
<!-- 限制详情区域高度 -->
<RichTextBox x:Name="SkillItemDetailsRichTextBox"
IsReadOnly="True"
BorderThickness="0"
Background="Transparent"
Foreground="DimGray"
FontSize="11"
AcceptsReturn="True"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
IsReadOnly="True"
BorderThickness="0"
Background="Transparent"
Foreground="DimGray"
FontSize="11"
AcceptsReturn="True"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
<FlowDocument>
<Paragraph Margin="0">将鼠标悬停在名称上以查看详情。</Paragraph>
</FlowDocument>
@ -388,18 +437,18 @@
<!-- 新增:目标选择确认面板 (Overlay) -->
<Border x:Name="TargetSelectionOverlay"
Panel.ZIndex="200"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Margin="10"
Width="250"
MaxHeight="300"
Background="#CCFFFFFF"
BorderBrush="DarkRed"
BorderThickness="1"
CornerRadius="5"
Padding="10"
Visibility="Collapsed">
Panel.ZIndex="200"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Margin="10"
Width="250"
MaxHeight="300"
Background="#CCFFFFFF"
BorderBrush="DarkRed"
BorderThickness="1"
CornerRadius="5"
Padding="10"
Visibility="Collapsed">
<StackPanel>
<TextBlock x:Name="TargetSelectionTitle" Text="选择目标" FontWeight="Bold" FontSize="14" Margin="0,0,0,5"/>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="200">
@ -445,7 +494,7 @@
<!-- 移除按钮 -->
<Button Grid.Column="1" Content="X" Margin="5,0,0,0" Tag="{Binding Id}" Click="RemoveTarget_Click"
Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" VerticalAlignment="Center"/>
Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" VerticalAlignment="Center"/>
</Grid>
</Border>
</DataTemplate>
@ -461,17 +510,17 @@
<!-- 新增:继续提示面板 (Overlay) -->
<Border x:Name="ContinuePromptOverlay"
Panel.ZIndex="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="300"
Height="150"
Background="#DDFFFFFF"
BorderBrush="DarkOrange"
BorderThickness="2"
CornerRadius="10"
Padding="20"
Visibility="Collapsed">
Panel.ZIndex="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="300"
Height="150"
Background="#DDFFFFFF"
BorderBrush="DarkOrange"
BorderThickness="2"
CornerRadius="10"
Padding="20"
Visibility="Collapsed">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock x:Name="ContinuePromptTextBlock" Text="请点击继续..." TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="14" Margin="0,0,0,15"/>
<Button Content="继续" Width="80" Click="ContinuePromptButton_Click"/>
@ -479,17 +528,17 @@
</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 秒后继续..." />
Panel.ZIndex="200"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Margin="15"
FontSize="18"
FontWeight="Bold"
Foreground="White"
Background="#88000000"
Padding="8"
Visibility="Collapsed"
Text="0 秒后继续..." />
</Grid>
</Border>
@ -504,18 +553,18 @@
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="调试日志" Margin="0,0,0,10" FontWeight="Bold" FontSize="14"/>
<ScrollViewer Grid.Row="1" x:Name="DebugLogScrollViewer"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<!-- 修改:将 TextBlock 替换为 RichTextBox -->
<RichTextBox x:Name="DebugLogRichTextBox"
IsReadOnly="True"
BorderThickness="0"
Background="Transparent"
Foreground="DimGray"
FontSize="11"
AcceptsReturn="True"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
IsReadOnly="True"
BorderThickness="0"
Background="Transparent"
Foreground="DimGray"
FontSize="11"
AcceptsReturn="True"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
<FlowDocument>
<Paragraph Margin="0">调试日志:</Paragraph>
</FlowDocument>
@ -559,16 +608,16 @@
<!-- 头像框 - 显示字符 -->
<Border x:Name="CharacterAvatarBorder" Grid.RowSpan="4" Grid.Column="0" Width="60" Height="60" BorderBrush="DarkGray" BorderThickness="1" CornerRadius="5" Margin="0,0,10,0"
Background="#FF6A5ACD">
Background="#FF6A5ACD">
<!-- 默认背景色,可以根据角色动态改变 -->
<Grid>
<TextBlock x:Name="CharacterAvatarTextBlock"
Text="?"
Foreground="White"
FontSize="28"
FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
Text="?"
Foreground="White"
FontSize="28"
FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Border>
@ -586,7 +635,7 @@
<Rectangle x:Name="HpFillRectangle" HorizontalAlignment="Left" Fill="Green" Height="10" RadiusX="2" RadiusY="2"/>
<!-- 护盾进度叠加在HP之上 -->
<Rectangle x:Name="ShieldFillRectangle" HorizontalAlignment="Left" Fill="Transparent" Height="10" RadiusX="2" RadiusY="2"
Visibility="Collapsed" Panel.ZIndex="1"/>
Visibility="Collapsed" Panel.ZIndex="1"/>
</Grid>
<TextBlock x:Name="HpValueTextBlock" Text="0/0" Margin="5,0,0,0" VerticalAlignment="Center" FontSize="10"/>
</StackPanel>
@ -652,7 +701,7 @@
<!-- 新增中间部分:装备和状态 -->
<Border Grid.Column="1" BorderBrush="LightGray" BorderThickness="0,0,1,0" Margin="0,0,5,0" Padding="5"
x:Name="EquipStatusInfoBorder" MouseLeftButtonDown="EquipStatusInfoBorder_MouseLeftButtonDown">
x:Name="EquipStatusInfoBorder" MouseLeftButtonDown="EquipStatusInfoBorder_MouseLeftButtonDown">
<StackPanel Orientation="Vertical">
<!-- 装备栏 -->
<TextBlock Text="装备" FontWeight="Bold" FontSize="14" Margin="0,0,0,5"/>
@ -695,18 +744,18 @@
<Border BorderBrush="LightGray" BorderThickness="0,1,0,0" Margin="0,10,0,0" Padding="0,10,0,0">
<StackPanel>
<TextBlock Text="详情描述" FontWeight="Bold" FontSize="14" Margin="0,0,0,5"/>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="80">
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="140">
<RichTextBox x:Name="DescriptionRichTextBox"
IsReadOnly="True"
BorderThickness="0"
Background="Transparent"
Foreground="DimGray"
FontSize="11"
AcceptsReturn="True"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
IsReadOnly="True"
BorderThickness="0"
Background="Transparent"
Foreground="DimGray"
FontSize="11"
AcceptsReturn="True"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
<FlowDocument>
<Paragraph Margin="0">点击装备或状态图标查看详情。</Paragraph>
<Paragraph Margin="0">点击装备、状态、技能或物品的图标以查看详情。</Paragraph>
</FlowDocument>
</RichTextBox>
</ScrollViewer>
@ -715,36 +764,139 @@
</StackPanel>
</Border>
<!-- 右半部分:操作按钮 -->
<!-- 右半部分:操作按钮 和 数据统计 (Grid.Column="2") -->
<Border Grid.Column="2" Padding="5">
<StackPanel Orientation="Vertical">
<TextBlock Text="操作" FontWeight="Bold" FontSize="14" Margin="0,0,0,10"/>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Center">
<!-- IsEnabled 默认设置为 False由代码控制 -->
<!-- Tag 属性用于在 Click 事件中识别是哪个动作 -->
<Button x:Name="MoveButton" Content="移动" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.Move}" IsEnabled="False"/>
<Button x:Name="AttackButton" Content="普通攻击" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.NormalAttack}" 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="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>
<!-- 将原有的StackPanel替换为Grid以容纳新的技能/物品区域 -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<!-- 新增:技能和物品图标区域 -->
<RowDefinition Height="*"/>
<!-- 原有的操作按钮和数据统计 -->
</Grid.RowDefinitions>
<!-- 新增:数据统计 -->
<Border BorderBrush="LightGray" BorderThickness="0,1,0,0" Margin="0,10,0,0" Padding="0,10,0,0">
<StackPanel>
<TextBlock Text="数据统计" FontWeight="Bold" FontSize="14" Margin="0,0,0,5"/>
<TextBlock x:Name="StatsRatingKillsAssistsDeathsTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsLiveTimeRoundTurnTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsControlHealShieldTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsTotalDamageTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsTotalTakenDamageTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsTrueDamageTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Visibility="Collapsed"/>
<!-- 初始隐藏,有真实伤害时显示 -->
<TextBlock x:Name="StatsDamagePerSecondTurnTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
</StackPanel>
<!-- 新增:技能和物品图标区域 (Grid.Row="0") -->
<Border Grid.Row="0" BorderBrush="LightGray" BorderThickness="0,0,0,1" Margin="0,0,0,10" Padding="0,0,0,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<!-- 技能列 -->
<ColumnDefinition Width="*"/>
<!-- 物品列 -->
</Grid.ColumnDefinitions>
<!-- 技能图标列表 -->
<StackPanel Grid.Column="0" Margin="0,0,5,0">
<TextBlock Text="技能" FontWeight="Bold" FontSize="12" Margin="0,0,0,5"/>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="80">
<ItemsControl x:Name="CharacterSkillsPanel" ItemsSource="{Binding CharacterSkillsAndItems.Skills, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Tag="{Binding}"
ToolTip="{Binding Converter={StaticResource ToStringConverter}}"
MouseLeftButtonDown="SkillIcon_MouseLeftButtonDown">
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource StatusIconStyle}">
<Style.Triggers>
<DataTrigger Value="False">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource SkillUsabilityConverter}">
<Binding Path="."/>
<Binding ElementName="GameMapViewerControl" Path="CurrentCharacter"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Background" Value="LightGray"/>
<Setter Property="BorderBrush" Value="DimGray"/>
<Setter Property="TextElement.Foreground" Value="DimGray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding Name, Converter={StaticResource FirstCharConverter}}"
Style="{StaticResource StatusIconTextStyle}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
<!-- 物品图标列表 -->
<StackPanel Grid.Column="1" Margin="5,0,0,0">
<TextBlock Text="物品" FontWeight="Bold" FontSize="12" Margin="0,0,0,5"/>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="80">
<ItemsControl x:Name="CharacterItemsPanel" ItemsSource="{Binding CharacterSkillsAndItems.Items, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Tag="{Binding}"
ToolTip="{Binding Converter={StaticResource ToStringConverter}}"
MouseLeftButtonDown="ItemIcon_MouseLeftButtonDown">
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource StatusIconStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Enable}" Value="False">
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Background" Value="LightGray"/>
<Setter Property="BorderBrush" Value="DimGray"/>
<Setter Property="TextElement.Foreground" Value="DimGray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding Name, Converter={StaticResource FirstCharConverter}}"
Style="{StaticResource StatusIconTextStyle}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</Grid>
</Border>
</StackPanel>
<!-- 原有的操作按钮和数据统计 (现在在 Grid.Row="1") -->
<StackPanel Grid.Row="1" Orientation="Vertical">
<TextBlock Text="操作" FontWeight="Bold" FontSize="14" Margin="0,0,0,10"/>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Center">
<!-- IsEnabled 默认设置为 False由代码控制 -->
<!-- Tag 属性用于在 Click 事件中识别是哪个动作 -->
<Button x:Name="MoveButton" Content="移动" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.Move}" IsEnabled="False"/>
<Button x:Name="AttackButton" Content="普通攻击" Width="100" Height="30" Margin="5" Click="ActionButton_Click" Tag="{x:Static constant:CharacterActionType.NormalAttack}" 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="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"/>
<Button x:Name="AutoModeButton" Content="自动模式:关" Width="100" Height="30" Margin="5" Click="AutoModeButton_Click" Tag="" IsEnabled="True"/>
<Button x:Name="FastModeButton" Content="快速模式:关" Width="100" Height="30" Margin="5" Click="FastModeButton_Click" Tag="" IsEnabled="True"/>
</WrapPanel>
<!-- 新增:数据统计 -->
<Border BorderBrush="LightGray" BorderThickness="0,1,0,0" Margin="0,10,0,0" Padding="0,10,0,0">
<StackPanel>
<TextBlock Text="数据统计" FontWeight="Bold" FontSize="14" Margin="0,0,0,5"/>
<TextBlock x:Name="StatsRatingKillsAssistsDeathsTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsLiveTimeRoundTurnTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsControlHealShieldTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsTotalDamageTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsTotalTakenDamageTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
<TextBlock x:Name="StatsTrueDamageTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Visibility="Collapsed"/>
<!-- 初始隐藏,有真实伤害时显示 -->
<TextBlock x:Name="StatsDamagePerSecondTurnTextBlock" Style="{StaticResource CharacterAttributeTextStyle}"/>
</StackPanel>
</Border>
</StackPanel>
</Grid>
</Border>
</Grid>
</Border>

View File

@ -3,21 +3,22 @@ using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.Xml.Linq;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Entity;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
using Oshima.FunGame.OshimaServers.Service;
using static Milimoe.FunGame.Core.Library.Constant.General;
using Brush = System.Windows.Media.Brush;
using Brushes = System.Windows.Media.Brushes;
using Button = System.Windows.Controls.Button;
using Grid = Milimoe.FunGame.Core.Library.Common.Addon.Grid;
using MessageBox = System.Windows.MessageBox;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using Panel = System.Windows.Controls.Panel;
using Rectangle = System.Windows.Shapes.Rectangle;
@ -67,6 +68,14 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
private bool _isSelectingTargets = false; // 标记当前是否处于目标选择模式
private readonly CharacterQueueItem _selectionPredictCharacter = new(Factory.GetCharacter(), 0); // 选择时进行下轮预测(用于行动顺序表显示)
// 新增:用于跟踪当前高亮的技能和物品图标
private Border? _highlightedSkillIcon;
private Border? _highlightedItemIcon;
// 模式
private bool _isAutoMode = false;
private bool _isFastMode = false;
public GameMapViewer()
{
InitializeComponent();
@ -112,7 +121,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
get { return (int)GetValue(CurrentRoundProperty); }
set { SetValue(CurrentRoundProperty, value); }
}
// 回合奖励
public static readonly DependencyProperty TurnRewardsProperty =
DependencyProperty.Register("TurnRewards", typeof(Dictionary<int, List<Skill>>), typeof(GameMapViewer),
@ -123,7 +132,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
get { return (Dictionary<int, List<Skill>>)GetValue(TurnRewardsProperty); }
set { SetValue(TurnRewardsProperty, value); }
}
// 新增 CurrentCharacter 依赖属性:用于显示当前玩家角色的信息
public static readonly DependencyProperty CurrentCharacterProperty =
DependencyProperty.Register("CurrentCharacter", typeof(Character), typeof(GameMapViewer),
@ -143,6 +152,9 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
// 用于 UI 绑定的 ViewModel 集合
public ObservableCollection<CharacterQueueItemViewModel> CharacterQueueDisplayItems { get; } = [];
// 技能组
public CharacterSkillsAndItemsViewModel CharacterSkillsAndItems { get; set; } = new();
public Character? CurrentCharacter
{
get { return (Character)GetValue(CurrentCharacterProperty); }
@ -199,23 +211,25 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
set { SetValue(MaxRespawnTimesProperty, value); }
}
public void Invoke(Action action)
public async Task InvokeAsync(Action action)
{
if (!this.Dispatcher.CheckAccess())
{
this.Dispatcher.Invoke(() => action());
await this.Dispatcher.InvokeAsync(action);
}
else
{
action();
}
else action();
}
public async Task Invoke(Func<Task> action)
public async Task InvokeAsync(Func<Task> task)
{
if (!this.Dispatcher.CheckAccess())
{
await this.Dispatcher.InvokeAsync(async () => await action());
await this.Dispatcher.InvokeAsync(task);
}
else await action();
else await task();
}
// 原有的 ShowInput 方法不再用于游戏逻辑,可以删除或保留用于其他非游戏逻辑的通用输入
@ -288,17 +302,15 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
GameMapViewer viewer = (GameMapViewer)d;
_ = viewer.UpdateBottomInfoPanel();
_ = viewer.UpdateCharacterStatisticsPanel(); // 角色改变时也更新统计面板
// 角色改变时,清除装备/状态描述
SetRichTextBoxText(viewer.DescriptionRichTextBox, "点击装备或状态图标查看详情。");
viewer.ClearEquipSlotHighlights();
viewer.ClearStatusIconHighlights();
// 角色改变时,清除装备/状态/技能/物品描述和高亮
viewer.ResetDescriptionAndHighlights(); // 使用新的重置方法
}
// 新增当CharacterQueueData属性改变时更新左侧队列面板
private static void OnCharacterQueueDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
GameMapViewer viewer = (GameMapViewer)d;
_ = viewer.UpdateLeftQueuePanel();
_ = viewer.UpdateLeftQueuePanelGrid();
}
// 新增当CharacterStatistics属性改变时更新数据统计面板
@ -521,6 +533,20 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
_uiElementToCharacter.Add(characterBorder, character);
}
// 清除所有角色的高亮
foreach (Character character in _characterToUiElement.Keys)
{
Border border = _characterToUiElement[character];
border.BorderBrush = character.Promotion switch
{
200 => Brushes.BurlyWood,
300 => Brushes.SkyBlue,
400 => Brushes.Orchid,
_ => Brushes.Salmon
};
border.BorderThickness = new Thickness(2);
}
// 如果处于目标选择模式,重新应用高亮
if (_isSelectingTargets)
{
@ -531,12 +557,12 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
/// <summary>
/// 更新左侧动态队列面板显示角色及其AT Delay。
/// </summary>
public async Task UpdateLeftQueuePanel()
public async Task UpdateLeftQueuePanelGrid()
{
// 确保在UI线程上执行
if (!this.Dispatcher.CheckAccess())
{
await this.Dispatcher.InvokeAsync(async () => await UpdateLeftQueuePanel());
await this.Dispatcher.InvokeAsync(async () => await UpdateLeftQueuePanelGrid());
return;
}
@ -569,10 +595,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
return;
}
// 每次更新面板时,清除装备/状态描述和高亮
SetRichTextBoxText(DescriptionRichTextBox, "点击装备或状态图标查看详情。"); // 新代码
ClearEquipSlotHighlights();
ClearStatusIconHighlights();
// 每次更新面板时,清除所有描述和高亮
ResetDescriptionAndHighlights(true);
Character? character = CurrentCharacter;
if (character != null)
@ -667,10 +691,15 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
}
}
// 更新技能组
CharacterSkillsAndItems.Skills = [character.NormalAttack, .. character.Skills];
CharacterSkillsAndItems.Items = [.. character.Items];
// --- 更新其他角色属性 ---
bool showGrowth = false; // 假设不显示成长值
AttackTextBlock.Text = $"攻击力:{character.ATK:0.##}" + (character.ExATK != 0 ? $" [{character.BaseATK:0.##} {(character.ExATK >= 0 ? "+" : "-")} {Math.Abs(character.ExATK):0.##}]" : "");
double exATK = character.ExATK + character.ExATK2 + character.ExATK3;
AttackTextBlock.Text = $"攻击力:{character.ATK:0.##}" + (exATK != 0 ? $" [{character.BaseATK:0.##} {(exATK >= 0 ? "+" : "-")} {Math.Abs(exATK):0.##}]" : "");
double exDEF = character.ExDEF + character.ExDEF2 + character.ExDEF3;
PhysicalDefTextBlock.Text = $"物理护甲:{character.DEF:0.##}" + (exDEF != 0 ? $" [{character.BaseDEF:0.##} {(exDEF >= 0 ? "+" : "-")} {Math.Abs(exDEF):0.##}]" : "") + $" ({character.PDR * 100:0.##}%)";
@ -731,6 +760,10 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
// 清空状态条
CharacterEffectsPanel.Children.Clear();
// 清空技能组
CharacterSkillsAndItems.Skills = [];
CharacterSkillsAndItems.Items = [];
// 清空角色属性
AttackTextBlock.Text = "攻击力:";
PhysicalDefTextBlock.Text = "物理护甲:";
@ -979,7 +1012,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
CurrentCharacter = PlayerCharacter;
}
// 新增:关闭格子信息面板时,重置装备/状态描述和高亮
// 新增:关闭格子信息面板时,重置装备/状态/技能/物品描述和高亮
ResetDescriptionAndHighlights();
}
@ -1001,6 +1034,9 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
ClearEquipSlotHighlights();
ClearStatusIconHighlights();
ClearSkillIconHighlights();
ClearItemIconHighlights();
clickedBorder.BorderBrush = Brushes.Blue;
clickedBorder.BorderThickness = new Thickness(2);
@ -1054,6 +1090,9 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
ClearStatusIconHighlights(); // 清除所有状态图标的旧高亮
ClearEquipSlotHighlights(); // 清除所有装备槽位的旧高亮
ClearSkillIconHighlights(); // 清除技能图标高亮
ClearItemIconHighlights(); // 清除物品图标高亮
clickedBorder.BorderBrush = Brushes.Blue; // 高亮当前点击的状态图标
clickedBorder.BorderThickness = new Thickness(1.5);
@ -1069,11 +1108,106 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
foreach (Border border in CharacterEffectsPanel.Children.OfType<Border>())
{
border.BorderBrush = Brushes.Gray;
border.BorderThickness = new Thickness(0.5);
// 获取默认样式,以便恢复默认边框颜色和粗细
Style defaultStyle = (Style)this.FindResource("StatusIconStyle");
border.BorderBrush = (Brush)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderBrushProperty)?.Value ?? Brushes.Gray);
border.BorderThickness = (Thickness)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderThicknessProperty)?.Value ?? new Thickness(0.5));
}
}
// --- 新增:技能图标点击事件和辅助方法 ---
private void SkillIcon_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Border clickedBorder && clickedBorder.Tag is ISkill skill)
{
ClearSkillIconHighlights();
ClearItemIconHighlights();
ClearEquipSlotHighlights();
ClearStatusIconHighlights();
_highlightedSkillIcon = clickedBorder;
_highlightedSkillIcon.BorderBrush = Brushes.Blue;
_highlightedSkillIcon.BorderThickness = new Thickness(1.5);
SetRichTextBoxText(DescriptionRichTextBox, skill.ToString() ?? "");
_ = AppendDebugLog($"查看技能: {skill.Name}");
}
}
/// <summary>
/// 清除所有技能图标的边框高亮。
/// </summary>
private void ClearSkillIconHighlights()
{
if (_highlightedSkillIcon != null)
{
Style defaultStyle = (Style)this.FindResource("StatusIconStyle");
_highlightedSkillIcon.BorderBrush = (Brush)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderBrushProperty)?.Value ?? Brushes.Gray);
_highlightedSkillIcon.BorderThickness = (Thickness)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderThicknessProperty)?.Value ?? new Thickness(0.5));
_highlightedSkillIcon = null;
}
// 遍历 ItemsControl 中的所有生成的容器,确保所有图标都恢复默认样式
if (CharacterSkillsPanel.ItemsSource != null)
{
foreach (var item in CharacterSkillsPanel.ItemsSource)
{
if (CharacterSkillsPanel.ItemContainerGenerator.ContainerFromItem(item) is Border border)
{
Style defaultStyle = (Style)this.FindResource("StatusIconStyle");
border.BorderBrush = (Brush)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderBrushProperty)?.Value ?? Brushes.Gray);
border.BorderThickness = (Thickness)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderThicknessProperty)?.Value ?? new Thickness(0.5));
}
}
}
}
// --- 新增:物品图标点击事件和辅助方法 ---
private void ItemIcon_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Border clickedBorder && clickedBorder.Tag is Item item)
{
ClearSkillIconHighlights();
ClearItemIconHighlights();
ClearEquipSlotHighlights();
ClearStatusIconHighlights();
_highlightedItemIcon = clickedBorder;
_highlightedItemIcon.BorderBrush = Brushes.Blue;
_highlightedItemIcon.BorderThickness = new Thickness(1.5);
SetRichTextBoxText(DescriptionRichTextBox, item.ToString() ?? "");
_ = AppendDebugLog($"查看物品: {item.Name}");
}
}
/// <summary>
/// 清除所有物品图标的边框高亮。
/// </summary>
private void ClearItemIconHighlights()
{
if (_highlightedItemIcon != null)
{
Style defaultStyle = (Style)this.FindResource("StatusIconStyle");
_highlightedItemIcon.BorderBrush = (Brush)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderBrushProperty)?.Value ?? Brushes.Gray);
_highlightedItemIcon.BorderThickness = (Thickness)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderThicknessProperty)?.Value ?? new Thickness(0.5));
_highlightedItemIcon = null;
}
// 遍历 ItemsControl 中的所有生成的容器,确保所有图标都恢复默认样式
if (CharacterItemsPanel.ItemsSource != null)
{
foreach (var item in CharacterItemsPanel.ItemsSource)
{
if (CharacterItemsPanel.ItemContainerGenerator.ContainerFromItem(item) is Border border)
{
Style defaultStyle = (Style)this.FindResource("StatusIconStyle");
border.BorderBrush = (Brush)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderBrushProperty)?.Value ?? Brushes.Gray);
border.BorderThickness = (Thickness)(defaultStyle.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == Border.BorderThicknessProperty)?.Value ?? new Thickness(0.5));
}
}
}
}
// --- 操作按钮 (底部右侧) ---
/// <summary>
@ -1099,7 +1233,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
_resolveActionType?.Invoke(actionType);
}
}
/// <summary>
/// 预释放爆发技的特殊处理。
/// </summary>
@ -1123,6 +1257,59 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
}
}
/// <summary>
/// 自动模式(托管)的特殊处理。
/// </summary>
private async void AutoModeButton_Click(object sender, RoutedEventArgs e)
{
if (PlayerCharacter != null)
{
_isAutoMode = !_isAutoMode;
AutoModeButton.Content = _isAutoMode ? "自动模式:开" : "自动模式:关";
_controller.SetAutoMode(!_isAutoMode, PlayerCharacter);
await AppendDebugLog($"已切换到{(_isAutoMode ? "" : "")}。");
if (_resolveActionType != null)
{
MessageBoxResult result = MessageBox.Show(
"是否跳过当前回合AI将代替你操作。",
"自动模式跳过当前回合确认",
MessageBoxButton.YesNo,
MessageBoxImage.Question
);
if (result == MessageBoxResult.Yes)
{
_resolveActionType.Invoke(GamingQueue.GetActionType(0.33, 0.33, 0.34));
}
else
{
await AppendDebugLog("自动模式将在下一回合开始接管你的回合。");
}
}
}
else
{
await AppendDebugLog("找不到角色。");
}
}
/// <summary>
/// 快速模式(仅看我的回合)的特殊处理。
/// </summary>
private async void FastModeButton_Click(object sender, RoutedEventArgs e)
{
if (PlayerCharacter != null)
{
_isFastMode = !_isFastMode;
FastModeButton.Content = _isFastMode ? "快速模式:开" : "快速模式:关";
_controller.SetFastMode(_isFastMode);
await AppendDebugLog($"已切换到{(_isFastMode ? "" : "")}。");
}
else
{
await AppendDebugLog("找不到角色。");
}
}
// --- UI 提示方法 (由 GameMapController 调用) ---
/// <summary>
@ -1134,7 +1321,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
_resolveCharacterSelection = callback;
CharacterSelectionItemsControl.ItemsSource = availableCharacters;
SetRichTextBoxText(CharacterDetailsRichTextBox, "将鼠标悬停在角色名称上查看详情。");
SetRichTextBoxText(CharacterDetailsRichTextBox, "将鼠标悬停在角色名称上查看详情。");
CharacterSelectionOverlay.Visibility = Visibility.Visible;
}
@ -1436,13 +1623,6 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
/// </summary>
private void UpdateCharacterHighlights()
{
// 清除所有角色的高亮
foreach (var border in _characterToUiElement.Values)
{
border.BorderBrush = Brushes.DarkBlue; // 默认颜色
border.BorderThickness = new Thickness(1.5);
}
// 高亮潜在目标(黄色边框)
if (_potentialTargetsForSelection != null)
{
@ -1451,7 +1631,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
if (_characterToUiElement.TryGetValue(potentialTarget, out Border? border))
{
border.BorderBrush = Brushes.Yellow;
border.BorderThickness = new Thickness(2);
border.BorderThickness = new Thickness(4);
}
}
}
@ -1462,7 +1642,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
if (_characterToUiElement.TryGetValue(selectedTarget, out Border? border))
{
border.BorderBrush = Brushes.Red; // 已选目标颜色
border.BorderThickness = new Thickness(3);
border.BorderThickness = new Thickness(6);
}
}
}
@ -1604,11 +1784,16 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
}
}
private void ResetDescriptionAndHighlights()
/// <summary>
/// 重置所有描述文本框并清除所有高亮。
/// </summary>
private void ResetDescriptionAndHighlights(bool clearHightlightsOnly = false)
{
SetRichTextBoxText(DescriptionRichTextBox, "点击装备或状态图标查看详情。");
if (!clearHightlightsOnly) SetRichTextBoxText(DescriptionRichTextBox, "点击装备、状态、技能或物品的图标以查看详情。");
ClearEquipSlotHighlights();
ClearStatusIconHighlights();
ClearSkillIconHighlights();
ClearItemIconHighlights();
}
/// <summary>

View File

@ -1,9 +1,47 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Entity;
namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
{
public class CharacterSkillsAndItemsViewModel : INotifyPropertyChanged
{
public List<ISkill> Skills
{
get => _skills;
set
{
if (_skills != value)
{
_skills = value;
OnPropertyChanged();
}
}
}
private List<ISkill> _skills = [];
public List<Item> Items
{
get => _items;
set
{
if (_items != value)
{
_items = value;
OnPropertyChanged();
}
}
}
private List<Item> _items = [];
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class CharacterQueueItemViewModel(CharacterQueueItem model, Func<Dictionary<int, List<Skill>>> getTurnRewards) : INotifyPropertyChanged
{
public CharacterQueueItem Model { get; } = model ?? throw new ArgumentNullException(nameof(model));