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="allEnemysInGame">场上所有敌人。</param>
|
||||
/// <param name="allTeammatesInGame">场上所有队友。</param>
|
||||
/// <param name="selectableEnemys">场上能够选取的敌人。</param>
|
||||
/// <param name="selectableTeammates">场上能够选取的队友。</param>
|
||||
/// <returns>包含最佳行动的AIDecision对象。</returns>
|
||||
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()
|
||||
@ -47,10 +50,10 @@ namespace Milimoe.FunGame.Core.Controller
|
||||
List<Grid> normalAttackReachableGrids = _map.GetGridsByRange(potentialMoveGrid, character.ATR, true);
|
||||
|
||||
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()];
|
||||
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()];
|
||||
|
||||
if (normalAttackReachableEnemys.Count > 0)
|
||||
@ -83,10 +86,10 @@ namespace Milimoe.FunGame.Core.Controller
|
||||
List<Grid> skillReachableGrids = _map.GetGridsByRange(potentialMoveGrid, skill.CastRange, true);
|
||||
|
||||
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()];
|
||||
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()];
|
||||
|
||||
// 检查是否有可用的目标(敌人或队友,取决于技能类型)
|
||||
@ -123,10 +126,10 @@ namespace Milimoe.FunGame.Core.Controller
|
||||
List<Grid> itemSkillReachableGrids = _map.GetGridsByRange(potentialMoveGrid, itemSkill.CastRange, true);
|
||||
|
||||
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()];
|
||||
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()];
|
||||
|
||||
// 检查是否有可用的目标
|
||||
@ -176,7 +179,7 @@ namespace Milimoe.FunGame.Core.Controller
|
||||
}
|
||||
List<Grid> tempAllReachableGridsForPureMove = [.. tempAttackGridsForPureMove.Union(tempCastGridsForPureMove).Distinct()];
|
||||
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()];
|
||||
|
||||
// 如果当前位置无法攻击任何敌人,但地图上还有敌人,尝试向最近的敌人移动
|
||||
|
||||
@ -93,7 +93,7 @@ namespace Milimoe.FunGame.Core.Entity
|
||||
/// <summary>
|
||||
/// 是否无视施法距离(全图施法),魔法默认为 true,战技默认为 false
|
||||
/// </summary>
|
||||
public bool CastAnywhere { get; set; } = false;
|
||||
public virtual bool CastAnywhere { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 施法距离 [ 单位:格 ]
|
||||
|
||||
@ -114,6 +114,29 @@ namespace Milimoe.FunGame.Core.Model
|
||||
/// </summary>
|
||||
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>
|
||||
@ -238,6 +261,11 @@ namespace Milimoe.FunGame.Core.Model
|
||||
/// </summary>
|
||||
protected Func<long, Dictionary<string, object>> _factoryRoundRewardEffects = id => [];
|
||||
|
||||
/// <summary>
|
||||
/// 最多被插队次数,-1 为默认,即队列长度,最少为 5
|
||||
/// </summary>
|
||||
protected int _maxCutQueueTimes = 5;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏是否结束
|
||||
/// </summary>
|
||||
@ -472,8 +500,9 @@ namespace Milimoe.FunGame.Core.Model
|
||||
if (isCheckProtected)
|
||||
{
|
||||
// 查找保护条件 被插队超过此次数便能获得插队补偿 即行动保护
|
||||
int countProtected = Math.Max(5, _queue.Count);
|
||||
|
||||
int countProtected = MaxCutQueueTimes;
|
||||
if (countProtected > 0)
|
||||
{
|
||||
// 查找队列中是否有满足插队补偿条件的角色(最后一个)
|
||||
var list = _queue
|
||||
.Select((c, index) => new { Character = c, Index = index })
|
||||
@ -518,6 +547,7 @@ namespace Milimoe.FunGame.Core.Model
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果插入索引无效(为-1 或 大于等于队列长度),则添加到队列尾部
|
||||
if (insertIndex == -1 || insertIndex >= _queue.Count)
|
||||
@ -815,7 +845,7 @@ namespace Milimoe.FunGame.Core.Model
|
||||
|
||||
// 队友列表
|
||||
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))];
|
||||
@ -831,7 +861,7 @@ namespace Milimoe.FunGame.Core.Model
|
||||
|
||||
// 回合开始事件,允许事件返回 false 接管回合操作
|
||||
// 如果事件全程接管回合操作,需要注意触发特效
|
||||
if (!await OnTurnStartAsync(character, selectableEnemys, selecableTeammates, skills, items))
|
||||
if (!await OnTurnStartAsync(character, selectableEnemys, selectableTeammates, skills, items))
|
||||
{
|
||||
_isInRound = false;
|
||||
return _isGameEnd;
|
||||
@ -839,13 +869,13 @@ namespace Milimoe.FunGame.Core.Model
|
||||
|
||||
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)];
|
||||
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)];
|
||||
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))];
|
||||
}
|
||||
else
|
||||
{
|
||||
enemys = selectableEnemys;
|
||||
teammates = selecableTeammates;
|
||||
teammates = selectableTeammates;
|
||||
}
|
||||
|
||||
// AI 决策结果(适用于启用战棋地图的情况)
|
||||
@ -1020,7 +1050,7 @@ namespace Milimoe.FunGame.Core.Model
|
||||
}
|
||||
else if (character.CharacterState == CharacterState.BattleRestricted)
|
||||
{
|
||||
// 战斗不能,只能使用物品
|
||||
// 战斗不能,只能对自己使用物品
|
||||
enemys.Clear();
|
||||
teammates.Clear();
|
||||
skills.Clear();
|
||||
@ -1063,7 +1093,7 @@ namespace Milimoe.FunGame.Core.Model
|
||||
// 启用战棋地图时的专属 AI 决策方法
|
||||
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;
|
||||
}
|
||||
else
|
||||
@ -1115,10 +1145,14 @@ namespace Milimoe.FunGame.Core.Model
|
||||
}
|
||||
moved = await CharacterMoveAsync(character, target, startGrid);
|
||||
}
|
||||
if (isAI && aiDecision != null && cancelTimes == 0)
|
||||
if (isAI && (aiDecision?.IsPureMove ?? false))
|
||||
{
|
||||
// 取消 AI 的移动
|
||||
SetOnlyMoveHardnessTime(character, ref baseTime);
|
||||
type = CharacterActionType.EndTurn;
|
||||
decided = true;
|
||||
WriteLine($"[ {character} ] 结束了回合!");
|
||||
await OnCharacterDoNothingAsync(character);
|
||||
}
|
||||
}
|
||||
else if (type == CharacterActionType.NormalAttack)
|
||||
@ -1439,14 +1473,7 @@ namespace Milimoe.FunGame.Core.Model
|
||||
}
|
||||
else if (type == CharacterActionType.EndTurn)
|
||||
{
|
||||
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}硬直时间!");
|
||||
}
|
||||
SetOnlyMoveHardnessTime(character, ref baseTime);
|
||||
decided = true;
|
||||
WriteLine($"[ {character} ] 结束了回合!");
|
||||
await OnCharacterDoNothingAsync(character);
|
||||
@ -2937,6 +2964,23 @@ namespace Milimoe.FunGame.Core.Model
|
||||
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
|
||||
|
||||
#region 回合奖励
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user