添加地图选取目标

This commit is contained in:
milimoe 2025-09-10 19:43:59 +08:00
parent 146b42bff3
commit 58d31b826d
Signed by: milimoe
GPG Key ID: 9554D37E4B8991D0
5 changed files with 106 additions and 43 deletions

View File

@ -98,15 +98,14 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
);
}
public async Task<List<Character>> RequestTargetSelection(Character character, ISkill skill, List<Character> enemys, List<Character> teammates)
public async Task<List<Character>> RequestTargetSelection(Character character, ISkill skill, List<Character> enemys, List<Character> teammates, List<Grid> range)
{
List<Character> selectable = skill.GetSelectableTargets(character, enemys, teammates);
await WriteLine($"请为 {character.NickName} 选择目标 (最多 {skill.RealCanSelectTargetCount(enemys, teammates)} 个)。");
List<Character> targetIds = await _targetSelectionRequester.RequestInput(
async (callback) => await UI.InvokeAsync(() => UI.ShowTargetSelectionUI(character, skill, selectable, enemys, teammates, callback))
List<Character> targets = await _targetSelectionRequester.RequestInput(
async (callback) => await UI.InvokeAsync(() => UI.ShowTargetSelectionUI(character, skill, selectable, enemys, teammates, range, callback))
) ?? [];
if (targetIds == null) return [];
return [.. selectable.Where(targetIds.Contains)];
return [.. targets.Where(selectable.Contains)];
}
public async Task<Skill?> RequestSkillSelection(Character character, List<Skill> availableSkills)

View File

@ -64,11 +64,13 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
Level = slevel
};
c.Skills.Add(skill);
Skill passive = new (c)
foreach (Skill skillLoop in FunGameConstant.CommonPassiveSkills)
{
Level = 1
};
c.Skills.Add(passive);
Skill passive = skillLoop.Copy();
passive.Character = c;
passive.Level = 1;
c.Skills.Add(passive);
}
characters.Add(c);
}
@ -176,6 +178,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
List<Grid> grids = [.. map.Grids.Values];
foreach (Character character in characters)
{
character.NormalAttack.GamingQueue = _gamingQueue;
Grid grid = Grid.Empty;
do
{
@ -482,7 +485,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
}
}
private async Task<Grid> GamingQueue_SelectTargetGrid(GamingQueue queue, Character character, List<Character> enemys, List<Character> teammates, GameMap map)
private async Task<Grid> GamingQueue_SelectTargetGrid(GamingQueue queue, Character character, List<Character> enemys, List<Character> teammates, GameMap map, List<Grid> moveRange)
{
if (!IsPlayer_OnlyTest(queue, character) || _gamingQueue is null || _gamingQueue.Map is null) return Grid.Empty;
@ -526,7 +529,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
return true;
}
private async Task<List<Character>> GamingQueue_SelectNormalAttackTargets(GamingQueue queue, Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates)
private async Task<List<Character>> GamingQueue_SelectNormalAttackTargets(GamingQueue queue, Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates, List<Grid> attackRange)
{
if (!IsPlayer_OnlyTest(queue, character)) return [];
@ -535,7 +538,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
character,
attack,
enemys,
teammates
teammates,
attackRange
);
await Controller.ResolveTargetSelection(selectedTargets);
@ -552,7 +556,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
return selectedItem;
}
private async Task<List<Character>> GamingQueue_SelectSkillTargets(GamingQueue queue, Character caster, Skill skill, List<Character> enemys, List<Character> teammates)
private async Task<List<Character>> GamingQueue_SelectSkillTargets(GamingQueue queue, Character caster, Skill skill, List<Character> enemys, List<Character> teammates, List<Grid> castRange)
{
if (!IsPlayer_OnlyTest(queue, caster)) return [];
@ -566,7 +570,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
caster,
skill,
enemys,
teammates
teammates,
castRange
);
await Controller.ResolveTargetSelection(selectedTargets);
@ -670,7 +675,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
if (b != null) drops.Add(b);
if (c != null) drops.Add(c);
if (d != null) drops.Add(d);
Item? magicCardPack = FunGameService.GenerateMagicCardPack(3, (QualityType)mQuality);
mQuality = 2;
Item? magicCardPack = FunGameService.GenerateMagicCardPack(8, (QualityType)mQuality);
if (magicCardPack != null)
{
foreach (Skill magic in magicCardPack.Skills.Magics)

View File

@ -575,7 +575,7 @@
</StackPanel>
<!-- 移除按钮 -->
<Button Grid.Column="1" Content="X" Margin="5,0,0,0" Tag="{Binding Id}" Click="RemoveTarget_Click"
<Button Grid.Column="1" Content="X" Margin="5,0,0,0" Tag="{Binding Id}" Click="RemoveTargetGrid_Click"
Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" VerticalAlignment="Center"/>
</Grid>
</Border>
@ -796,26 +796,32 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" x:Name="AttackTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="攻击力:"/>
<TextBlock Grid.Row="0" Grid.Column="1" x:Name="PhysicalDefTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="物理护甲:"/>
<TextBlock Grid.Row="1" Grid.Column="0" x:Name="MagicResTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="魔法抗性:"/>
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="SpeedTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="行动速度:"/>
<TextBlock Grid.Row="2" Grid.Column="0" x:Name="PrimaryAttrTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="核心属性:"/>
<TextBlock Grid.Row="2" Grid.Column="1" x:Name="StrengthTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="力量:"/>
<TextBlock Grid.Row="3" Grid.Column="0" x:Name="AgilityTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="敏捷:"/>
<TextBlock Grid.Row="3" Grid.Column="1" x:Name="IntellectTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="智力:"/>
<TextBlock Grid.Row="4" Grid.Column="0" x:Name="HpRegenTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="生命回复:"/>
<TextBlock Grid.Row="4" Grid.Column="1" x:Name="MpRegenTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="魔法回复:"/>
<TextBlock Grid.Row="5" Grid.Column="0" x:Name="CritRateTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="暴击率:"/>
<TextBlock Grid.Row="5" Grid.Column="1" x:Name="CritDmgTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="暴击伤害:"/>
<TextBlock Grid.Row="6" Grid.Column="0" x:Name="EvadeRateTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="闪避率:"/>
<TextBlock Grid.Row="6" Grid.Column="1" x:Name="LifestealTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="生命偷取:"/>
<TextBlock Grid.Row="7" Grid.Column="0" x:Name="CdrTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="冷却缩减:"/>
<TextBlock Grid.Row="7" Grid.Column="1" x:Name="AccelCoeffTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="加速系数:"/>
<TextBlock Grid.Row="8" Grid.Column="0" x:Name="PhysPenTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="物理穿透:"/>
<TextBlock Grid.Row="8" Grid.Column="1" x:Name="MagicPenTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="魔法穿透:"/>
<TextBlock Grid.Row="0" Grid.Column="0" x:Name="HpTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="生命值:"/>
<TextBlock Grid.Row="0" Grid.Column="1" x:Name="MpTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="魔法值:"/>
<TextBlock Grid.Row="1" Grid.Column="0" x:Name="AttackTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="攻击力:"/>
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="PhysicalDefTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="物理护甲:"/>
<TextBlock Grid.Row="2" Grid.Column="0" x:Name="MagicResTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="魔法抗性:"/>
<TextBlock Grid.Row="2" Grid.Column="1" x:Name="SpeedTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="行动速度:"/>
<TextBlock Grid.Row="3" Grid.Column="0" x:Name="PrimaryAttrTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="核心属性:"/>
<TextBlock Grid.Row="3" Grid.Column="1" x:Name="StrengthTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="力量:"/>
<TextBlock Grid.Row="4" Grid.Column="0" x:Name="AgilityTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="敏捷:"/>
<TextBlock Grid.Row="4" Grid.Column="1" x:Name="IntellectTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="智力:"/>
<TextBlock Grid.Row="5" Grid.Column="0" x:Name="HpRegenTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="生命回复:"/>
<TextBlock Grid.Row="5" Grid.Column="1" x:Name="MpRegenTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="魔法回复:"/>
<TextBlock Grid.Row="6" Grid.Column="0" x:Name="CritRateTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="暴击率:"/>
<TextBlock Grid.Row="6" Grid.Column="1" x:Name="CritDmgTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="暴击伤害:"/>
<TextBlock Grid.Row="7" Grid.Column="0" x:Name="EvadeRateTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="闪避率:"/>
<TextBlock Grid.Row="7" Grid.Column="1" x:Name="LifestealTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="生命偷取:"/>
<TextBlock Grid.Row="8" Grid.Column="0" x:Name="CdrTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="冷却缩减:"/>
<TextBlock Grid.Row="8" Grid.Column="1" x:Name="AccelCoeffTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="加速系数:"/>
<TextBlock Grid.Row="9" Grid.Column="0" x:Name="PhysPenTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="物理穿透:"/>
<TextBlock Grid.Row="9" Grid.Column="1" x:Name="MagicPenTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="魔法穿透:"/>
<TextBlock Grid.Row="10" Grid.Column="0" x:Name="MovTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="移动距离:"/>
<TextBlock Grid.Row="10" Grid.Column="1" x:Name="AtrTextBlock" Style="{StaticResource CharacterAttributeTextStyle}" Text="攻击距离:"/>
</Grid>
</Border>
</StackPanel>

View File

@ -1,4 +1,5 @@
using System.Collections.ObjectModel;
using System.Buffers.Text;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading.Tasks;
using System.Windows;
@ -12,6 +13,7 @@ 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.OshimaModules.Effects.OpenEffects;
using static Milimoe.FunGame.Core.Library.Constant.General;
using Brush = System.Windows.Media.Brush;
using Brushes = System.Windows.Media.Brushes;
@ -499,7 +501,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
Border characterBorder = new()
{
Style = (Style)this.FindResource("CharacterIconStyle"),
ToolTip = character.GetInfo(),
ToolTip = character.GetInfo(showMapRelated: true),
IsHitTestVisible = true // 确保角色图标可以被点击
};
@ -709,7 +711,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
TextBlock effectText = new()
{
Style = (Style)this.FindResource("StatusIconTextStyle"),
Text = effect.GetType().Name.Length > 0 ? effect.GetType().Name[0].ToString().ToUpper() : "?"
Text = effect.Name.Length > 0 ? effect.Name[0].ToString() : "?"
};
effectBorder.Child = effectText;
effectBorder.Tag = effect; // 存储Effect对象以便点击时获取其描述
@ -725,6 +727,16 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
// --- 更新其他角色属性 ---
bool showGrowth = false; // 假设不显示成长值
double exHP = character.ExHP + character.ExHP2 + character.ExHP3;
List<string> shield = [];
if (character.Shield.TotalPhysical > 0) shield.Add($"物理:{character.Shield.TotalPhysical:0.##}");
if (character.Shield.TotalMagical > 0) shield.Add($"魔法:{character.Shield.TotalMagical:0.##}");
if (character.Shield.TotalMix > 0) shield.Add($"混合:{character.Shield.TotalMix:0.##}");
HpTextBlock.Text = $"生命值:{character.HP:0.##} / {character.MaxHP:0.##}" + (exHP != 0 ? $" [{character.BaseHP:0.##} {(exHP >= 0 ? "+" : "-")} {Math.Abs(exHP):0.##}]" : "") + (shield.Count > 0 ? $"{string.Join("", shield)}" : "");
double exMP = character.ExMP + character.ExMP2 + character.ExMP3;
MpTextBlock.Text = $"魔法值:{character.MP:0.##} / {character.MaxMP:0.##}" + (exMP != 0 ? $" [{character.BaseMP:0.##} {(exMP >= 0 ? "+" : "-")} {Math.Abs(exMP):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.##}]" : "");
@ -759,6 +771,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
AccelCoeffTextBlock.Text = $"加速系数:{character.AccelerationCoefficient * 100:0.##}%";
PhysPenTextBlock.Text = $"物理穿透:{character.PhysicalPenetration * 100:0.##}%";
MagicPenTextBlock.Text = $"魔法穿透:{character.MagicalPenetration * 100:0.##}%";
MovTextBlock.Text = $"移动距离:{character.MOV}";
AtrTextBlock.Text = $"攻击距离:{character.ATR}";
}
else
{
@ -903,6 +917,12 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
GridCharactersInfoItemsControl.ItemsSource = grid.Characters;
GridInfoPanel.Visibility = Visibility.Visible; // 显示格子信息面板
if (grid.Characters.Count > 0)
{
_potentialTargetGridForSelection = CurrentGameMap.GetGridsByRange(grid, grid.Characters.First().MOV, true);
UpdateGridHighlights();
}
}
/// <summary>
@ -1060,8 +1080,10 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
/// </summary>
private void CloseGridInfoButton_Click(object sender, RoutedEventArgs e)
{
_potentialTargetGridForSelection = [];
GridInfoPanel.Visibility = Visibility.Collapsed;
ClearGridHighlights(); // 关闭时清除格子高亮
UpdateGridHighlights();
// 关闭格子信息面板时如果CurrentCharacter不是PlayerCharacter可以考虑将其设置回PlayerCharacter
if (CurrentCharacter != PlayerCharacter)
{
@ -1152,7 +1174,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
clickedBorder.BorderThickness = new Thickness(1.5);
SetRichTextBoxText(DescriptionRichTextBox, effect.ToString());
_ = AppendDebugLog($"查看状态: {effect.GetType().Name}");
_ = AppendDebugLog($"查看状态: {effect.Name}");
}
}
@ -1399,7 +1421,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
// Tag 现在是 Character 对象
if (sender is Border border && border.Tag is Character hoveredCharacter)
{
string details = hoveredCharacter.GetInfo();
string details = hoveredCharacter.GetInfo(showMapRelated: true);
SetRichTextBoxText(CharacterDetailsRichTextBox, details);
}
}
@ -1577,12 +1599,14 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
/// <param name="selectable">所有可选择的目标列表。</param>
/// <param name="enemys">所有可选敌方目标列表。</param>
/// <param name="teammates">所有可选友方目标列表。</param>
/// <param name="range">所有可选友方目标列表。</param>
/// <param name="callback">选择完成后调用的回调函数。</param>
public void ShowTargetSelectionUI(Character character, ISkill skill, List<Character> selectable, List<Character> enemys, List<Character> teammates, Action<List<Character>> callback)
public void ShowTargetSelectionUI(Character character, ISkill skill, List<Character> selectable, List<Character> enemys, List<Character> teammates, List<Grid> range, Action<List<Character>> callback)
{
_resolveTargetSelection = callback;
_actingCharacterForTargetSelection = character;
_potentialTargetsForSelection = selectable;
_potentialTargetGridForSelection = range;
_maxTargetsForSelection = skill.CanSelectTargetCount;
_canSelectAllTeammates = skill.SelectAllTeammates;
_canSelectAllEnemies = skill.SelectAllEnemies;
@ -1620,6 +1644,8 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
// 更新地图上角色的高亮,以显示潜在目标和已选目标
UpdateCharacterHighlights();
// 更新地图上格子的高亮
UpdateGridHighlights();
}
/// <summary>
@ -1797,6 +1823,22 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
}
}
/// <summary>
/// 从已选目标列表中移除一个格子。
/// </summary>
private void RemoveTargetGrid_Click(object sender, RoutedEventArgs e)
{
if (sender is Button button && button.Tag is long gid)
{
Grid? gridToRemove = SelectedTargetGrid.FirstOrDefault(g => g.Id == gid);
if (gridToRemove != null)
{
SelectedTargetGrid.Remove(gridToRemove);
UpdateGridHighlights();
}
}
}
/// <summary>
/// 确认目标选择的点击事件。
/// </summary>
@ -1841,7 +1883,9 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
_actingCharacterForTargetSelection = null;
_potentialTargetsForSelection = [];
_resolveTargetSelection = null;
_potentialTargetGridForSelection = [];
UpdateCharacterHighlights(); // 清除所有高亮
UpdateGridHighlights();
}
/// <summary>
@ -1856,6 +1900,7 @@ namespace Milimoe.FunGame.Testing.Desktop.GameMapTesting
_potentialTargetGridForSelection = [];
_resolveTargetGridSelection = null;
UpdateGridHighlights();
CloseGridInfoButton_Click(new(), new());
}
/// <summary>

