mirror of
https://github.com/project-redbud/FunGame-Core.git
synced 2026-01-19 14:08:22 +00:00
插队机制优化;AI移动决策优化
This commit is contained in:
parent
2c39f46dd9
commit
ad4a9a4346
@ -21,9 +21,12 @@ namespace Milimoe.FunGame.Core.Controller
|
|||||||
/// <param name="availableItems">角色所有可用的物品(已过滤CD和EP/MP)。</param>
|
/// <param name="availableItems">角色所有可用的物品(已过滤CD和EP/MP)。</param>
|
||||||
/// <param name="allEnemysInGame">场上所有敌人。</param>
|
/// <param name="allEnemysInGame">场上所有敌人。</param>
|
||||||
/// <param name="allTeammatesInGame">场上所有队友。</param>
|
/// <param name="allTeammatesInGame">场上所有队友。</param>
|
||||||
|
/// <param name="selectableEnemys">场上能够选取的敌人。</param>
|
||||||
|
/// <param name="selectableTeammates">场上能够选取的队友。</param>
|
||||||
/// <returns>包含最佳行动的AIDecision对象。</returns>
|
/// <returns>包含最佳行动的AIDecision对象。</returns>
|
||||||
public async Task<AIDecision> DecideAIActionAsync(Character character, Grid startGrid, List<Grid> allPossibleMoveGrids,
|
public async Task<AIDecision> DecideAIActionAsync(Character character, Grid startGrid, List<Grid> allPossibleMoveGrids,
|
||||||
List<Skill> availableSkills, List<Item> availableItems, List<Character> allEnemysInGame, List<Character> allTeammatesInGame)
|
List<Skill> availableSkills, List<Item> availableItems, List<Character> allEnemysInGame, List<Character> allTeammatesInGame,
|
||||||
|
List<Character> selectableEnemys, List<Character> selectableTeammates)
|
||||||
{
|
{
|
||||||
// 初始化一个默认的“结束回合”决策作为基准
|
// 初始化一个默认的“结束回合”决策作为基准
|
||||||
AIDecision bestDecision = new()
|
AIDecision bestDecision = new()
|
||||||
@ -47,10 +50,10 @@ namespace Milimoe.FunGame.Core.Controller
|
|||||||
List<Grid> normalAttackReachableGrids = _map.GetGridsByRange(potentialMoveGrid, character.ATR, true);
|
List<Grid> normalAttackReachableGrids = _map.GetGridsByRange(potentialMoveGrid, character.ATR, true);
|
||||||
|
|
||||||
List<Character> normalAttackReachableEnemys = [.. allEnemysInGame
|
List<Character> normalAttackReachableEnemys = [.. allEnemysInGame
|
||||||
.Where(c => normalAttackReachableGrids.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable)
|
.Where(c => normalAttackReachableGrids.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable && selectableEnemys.Contains(c))
|
||||||
.Distinct()];
|
.Distinct()];
|
||||||
List<Character> normalAttackReachableTeammates = [.. allTeammatesInGame
|
List<Character> normalAttackReachableTeammates = [.. allTeammatesInGame
|
||||||
.Where(c => normalAttackReachableGrids.SelectMany(g => g.Characters).Contains(c))
|
.Where(c => normalAttackReachableGrids.SelectMany(g => g.Characters).Contains(c) && selectableTeammates.Contains(c))
|
||||||
.Distinct()];
|
.Distinct()];
|
||||||
|
|
||||||
if (normalAttackReachableEnemys.Count > 0)
|
if (normalAttackReachableEnemys.Count > 0)
|
||||||
@ -83,10 +86,10 @@ namespace Milimoe.FunGame.Core.Controller
|
|||||||
List<Grid> skillReachableGrids = _map.GetGridsByRange(potentialMoveGrid, skill.CastRange, true);
|
List<Grid> skillReachableGrids = _map.GetGridsByRange(potentialMoveGrid, skill.CastRange, true);
|
||||||
|
|
||||||
List<Character> skillReachableEnemys = [.. allEnemysInGame
|
List<Character> skillReachableEnemys = [.. allEnemysInGame
|
||||||
.Where(c => skillReachableGrids.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable)
|
.Where(c => skillReachableGrids.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable && selectableEnemys.Contains(c))
|
||||||
.Distinct()];
|
.Distinct()];
|
||||||
List<Character> skillReachableTeammates = [.. allTeammatesInGame
|
List<Character> skillReachableTeammates = [.. allTeammatesInGame
|
||||||
.Where(c => skillReachableGrids.SelectMany(g => g.Characters).Contains(c))
|
.Where(c => skillReachableGrids.SelectMany(g => g.Characters).Contains(c) && selectableTeammates.Contains(c))
|
||||||
.Distinct()];
|
.Distinct()];
|
||||||
|
|
||||||
// 检查是否有可用的目标(敌人或队友,取决于技能类型)
|
// 检查是否有可用的目标(敌人或队友,取决于技能类型)
|
||||||
@ -123,10 +126,10 @@ namespace Milimoe.FunGame.Core.Controller
|
|||||||
List<Grid> itemSkillReachableGrids = _map.GetGridsByRange(potentialMoveGrid, itemSkill.CastRange, true);
|
List<Grid> itemSkillReachableGrids = _map.GetGridsByRange(potentialMoveGrid, itemSkill.CastRange, true);
|
||||||
|
|
||||||
List<Character> itemSkillReachableEnemys = [.. allEnemysInGame
|
List<Character> itemSkillReachableEnemys = [.. allEnemysInGame
|
||||||
.Where(c => itemSkillReachableGrids.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable)
|
.Where(c => itemSkillReachableGrids.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable && selectableEnemys.Contains(c))
|
||||||
.Distinct()];
|
.Distinct()];
|
||||||
List<Character> itemSkillReachableTeammates = [.. allTeammatesInGame
|
List<Character> itemSkillReachableTeammates = [.. allTeammatesInGame
|
||||||
.Where(c => itemSkillReachableGrids.SelectMany(g => g.Characters).Contains(c))
|
.Where(c => itemSkillReachableGrids.SelectMany(g => g.Characters).Contains(c) && selectableTeammates.Contains(c))
|
||||||
.Distinct()];
|
.Distinct()];
|
||||||
|
|
||||||
// 检查是否有可用的目标
|
// 检查是否有可用的目标
|
||||||
@ -176,7 +179,7 @@ namespace Milimoe.FunGame.Core.Controller
|
|||||||
}
|
}
|
||||||
List<Grid> tempAllReachableGridsForPureMove = [.. tempAttackGridsForPureMove.Union(tempCastGridsForPureMove).Distinct()];
|
List<Grid> tempAllReachableGridsForPureMove = [.. tempAttackGridsForPureMove.Union(tempCastGridsForPureMove).Distinct()];
|
||||||
List<Character> tempCurrentReachableEnemysForPureMove = [.. allEnemysInGame
|
List<Character> tempCurrentReachableEnemysForPureMove = [.. allEnemysInGame
|
||||||
.Where(c => tempAllReachableGridsForPureMove.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable)
|
.Where(c => tempAllReachableGridsForPureMove.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable && selectableEnemys.Contains(c))
|
||||||
.Distinct()];
|
.Distinct()];
|
||||||
|
|
||||||
// 如果当前位置无法攻击任何敌人,但地图上还有敌人,尝试向最近的敌人移动
|
// 如果当前位置无法攻击任何敌人,但地图上还有敌人,尝试向最近的敌人移动
|
||||||
|
|||||||
@ -93,7 +93,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否无视施法距离(全图施法),魔法默认为 true,战技默认为 false
|
/// 是否无视施法距离(全图施法),魔法默认为 true,战技默认为 false
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CastAnywhere { get; set; } = false;
|
public virtual bool CastAnywhere { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 施法距离 [ 单位:格 ]
|
/// 施法距离 [ 单位:格 ]
|
||||||
|
|||||||
@ -114,6 +114,29 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<int, List<Skill>> RoundRewards => _roundRewards;
|
public Dictionary<int, List<Skill>> RoundRewards => _roundRewards;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否使用插队保护机制
|
||||||
|
/// </summary>
|
||||||
|
public bool UseQueueProtected { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 插队保护机制检查的最多被插队次数:-1 为默认,即队列长度,最少为 5;0 为不保护
|
||||||
|
/// </summary>
|
||||||
|
public int MaxCutQueueTimes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!UseQueueProtected || _maxCutQueueTimes == 0) return 0;
|
||||||
|
else if (_maxCutQueueTimes == -1) return Math.Max(5, _queue.Count);
|
||||||
|
else if (_maxCutQueueTimes < 5) return 5;
|
||||||
|
else return _maxCutQueueTimes;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_maxCutQueueTimes = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 自定义数据
|
/// 自定义数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -238,6 +261,11 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected Func<long, Dictionary<string, object>> _factoryRoundRewardEffects = id => [];
|
protected Func<long, Dictionary<string, object>> _factoryRoundRewardEffects = id => [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最多被插队次数,-1 为默认,即队列长度,最少为 5
|
||||||
|
/// </summary>
|
||||||
|
protected int _maxCutQueueTimes = 5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 游戏是否结束
|
/// 游戏是否结束
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -472,8 +500,9 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
if (isCheckProtected)
|
if (isCheckProtected)
|
||||||
{
|
{
|
||||||
// 查找保护条件 被插队超过此次数便能获得插队补偿 即行动保护
|
// 查找保护条件 被插队超过此次数便能获得插队补偿 即行动保护
|
||||||
int countProtected = Math.Max(5, _queue.Count);
|
int countProtected = MaxCutQueueTimes;
|
||||||
|
if (countProtected > 0)
|
||||||
|
{
|
||||||
// 查找队列中是否有满足插队补偿条件的角色(最后一个)
|
// 查找队列中是否有满足插队补偿条件的角色(最后一个)
|
||||||
var list = _queue
|
var list = _queue
|
||||||
.Select((c, index) => new { Character = c, Index = index })
|
.Select((c, index) => new { Character = c, Index = index })
|
||||||
@ -518,6 +547,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 如果插入索引无效(为-1 或 大于等于队列长度),则添加到队列尾部
|
// 如果插入索引无效(为-1 或 大于等于队列长度),则添加到队列尾部
|
||||||
if (insertIndex == -1 || insertIndex >= _queue.Count)
|
if (insertIndex == -1 || insertIndex >= _queue.Count)
|
||||||
@ -815,7 +845,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
|
|
||||||
// 队友列表
|
// 队友列表
|
||||||
List<Character> allTeammates = GetTeammates(character);
|
List<Character> allTeammates = GetTeammates(character);
|
||||||
List<Character> selecableTeammates = [.. allTeammates.Where(_queue.Contains)];
|
List<Character> selectableTeammates = [.. allTeammates.Where(_queue.Contains)];
|
||||||
|
|
||||||
// 敌人列表
|
// 敌人列表
|
||||||
List<Character> allEnemys = [.. _allCharacters.Where(c => c != character && !allTeammates.Contains(c))];
|
List<Character> allEnemys = [.. _allCharacters.Where(c => c != character && !allTeammates.Contains(c))];
|
||||||
@ -831,7 +861,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
|
|
||||||
// 回合开始事件,允许事件返回 false 接管回合操作
|
// 回合开始事件,允许事件返回 false 接管回合操作
|
||||||
// 如果事件全程接管回合操作,需要注意触发特效
|
// 如果事件全程接管回合操作,需要注意触发特效
|
||||||
if (!await OnTurnStartAsync(character, selectableEnemys, selecableTeammates, skills, items))
|
if (!await OnTurnStartAsync(character, selectableEnemys, selectableTeammates, skills, items))
|
||||||
{
|
{
|
||||||
_isInRound = false;
|
_isInRound = false;
|
||||||
return _isGameEnd;
|
return _isGameEnd;
|
||||||
@ -839,13 +869,13 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
|
|
||||||
foreach (Skill skillTurnStart in skills)
|
foreach (Skill skillTurnStart in skills)
|
||||||
{
|
{
|
||||||
skillTurnStart.OnTurnStart(character, selectableEnemys, selecableTeammates, skills, items);
|
skillTurnStart.OnTurnStart(character, selectableEnemys, selectableTeammates, skills, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Effect> effects = [.. character.Effects.Where(e => e.IsInEffect)];
|
List<Effect> effects = [.. character.Effects.Where(e => e.IsInEffect)];
|
||||||
foreach (Effect effect in effects)
|
foreach (Effect effect in effects)
|
||||||
{
|
{
|
||||||
effect.OnTurnStart(character, selectableEnemys, selecableTeammates, skills, items);
|
effect.OnTurnStart(character, selectableEnemys, selectableTeammates, skills, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 此变量用于在取消选择时,能够重新行动
|
// 此变量用于在取消选择时,能够重新行动
|
||||||
@ -919,13 +949,13 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
enemys = [.. selectableEnemys.Where(canAttackGrids.Union(canCastGrids).SelectMany(g => g.Characters).Contains)];
|
enemys = [.. selectableEnemys.Where(canAttackGrids.Union(canCastGrids).SelectMany(g => g.Characters).Contains)];
|
||||||
teammates = [.. selecableTeammates.Where(canAttackGrids.Union(canCastGrids).SelectMany(g => g.Characters).Contains)];
|
teammates = [.. selectableTeammates.Where(canAttackGrids.Union(canCastGrids).SelectMany(g => g.Characters).Contains)];
|
||||||
willMoveGridWithSkill = [.. canMoveGrids.Where(g => canAttackGrids.Union(canCastGrids).Contains(g))];
|
willMoveGridWithSkill = [.. canMoveGrids.Where(g => canAttackGrids.Union(canCastGrids).Contains(g))];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
enemys = selectableEnemys;
|
enemys = selectableEnemys;
|
||||||
teammates = selecableTeammates;
|
teammates = selectableTeammates;
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI 决策结果(适用于启用战棋地图的情况)
|
// AI 决策结果(适用于启用战棋地图的情况)
|
||||||
@ -1020,7 +1050,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
}
|
}
|
||||||
else if (character.CharacterState == CharacterState.BattleRestricted)
|
else if (character.CharacterState == CharacterState.BattleRestricted)
|
||||||
{
|
{
|
||||||
// 战斗不能,只能使用物品
|
// 战斗不能,只能对自己使用物品
|
||||||
enemys.Clear();
|
enemys.Clear();
|
||||||
teammates.Clear();
|
teammates.Clear();
|
||||||
skills.Clear();
|
skills.Clear();
|
||||||
@ -1063,7 +1093,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
// 启用战棋地图时的专属 AI 决策方法
|
// 启用战棋地图时的专属 AI 决策方法
|
||||||
if (isAI && ai != null && startGrid != null)
|
if (isAI && ai != null && startGrid != null)
|
||||||
{
|
{
|
||||||
aiDecision = await ai.DecideAIActionAsync(character, startGrid, canMoveGrids, skills, items, allEnemys, allTeammates);
|
aiDecision = await ai.DecideAIActionAsync(character, startGrid, canMoveGrids, skills, items, allEnemys, allTeammates, enemys, teammates);
|
||||||
type = aiDecision.ActionType;
|
type = aiDecision.ActionType;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1115,10 +1145,14 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
}
|
}
|
||||||
moved = await CharacterMoveAsync(character, target, startGrid);
|
moved = await CharacterMoveAsync(character, target, startGrid);
|
||||||
}
|
}
|
||||||
if (isAI && aiDecision != null && cancelTimes == 0)
|
if (isAI && (aiDecision?.IsPureMove ?? false))
|
||||||
{
|
{
|
||||||
// 取消 AI 的移动
|
// 取消 AI 的移动
|
||||||
|
SetOnlyMoveHardnessTime(character, ref baseTime);
|
||||||
type = CharacterActionType.EndTurn;
|
type = CharacterActionType.EndTurn;
|
||||||
|
decided = true;
|
||||||
|
WriteLine($"[ {character} ] 结束了回合!");
|
||||||
|
await OnCharacterDoNothingAsync(character);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type == CharacterActionType.NormalAttack)
|
else if (type == CharacterActionType.NormalAttack)
|
||||||
@ -1439,14 +1473,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
}
|
}
|
||||||
else if (type == CharacterActionType.EndTurn)
|
else if (type == CharacterActionType.EndTurn)
|
||||||
{
|
{
|
||||||
baseTime += 3;
|
SetOnlyMoveHardnessTime(character, ref baseTime);
|
||||||
if (character.CharacterState == CharacterState.NotActionable ||
|
|
||||||
character.CharacterState == CharacterState.ActionRestricted ||
|
|
||||||
character.CharacterState == CharacterState.BattleRestricted)
|
|
||||||
{
|
|
||||||
baseTime += 3;
|
|
||||||
WriteLine($"[ {character} ] {CharacterSet.GetCharacterState(character.CharacterState)},放弃行动将额外获得 3 {GameplayEquilibriumConstant.InGameTime}硬直时间!");
|
|
||||||
}
|
|
||||||
decided = true;
|
decided = true;
|
||||||
WriteLine($"[ {character} ] 结束了回合!");
|
WriteLine($"[ {character} ] 结束了回合!");
|
||||||
await OnCharacterDoNothingAsync(character);
|
await OnCharacterDoNothingAsync(character);
|
||||||
@ -2937,6 +2964,23 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对角色设置仅移动的硬直时间
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character"></param>
|
||||||
|
/// <param name="baseTime"></param>
|
||||||
|
public void SetOnlyMoveHardnessTime(Character character, ref double baseTime)
|
||||||
|
{
|
||||||
|
baseTime += 3;
|
||||||
|
if (character.CharacterState == CharacterState.NotActionable ||
|
||||||
|
character.CharacterState == CharacterState.ActionRestricted ||
|
||||||
|
character.CharacterState == CharacterState.BattleRestricted)
|
||||||
|
{
|
||||||
|
baseTime += 3;
|
||||||
|
WriteLine($"[ {character} ] {CharacterSet.GetCharacterState(character.CharacterState)},放弃行动将额外获得 3 {GameplayEquilibriumConstant.InGameTime}硬直时间!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 回合奖励
|
#region 回合奖励
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user