View File

@ -1,6 +1,7 @@
using System.Text;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
using Oshima.Core.Constant;
@ -65,6 +66,12 @@ namespace Milimoe.FunGame.Testing.Tests
Level = slevel
};
c.Skills.Add();
foreach (Skill skillLoop in FunGameConstant.CommonPassiveSkills)
{
Skill passive = skillLoop.Copy();
passive.Level = 1;
c.Skills.Add(passive);
}
characters.Add(c);
}
@ -423,7 +430,7 @@ namespace Milimoe.FunGame.Testing.Tests
return true;
}
private static async Task<List<Character>> GamingQueue_SelectNormalAttackTargets(GamingQueue queue, Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates)
private static async Task<List<Character>> GamingQueue_SelectNormalAttackTargets(GamingQueue queue, Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates, List<Grid> attackRange)
{
List<Character> characters = [];
if (IsPlayer_OnlyTest(queue, character))
@ -525,7 +532,7 @@ namespace Milimoe.FunGame.Testing.Tests
return item;
}
private static async Task<List<Character>> GamingQueue_SelectSkillTargets(GamingQueue queue, Character caster, Skill skill, List<Character> enemys, List<Character> teammates)
private static async Task<List<Character>> GamingQueue_SelectSkillTargets(GamingQueue queue, Character caster, Skill skill, List<Character> enemys, List<Character> teammates, List<Grid> castRange)
{
List<Character> characters = [];
if (IsPlayer_OnlyTest(queue, caster))