mirror of
https://github.com/project-redbud/FunGame-Core.git
synced 2025-12-05 08:09:02 +00:00
支持战棋地图玩法 (#142)
This commit is contained in:
parent
09eea71cb6
commit
2c39f46dd9
322
Controller/AIController.cs
Normal file
322
Controller/AIController.cs
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.Core.Controller
|
||||||
|
{
|
||||||
|
public class AIController(GamingQueue queue, GameMap map)
|
||||||
|
{
|
||||||
|
private readonly GamingQueue _queue = queue;
|
||||||
|
private readonly GameMap _map = map;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AI的核心决策方法,根据当前游戏状态为角色选择最佳行动。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character">当前行动的AI角色。</param>
|
||||||
|
/// <param name="startGrid">角色的起始格子。</param>
|
||||||
|
/// <param name="allPossibleMoveGrids">从起始格子可达的所有移动格子(包括起始格子本身)。</param>
|
||||||
|
/// <param name="availableSkills">角色所有可用的技能(已过滤CD和EP/MP)。</param>
|
||||||
|
/// <param name="availableItems">角色所有可用的物品(已过滤CD和EP/MP)。</param>
|
||||||
|
/// <param name="allEnemysInGame">场上所有敌人。</param>
|
||||||
|
/// <param name="allTeammatesInGame">场上所有队友。</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)
|
||||||
|
{
|
||||||
|
// 初始化一个默认的“结束回合”决策作为基准
|
||||||
|
AIDecision bestDecision = new()
|
||||||
|
{
|
||||||
|
ActionType = CharacterActionType.EndTurn,
|
||||||
|
TargetMoveGrid = startGrid,
|
||||||
|
Targets = [],
|
||||||
|
Score = -1000.0
|
||||||
|
};
|
||||||
|
|
||||||
|
// 遍历所有可能的移动目标格子 (包括起始格子本身)
|
||||||
|
foreach (Grid potentialMoveGrid in allPossibleMoveGrids)
|
||||||
|
{
|
||||||
|
// 计算移动到这个格子的代价(曼哈顿距离)
|
||||||
|
int moveDistance = GameMap.CalculateManhattanDistance(startGrid, potentialMoveGrid);
|
||||||
|
double movePenalty = moveDistance * 0.5; // 每移动一步扣0.5分
|
||||||
|
|
||||||
|
if (CanCharacterNormalAttack(character))
|
||||||
|
{
|
||||||
|
// 计算普通攻击的可达格子
|
||||||
|
List<Grid> normalAttackReachableGrids = _map.GetGridsByRange(potentialMoveGrid, character.ATR, true);
|
||||||
|
|
||||||
|
List<Character> normalAttackReachableEnemys = [.. allEnemysInGame
|
||||||
|
.Where(c => normalAttackReachableGrids.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable)
|
||||||
|
.Distinct()];
|
||||||
|
List<Character> normalAttackReachableTeammates = [.. allTeammatesInGame
|
||||||
|
.Where(c => normalAttackReachableGrids.SelectMany(g => g.Characters).Contains(c))
|
||||||
|
.Distinct()];
|
||||||
|
|
||||||
|
if (normalAttackReachableEnemys.Count > 0)
|
||||||
|
{
|
||||||
|
// 将筛选后的目标列表传递给 SelectTargets
|
||||||
|
List<Character> targets = SelectTargets(character, character.NormalAttack, normalAttackReachableEnemys, normalAttackReachableTeammates);
|
||||||
|
if (targets.Count > 0)
|
||||||
|
{
|
||||||
|
double currentScore = EvaluateNormalAttack(character, targets) - movePenalty;
|
||||||
|
if (currentScore > bestDecision.Score)
|
||||||
|
{
|
||||||
|
bestDecision = new AIDecision
|
||||||
|
{
|
||||||
|
ActionType = CharacterActionType.NormalAttack,
|
||||||
|
TargetMoveGrid = potentialMoveGrid,
|
||||||
|
SkillToUse = character.NormalAttack,
|
||||||
|
Targets = targets,
|
||||||
|
Score = currentScore
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Skill skill in availableSkills)
|
||||||
|
{
|
||||||
|
if (CanCharacterUseSkill(character) && _queue.CheckCanCast(character, skill, out double cost))
|
||||||
|
{
|
||||||
|
// 计算当前技能的可达格子
|
||||||
|
List<Grid> skillReachableGrids = _map.GetGridsByRange(potentialMoveGrid, skill.CastRange, true);
|
||||||
|
|
||||||
|
List<Character> skillReachableEnemys = [.. allEnemysInGame
|
||||||
|
.Where(c => skillReachableGrids.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable)
|
||||||
|
.Distinct()];
|
||||||
|
List<Character> skillReachableTeammates = [.. allTeammatesInGame
|
||||||
|
.Where(c => skillReachableGrids.SelectMany(g => g.Characters).Contains(c))
|
||||||
|
.Distinct()];
|
||||||
|
|
||||||
|
// 检查是否有可用的目标(敌人或队友,取决于技能类型)
|
||||||
|
if (skillReachableEnemys.Count > 0 || skillReachableTeammates.Count > 0)
|
||||||
|
{
|
||||||
|
// 将筛选后的目标列表传递给 SelectTargets
|
||||||
|
List<Character> targets = SelectTargets(character, skill, skillReachableEnemys, skillReachableTeammates);
|
||||||
|
if (targets.Count > 0)
|
||||||
|
{
|
||||||
|
double currentScore = EvaluateSkill(character, skill, targets, cost) - movePenalty;
|
||||||
|
if (currentScore > bestDecision.Score)
|
||||||
|
{
|
||||||
|
bestDecision = new AIDecision
|
||||||
|
{
|
||||||
|
ActionType = CharacterActionType.PreCastSkill,
|
||||||
|
TargetMoveGrid = potentialMoveGrid,
|
||||||
|
SkillToUse = skill,
|
||||||
|
Targets = targets,
|
||||||
|
Score = currentScore
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Item item in availableItems)
|
||||||
|
{
|
||||||
|
if (item.Skills.Active != null && CanCharacterUseItem(character, item) && _queue.CheckCanCast(character, item.Skills.Active, out double cost))
|
||||||
|
{
|
||||||
|
Skill itemSkill = item.Skills.Active;
|
||||||
|
|
||||||
|
// 计算当前物品技能的可达格子
|
||||||
|
List<Grid> itemSkillReachableGrids = _map.GetGridsByRange(potentialMoveGrid, itemSkill.CastRange, true);
|
||||||
|
|
||||||
|
List<Character> itemSkillReachableEnemys = [.. allEnemysInGame
|
||||||
|
.Where(c => itemSkillReachableGrids.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable)
|
||||||
|
.Distinct()];
|
||||||
|
List<Character> itemSkillReachableTeammates = [.. allTeammatesInGame
|
||||||
|
.Where(c => itemSkillReachableGrids.SelectMany(g => g.Characters).Contains(c))
|
||||||
|
.Distinct()];
|
||||||
|
|
||||||
|
// 检查是否有可用的目标
|
||||||
|
if (itemSkillReachableEnemys.Count > 0 || itemSkillReachableTeammates.Count > 0)
|
||||||
|
{
|
||||||
|
// 将筛选后的目标列表传递给 SelectTargets
|
||||||
|
List<Character> targetsForItem = SelectTargets(character, itemSkill, itemSkillReachableEnemys, itemSkillReachableTeammates);
|
||||||
|
if (targetsForItem.Count > 0)
|
||||||
|
{
|
||||||
|
double currentScore = EvaluateItem(character, item, targetsForItem, cost) - movePenalty;
|
||||||
|
if (currentScore > bestDecision.Score)
|
||||||
|
{
|
||||||
|
bestDecision = new AIDecision
|
||||||
|
{
|
||||||
|
ActionType = CharacterActionType.UseItem,
|
||||||
|
TargetMoveGrid = potentialMoveGrid,
|
||||||
|
ItemToUse = item,
|
||||||
|
SkillToUse = itemSkill,
|
||||||
|
Targets = targetsForItem,
|
||||||
|
Score = currentScore
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果从该格子没有更好的行动,但移动本身有价值
|
||||||
|
// 只有当当前最佳决策是“结束回合”或分数很低时,才考虑纯粹的移动。
|
||||||
|
if (potentialMoveGrid != startGrid && bestDecision.Score < 0) // 如果当前最佳决策是负分(即什么都不做)
|
||||||
|
{
|
||||||
|
double pureMoveScore = -movePenalty; // 移动本身有代价
|
||||||
|
|
||||||
|
// 为纯粹移动逻辑重新计算综合可达敌人列表
|
||||||
|
List<Grid> tempAttackGridsForPureMove = _map.GetGridsByRange(potentialMoveGrid, character.ATR, true);
|
||||||
|
List<Grid> tempCastGridsForPureMove = [];
|
||||||
|
foreach (Skill skill in availableSkills)
|
||||||
|
{
|
||||||
|
tempCastGridsForPureMove.AddRange(_map.GetGridsByRange(potentialMoveGrid, skill.CastRange, true));
|
||||||
|
}
|
||||||
|
foreach (Item item in availableItems)
|
||||||
|
{
|
||||||
|
if (item.Skills.Active != null)
|
||||||
|
{
|
||||||
|
tempCastGridsForPureMove.AddRange(_map.GetGridsByRange(potentialMoveGrid, item.Skills.Active.CastRange, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<Grid> tempAllReachableGridsForPureMove = [.. tempAttackGridsForPureMove.Union(tempCastGridsForPureMove).Distinct()];
|
||||||
|
List<Character> tempCurrentReachableEnemysForPureMove = [.. allEnemysInGame
|
||||||
|
.Where(c => tempAllReachableGridsForPureMove.SelectMany(g => g.Characters).Contains(c) && !c.IsUnselectable)
|
||||||
|
.Distinct()];
|
||||||
|
|
||||||
|
// 如果当前位置无法攻击任何敌人,但地图上还有敌人,尝试向最近的敌人移动
|
||||||
|
if (tempCurrentReachableEnemysForPureMove.Count == 0 && allEnemysInGame.Count > 0) // 使用新计算的列表
|
||||||
|
{
|
||||||
|
Character? target = allEnemysInGame
|
||||||
|
.OrderBy(e => GameMap.CalculateManhattanDistance(potentialMoveGrid, _map.GetCharacterCurrentGrid(e)!))
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (target != null)
|
||||||
|
{
|
||||||
|
Grid? nearestEnemyGrid = _map.GetCharacterCurrentGrid(target);
|
||||||
|
if (nearestEnemyGrid != null)
|
||||||
|
{
|
||||||
|
// 奖励靠近敌人
|
||||||
|
pureMoveScore += (10 - GameMap.CalculateManhattanDistance(potentialMoveGrid, nearestEnemyGrid)) * 0.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果纯粹移动比当前最佳(什么都不做)更好
|
||||||
|
if (pureMoveScore > bestDecision.Score)
|
||||||
|
{
|
||||||
|
bestDecision = new AIDecision
|
||||||
|
{
|
||||||
|
ActionType = CharacterActionType.Move,
|
||||||
|
TargetMoveGrid = potentialMoveGrid,
|
||||||
|
Targets = [],
|
||||||
|
Score = pureMoveScore,
|
||||||
|
IsPureMove = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await Task.FromResult(bestDecision);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- AI 决策辅助方法 ---
|
||||||
|
|
||||||
|
// 检查角色是否能进行普通攻击(基于状态)
|
||||||
|
private static bool CanCharacterNormalAttack(Character character)
|
||||||
|
{
|
||||||
|
return character.CharacterState != CharacterState.NotActionable &&
|
||||||
|
character.CharacterState != CharacterState.ActionRestricted &&
|
||||||
|
character.CharacterState != CharacterState.BattleRestricted &&
|
||||||
|
character.CharacterState != CharacterState.AttackRestricted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查角色是否能使用某个技能(基于状态)
|
||||||
|
private static bool CanCharacterUseSkill(Character character)
|
||||||
|
{
|
||||||
|
return character.CharacterState != CharacterState.NotActionable &&
|
||||||
|
character.CharacterState != CharacterState.ActionRestricted &&
|
||||||
|
character.CharacterState != CharacterState.BattleRestricted &&
|
||||||
|
character.CharacterState != CharacterState.SkillRestricted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查角色是否能使用某个物品(基于状态)
|
||||||
|
private static bool CanCharacterUseItem(Character character, Item item)
|
||||||
|
{
|
||||||
|
return character.CharacterState != CharacterState.NotActionable &&
|
||||||
|
(character.CharacterState != CharacterState.ActionRestricted || item.ItemType == ItemType.Consumable) && // 行动受限只能用消耗品
|
||||||
|
character.CharacterState != CharacterState.BattleRestricted;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 选择技能的最佳目标
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character"></param>
|
||||||
|
/// <param name="skill"></param>
|
||||||
|
/// <param name="enemys"></param>
|
||||||
|
/// <param name="teammates"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static List<Character> SelectTargets(Character character, ISkill skill, List<Character> enemys, List<Character> teammates)
|
||||||
|
{
|
||||||
|
List<Character> targets = skill.GetSelectableTargets(character, enemys, teammates);
|
||||||
|
int count = skill.RealCanSelectTargetCount(enemys, teammates);
|
||||||
|
return [.. targets.OrderBy(o => Random.Shared.Next()).Take(count)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 评估普通攻击的价值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character"></param>
|
||||||
|
/// <param name="targets"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static double EvaluateNormalAttack(Character character, List<Character> targets)
|
||||||
|
{
|
||||||
|
double score = 0;
|
||||||
|
foreach (Character target in targets)
|
||||||
|
{
|
||||||
|
double damage = character.NormalAttack.Damage * (1 - target.PDR);
|
||||||
|
score += damage;
|
||||||
|
if (target.HP <= damage) score += 100;
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 评估技能的价值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character"></param>
|
||||||
|
/// <param name="skill"></param>
|
||||||
|
/// <param name="targets"></param>
|
||||||
|
/// <param name="cost"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static double EvaluateSkill(Character character, Skill skill, List<Character> targets, double cost)
|
||||||
|
{
|
||||||
|
double score = 0;
|
||||||
|
score += targets.Sum(t => CalculateTargetValue(t, skill));
|
||||||
|
//score -= cost * 5;
|
||||||
|
//score -= skill.RealCD * 2;
|
||||||
|
//score -= skill.HardnessTime * 2;
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 评估物品的价值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character"></param>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <param name="targets"></param>
|
||||||
|
/// <param name="cost"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static double EvaluateItem(Character character, Item item, List<Character> targets, double cost)
|
||||||
|
{
|
||||||
|
double score = Random.Shared.Next(1000);
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 辅助函数:计算单个目标在某个技能下的价值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target"></param>
|
||||||
|
/// <param name="skill"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static double CalculateTargetValue(Character target, ISkill skill)
|
||||||
|
{
|
||||||
|
double value = Random.Shared.Next(1000);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -219,7 +219,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 最大生命值 = 基础生命值 + 额外生命值 + 额外生命值2 + 额外生命值3
|
/// 最大生命值 = 基础生命值 + 额外生命值 + 额外生命值2 + 额外生命值3
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double MaxHP => BaseHP + ExHP + ExHP2 + ExHP3;
|
public double MaxHP => Math.Max(1, BaseHP + ExHP + ExHP2 + ExHP3);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前生命值 [ 战斗相关 ]
|
/// 当前生命值 [ 战斗相关 ]
|
||||||
@ -238,6 +238,12 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否有魔法值 [ 初始设定 ]
|
||||||
|
/// </summary>
|
||||||
|
[InitRequired]
|
||||||
|
public bool HasMP { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始魔法值 [ 初始设定 ]
|
/// 初始魔法值 [ 初始设定 ]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -272,7 +278,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 最大魔法值 = 基础魔法值 + 额外魔法值 + 额外魔法值2 + 额外魔法值3
|
/// 最大魔法值 = 基础魔法值 + 额外魔法值 + 额外魔法值2 + 额外魔法值3
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double MaxMP => BaseMP + ExMP + ExMP2 + ExMP3;
|
public double MaxMP => Math.Max(1, BaseMP + ExMP + ExMP2 + ExMP3);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前魔法值 [ 战斗相关 ]
|
/// 当前魔法值 [ 战斗相关 ]
|
||||||
@ -749,16 +755,68 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
public double ExCDR { get; set; } = 0;
|
public double ExCDR { get; set; } = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 攻击距离 [ 与技能和物品相关 ] [ 单位:格(半径) ]
|
/// 攻击距离 [ 与武器相关 ] [ 单位:格(半径) ]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[InitOptional]
|
public int ATR
|
||||||
public int ATR { get; set; } = 1;
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int baseATR = 1;
|
||||||
|
if (EquipSlot.Weapon != null)
|
||||||
|
{
|
||||||
|
baseATR = EquipSlot.Weapon.WeaponType switch
|
||||||
|
{
|
||||||
|
WeaponType.OneHandedSword => GameplayEquilibriumConstant.OneHandedSwordAttackRange,
|
||||||
|
WeaponType.TwoHandedSword => GameplayEquilibriumConstant.TwoHandedSwordAttackRange,
|
||||||
|
WeaponType.Bow => GameplayEquilibriumConstant.BowAttackRange,
|
||||||
|
WeaponType.Pistol => GameplayEquilibriumConstant.PistolAttackRange,
|
||||||
|
WeaponType.Rifle => GameplayEquilibriumConstant.RifleAttackRange,
|
||||||
|
WeaponType.DualDaggers => GameplayEquilibriumConstant.DualDaggersAttackRange,
|
||||||
|
WeaponType.Talisman => GameplayEquilibriumConstant.TalismanAttackRange,
|
||||||
|
WeaponType.Staff => GameplayEquilibriumConstant.StaffAttackRange,
|
||||||
|
WeaponType.Polearm => GameplayEquilibriumConstant.PolearmAttackRange,
|
||||||
|
WeaponType.Gauntlet => GameplayEquilibriumConstant.GauntletAttackRange,
|
||||||
|
WeaponType.HiddenWeapon => GameplayEquilibriumConstant.HiddenWeaponAttackRange,
|
||||||
|
_ => baseATR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Math.Max(1, baseATR + ExATR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 额外攻击距离 [ 与技能和物品相关 ] [ 单位:格(半径) ]
|
||||||
|
/// </summary>
|
||||||
|
public int ExATR { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 行动力/可移动距离 [ 与第一定位相关 ] [ 单位:格(半径) ]
|
||||||
|
/// </summary>
|
||||||
|
public int MOV
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int baseMOV = 3;
|
||||||
|
if (EquipSlot.Weapon != null)
|
||||||
|
{
|
||||||
|
baseMOV = FirstRoleType switch
|
||||||
|
{
|
||||||
|
RoleType.Core => GameplayEquilibriumConstant.RoleMOV_Core,
|
||||||
|
RoleType.Vanguard => GameplayEquilibriumConstant.RoleMOV_Vanguard,
|
||||||
|
RoleType.Guardian => GameplayEquilibriumConstant.RoleMOV_Guardian,
|
||||||
|
RoleType.Support => GameplayEquilibriumConstant.RoleMOV_Support,
|
||||||
|
RoleType.Medic => GameplayEquilibriumConstant.RoleMOV_Medic,
|
||||||
|
_ => baseMOV
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Math.Max(1, baseMOV + ExMOV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 行动力/可移动距离 [ 与技能和物品相关 ] [ 单位:格(半径) ]
|
/// 行动力/可移动距离 [ 与技能和物品相关 ] [ 单位:格(半径) ]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[InitOptional]
|
public int ExMOV { get; set; } = 0;
|
||||||
public int MOV { get; set; } = 5;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 暴击率(%) = [ 与敏捷相关 ] + 额外暴击率(%)
|
/// 暴击率(%) = [ 与敏捷相关 ] + 额外暴击率(%)
|
||||||
@ -1388,7 +1446,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// 获取角色的详细信息
|
/// 获取角色的详细信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public string GetInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false)
|
public string GetInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false, bool showMapRelated = false)
|
||||||
{
|
{
|
||||||
StringBuilder builder = new();
|
StringBuilder builder = new();
|
||||||
|
|
||||||
@ -1434,6 +1492,12 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
builder.AppendLine($"魔法消耗减少:{INT * GameplayEquilibriumConstant.INTtoCastMPReduce * 100:0.##}%");
|
builder.AppendLine($"魔法消耗减少:{INT * GameplayEquilibriumConstant.INTtoCastMPReduce * 100:0.##}%");
|
||||||
builder.AppendLine($"能量消耗减少:{INT * GameplayEquilibriumConstant.INTtoCastEPReduce * 100:0.##}%");
|
builder.AppendLine($"能量消耗减少:{INT * GameplayEquilibriumConstant.INTtoCastEPReduce * 100:0.##}%");
|
||||||
|
|
||||||
|
if (showMapRelated)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"移动距离:{MOV}");
|
||||||
|
builder.AppendLine($"攻击距离:{ATR}");
|
||||||
|
}
|
||||||
|
|
||||||
GetStatusInfo(builder);
|
GetStatusInfo(builder);
|
||||||
|
|
||||||
builder.AppendLine("== 普通攻击 ==");
|
builder.AppendLine("== 普通攻击 ==");
|
||||||
@ -1475,7 +1539,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// 获取角色的简略信息
|
/// 获取角色的简略信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public string GetSimpleInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false, bool showBasicOnly = false)
|
public string GetSimpleInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false, bool showBasicOnly = false, bool showMapRelated = false)
|
||||||
{
|
{
|
||||||
StringBuilder builder = new();
|
StringBuilder builder = new();
|
||||||
|
|
||||||
@ -1518,6 +1582,12 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
builder.AppendLine($"生命回复:{HR:0.##}" + (ExHR != 0 ? $" [{InitialHR + STR * GameplayEquilibriumConstant.STRtoHRFactor:0.##} {(ExHR >= 0 ? "+" : "-")} {Math.Abs(ExHR):0.##}]" : ""));
|
builder.AppendLine($"生命回复:{HR:0.##}" + (ExHR != 0 ? $" [{InitialHR + STR * GameplayEquilibriumConstant.STRtoHRFactor:0.##} {(ExHR >= 0 ? "+" : "-")} {Math.Abs(ExHR):0.##}]" : ""));
|
||||||
builder.AppendLine($"魔法回复:{MR:0.##}" + (ExMR != 0 ? $" [{InitialMR + INT * GameplayEquilibriumConstant.INTtoMRFactor:0.##} {(ExMR >= 0 ? "+" : "-")} {Math.Abs(ExMR):0.##}]" : ""));
|
builder.AppendLine($"魔法回复:{MR:0.##}" + (ExMR != 0 ? $" [{InitialMR + INT * GameplayEquilibriumConstant.INTtoMRFactor:0.##} {(ExMR >= 0 ? "+" : "-")} {Math.Abs(ExMR):0.##}]" : ""));
|
||||||
|
|
||||||
|
if (showMapRelated)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"移动距离:{MOV}");
|
||||||
|
builder.AppendLine($"攻击距离:{ATR}");
|
||||||
|
}
|
||||||
|
|
||||||
if (!showBasicOnly)
|
if (!showBasicOnly)
|
||||||
{
|
{
|
||||||
GetStatusInfo(builder);
|
GetStatusInfo(builder);
|
||||||
@ -1685,7 +1755,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// 获取角色的物品信息
|
/// 获取角色的物品信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public string GetItemInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false)
|
public string GetItemInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false, bool showMapRelated = false)
|
||||||
{
|
{
|
||||||
StringBuilder builder = new();
|
StringBuilder builder = new();
|
||||||
|
|
||||||
@ -1731,6 +1801,12 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
builder.AppendLine($"魔法消耗减少:{INT * GameplayEquilibriumConstant.INTtoCastMPReduce * 100:0.##}%");
|
builder.AppendLine($"魔法消耗减少:{INT * GameplayEquilibriumConstant.INTtoCastMPReduce * 100:0.##}%");
|
||||||
builder.AppendLine($"能量消耗减少:{INT * GameplayEquilibriumConstant.INTtoCastEPReduce * 100:0.##}%");
|
builder.AppendLine($"能量消耗减少:{INT * GameplayEquilibriumConstant.INTtoCastEPReduce * 100:0.##}%");
|
||||||
|
|
||||||
|
if (showMapRelated)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"移动距离:{MOV}");
|
||||||
|
builder.AppendLine($"攻击距离:{ATR}");
|
||||||
|
}
|
||||||
|
|
||||||
if (EquipSlot.Any())
|
if (EquipSlot.Any())
|
||||||
{
|
{
|
||||||
builder.AppendLine(GetEquipSlotInfo().Trim());
|
builder.AppendLine(GetEquipSlotInfo().Trim());
|
||||||
@ -2022,8 +2098,8 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
c.MDF = MDF.Copy();
|
c.MDF = MDF.Copy();
|
||||||
c.Lifesteal = Lifesteal;
|
c.Lifesteal = Lifesteal;
|
||||||
c.Shield = Shield.Copy();
|
c.Shield = Shield.Copy();
|
||||||
c.ATR = ATR;
|
c.ExATR = ExATR;
|
||||||
c.MOV = MOV;
|
c.ExMOV = ExMOV;
|
||||||
c.MagicType = MagicType;
|
c.MagicType = MagicType;
|
||||||
c.ImmuneType = ImmuneType;
|
c.ImmuneType = ImmuneType;
|
||||||
}
|
}
|
||||||
@ -2132,8 +2208,8 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
ExActionCoefficient = c.ExActionCoefficient;
|
ExActionCoefficient = c.ExActionCoefficient;
|
||||||
ExAccelerationCoefficient = c.ExAccelerationCoefficient;
|
ExAccelerationCoefficient = c.ExAccelerationCoefficient;
|
||||||
ExCDR = c.ExCDR;
|
ExCDR = c.ExCDR;
|
||||||
ATR = c.ATR;
|
ExATR = c.ExATR;
|
||||||
MOV = c.MOV;
|
ExMOV = c.ExMOV;
|
||||||
ExCritRate = c.ExCritRate;
|
ExCritRate = c.ExCritRate;
|
||||||
ExCritDMG = c.ExCritDMG;
|
ExCritDMG = c.ExCritDMG;
|
||||||
ExEvadeRate = c.ExEvadeRate;
|
ExEvadeRate = c.ExEvadeRate;
|
||||||
|
|||||||
@ -47,7 +47,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// 获取单位的详细信息
|
/// 获取单位的详细信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public new string GetInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false)
|
public new string GetInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false, bool showMapRelated = false)
|
||||||
{
|
{
|
||||||
StringBuilder builder = new();
|
StringBuilder builder = new();
|
||||||
|
|
||||||
@ -78,6 +78,12 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
builder.AppendLine($"物理穿透:{PhysicalPenetration * 100:0.##}%");
|
builder.AppendLine($"物理穿透:{PhysicalPenetration * 100:0.##}%");
|
||||||
builder.AppendLine($"魔法穿透:{MagicalPenetration * 100:0.##}%");
|
builder.AppendLine($"魔法穿透:{MagicalPenetration * 100:0.##}%");
|
||||||
|
|
||||||
|
if (showMapRelated)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"移动距离:{MOV}");
|
||||||
|
builder.AppendLine($"攻击距离:{ATR}");
|
||||||
|
}
|
||||||
|
|
||||||
if (CharacterState != CharacterState.Actionable)
|
if (CharacterState != CharacterState.Actionable)
|
||||||
{
|
{
|
||||||
builder.AppendLine(CharacterSet.GetCharacterState(CharacterState));
|
builder.AppendLine(CharacterSet.GetCharacterState(CharacterState));
|
||||||
@ -174,7 +180,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// 获取单位的简略信息
|
/// 获取单位的简略信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public new string GetSimpleInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false, bool showBasicOnly = false)
|
public new string GetSimpleInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false, bool showBasicOnly = false, bool showMapRelated = false)
|
||||||
{
|
{
|
||||||
StringBuilder builder = new();
|
StringBuilder builder = new();
|
||||||
|
|
||||||
@ -198,6 +204,12 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
builder.AppendLine($"生命回复:{HR:0.##}" + (ExHR != 0 ? $" [{InitialHR + STR * GameplayEquilibriumConstant.STRtoHRFactor:0.##} {(ExHR >= 0 ? "+" : "-")} {Math.Abs(ExHR):0.##}]" : ""));
|
builder.AppendLine($"生命回复:{HR:0.##}" + (ExHR != 0 ? $" [{InitialHR + STR * GameplayEquilibriumConstant.STRtoHRFactor:0.##} {(ExHR >= 0 ? "+" : "-")} {Math.Abs(ExHR):0.##}]" : ""));
|
||||||
builder.AppendLine($"魔法回复:{MR:0.##}" + (ExMR != 0 ? $" [{InitialMR + INT * GameplayEquilibriumConstant.INTtoMRFactor:0.##} {(ExMR >= 0 ? "+" : "-")} {Math.Abs(ExMR):0.##}]" : ""));
|
builder.AppendLine($"魔法回复:{MR:0.##}" + (ExMR != 0 ? $" [{InitialMR + INT * GameplayEquilibriumConstant.INTtoMRFactor:0.##} {(ExMR >= 0 ? "+" : "-")} {Math.Abs(ExMR):0.##}]" : ""));
|
||||||
|
|
||||||
|
if (showMapRelated)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"移动距离:{MOV}");
|
||||||
|
builder.AppendLine($"攻击距离:{ATR}");
|
||||||
|
}
|
||||||
|
|
||||||
if (!showBasicOnly)
|
if (!showBasicOnly)
|
||||||
{
|
{
|
||||||
if (CharacterState != CharacterState.Actionable)
|
if (CharacterState != CharacterState.Actionable)
|
||||||
@ -408,7 +420,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// 获取单位的物品信息
|
/// 获取单位的物品信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public new string GetItemInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false)
|
public new string GetItemInfo(bool showUser = true, bool showGrowth = true, bool showEXP = false, bool showMapRelated = false)
|
||||||
{
|
{
|
||||||
StringBuilder builder = new();
|
StringBuilder builder = new();
|
||||||
|
|
||||||
@ -439,6 +451,12 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
builder.AppendLine($"物理穿透:{PhysicalPenetration * 100:0.##}%");
|
builder.AppendLine($"物理穿透:{PhysicalPenetration * 100:0.##}%");
|
||||||
builder.AppendLine($"魔法穿透:{MagicalPenetration * 100:0.##}%");
|
builder.AppendLine($"魔法穿透:{MagicalPenetration * 100:0.##}%");
|
||||||
|
|
||||||
|
if (showMapRelated)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"移动距离:{MOV}");
|
||||||
|
builder.AppendLine($"攻击距离:{ATR}");
|
||||||
|
}
|
||||||
|
|
||||||
if (EquipSlot.Any())
|
if (EquipSlot.Any())
|
||||||
{
|
{
|
||||||
builder.AppendLine("== 装备栏 ==");
|
builder.AppendLine("== 装备栏 ==");
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
using System.Text;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using Milimoe.FunGame.Core.Api.Utility;
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
using Milimoe.FunGame.Core.Interface.Base;
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
using Milimoe.FunGame.Core.Interface.Entity;
|
using Milimoe.FunGame.Core.Interface.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.Addon;
|
||||||
using Milimoe.FunGame.Core.Library.Constant;
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
|
||||||
namespace Milimoe.FunGame.Core.Entity
|
namespace Milimoe.FunGame.Core.Entity
|
||||||
@ -310,7 +312,13 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
}
|
}
|
||||||
if (result && Skills.Active != null)
|
if (result && Skills.Active != null)
|
||||||
{
|
{
|
||||||
used = await queue.UseItemAsync(this, character, enemys, teammates);
|
List<Grid> castRange = [];
|
||||||
|
if (Skills.Active.GamingQueue != null && Skills.Active.GamingQueue.Map != null)
|
||||||
|
{
|
||||||
|
Grid? grid = Skills.Active.GamingQueue.Map.GetCharacterCurrentGrid(character);
|
||||||
|
castRange = grid is null ? [] : Skills.Active.GamingQueue.Map.GetGridsByRange(grid, Skills.Active.CastRange, true);
|
||||||
|
}
|
||||||
|
used = await queue.UseItemAsync(this, character, enemys, teammates, castRange);
|
||||||
}
|
}
|
||||||
if (used)
|
if (used)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Text;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using Milimoe.FunGame.Core.Api.Utility;
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
using Milimoe.FunGame.Core.Interface.Base;
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
using Milimoe.FunGame.Core.Interface.Entity;
|
using Milimoe.FunGame.Core.Interface.Entity;
|
||||||
@ -560,7 +561,8 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
/// <param name="map"></param>
|
/// <param name="map"></param>
|
||||||
public virtual void BeforeSelectTargetGrid(Character character, List<Character> enemys, List<Character> teammates, GameMap map)
|
/// <param name="moveRange"></param>
|
||||||
|
public virtual void BeforeSelectTargetGrid(Character character, List<Character> enemys, List<Character> teammates, GameMap map, List<Grid> moveRange)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1067,6 +1069,19 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
return GamingQueue?.IsCharacterInAIControlling(character) ?? false;
|
return GamingQueue?.IsCharacterInAIControlling(character) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加角色应用的特效类型到回合记录中
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character"></param>
|
||||||
|
/// <param name="types"></param>
|
||||||
|
public void RecordCharacterApplyEffects(Character character, params List<EffectType> types)
|
||||||
|
{
|
||||||
|
if (GamingQueue?.LastRound.ApplyEffects.TryAdd(character, types) ?? false)
|
||||||
|
{
|
||||||
|
GamingQueue?.LastRound.ApplyEffects[character].AddRange(types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 返回特效详情
|
/// 返回特效详情
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -233,6 +233,11 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double CurrentCD => 0;
|
public double CurrentCD => 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏中的行动顺序表实例,使用时需要判断其是否存在
|
||||||
|
/// </summary>
|
||||||
|
public IGamingQueue? GamingQueue { get; set; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 绑定到特效的普通攻击扩展。键为特效,值为对应的普攻扩展对象。
|
/// 绑定到特效的普通攻击扩展。键为特效,值为对应的普攻扩展对象。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -450,6 +455,7 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
|
|
||||||
builder.AppendLine($"{Name} - 等级 {Level}");
|
builder.AppendLine($"{Name} - 等级 {Level}");
|
||||||
builder.AppendLine($"描述:{Description}");
|
builder.AppendLine($"描述:{Description}");
|
||||||
|
if (GamingQueue?.Map != null) builder.AppendLine($"攻击距离:{Character.ATR}");
|
||||||
builder.AppendLine($"硬直时间:{RealHardnessTime:0.##}{(showOriginal && RealHardnessTime != HardnessTime ? $"(原始值:{HardnessTime})" : "")}");
|
builder.AppendLine($"硬直时间:{RealHardnessTime:0.##}{(showOriginal && RealHardnessTime != HardnessTime ? $"(原始值:{HardnessTime})" : "")}");
|
||||||
|
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
|
|||||||
@ -44,6 +44,21 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
CanSelectTeammate = teammate;
|
CanSelectTeammate = teammate;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "allenemy":
|
||||||
|
case "allenemys":
|
||||||
|
case "allenemies":
|
||||||
|
if (bool.TryParse(args[str].ToString(), out bool allenemy))
|
||||||
|
{
|
||||||
|
SelectAllEnemies = allenemy;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "allteammate":
|
||||||
|
case "allteammates":
|
||||||
|
if (bool.TryParse(args[str].ToString(), out bool allteammate))
|
||||||
|
{
|
||||||
|
SelectAllTeammates = allteammate;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "count":
|
case "count":
|
||||||
if (int.TryParse(args[str].ToString(), out int count) && count > 0)
|
if (int.TryParse(args[str].ToString(), out int count) && count > 0)
|
||||||
{
|
{
|
||||||
@ -56,6 +71,19 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
CanSelectTargetRange = range;
|
CanSelectTargetRange = range;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "nd":
|
||||||
|
case "nondirectional":
|
||||||
|
if (bool.TryParse(args[str].ToString(), out bool nondirectional))
|
||||||
|
{
|
||||||
|
IsNonDirectional = nondirectional;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "rangetype":
|
||||||
|
if (int.TryParse(args[str].ToString(), out int rangetype) && rangetype > 0)
|
||||||
|
{
|
||||||
|
SkillRangeType = (SkillRangeType)rangetype;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "mpcost":
|
case "mpcost":
|
||||||
if (double.TryParse(args[str].ToString(), out double mpcost) && mpcost > 0)
|
if (double.TryParse(args[str].ToString(), out double mpcost) && mpcost > 0)
|
||||||
{
|
{
|
||||||
@ -69,12 +97,14 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "costall":
|
case "costall":
|
||||||
|
case "costallep":
|
||||||
if (bool.TryParse(args[str].ToString(), out bool costall) && costall)
|
if (bool.TryParse(args[str].ToString(), out bool costall) && costall)
|
||||||
{
|
{
|
||||||
CostAllEP = costall;
|
CostAllEP = costall;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "mincost":
|
case "mincost":
|
||||||
|
case "mincostep":
|
||||||
if (double.TryParse(args[str].ToString(), out double mincost) && mincost > 0)
|
if (double.TryParse(args[str].ToString(), out double mincost) && mincost > 0)
|
||||||
{
|
{
|
||||||
MinCostEP = mincost;
|
MinCostEP = mincost;
|
||||||
@ -87,12 +117,28 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "cast":
|
case "cast":
|
||||||
|
case "casttime":
|
||||||
if (double.TryParse(args[str].ToString(), out double cast) && cast > 0)
|
if (double.TryParse(args[str].ToString(), out double cast) && cast > 0)
|
||||||
{
|
{
|
||||||
CastTime = cast;
|
CastTime = cast;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "cr":
|
||||||
|
case "castrange":
|
||||||
|
if (int.TryParse(args[str].ToString(), out int castrange) && castrange > 0)
|
||||||
|
{
|
||||||
|
CastRange = castrange;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "caw":
|
||||||
|
case "castanywhere":
|
||||||
|
if (bool.TryParse(args[str].ToString(), out bool castanywhere))
|
||||||
|
{
|
||||||
|
CastAnywhere = castanywhere;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "ht":
|
case "ht":
|
||||||
|
case "hardnesstime":
|
||||||
if (double.TryParse(args[str].ToString(), out double ht) && ht > 0)
|
if (double.TryParse(args[str].ToString(), out double ht) && ht > 0)
|
||||||
{
|
{
|
||||||
HardnessTime = ht;
|
HardnessTime = ht;
|
||||||
|
|||||||
@ -99,7 +99,11 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// 施法距离 [ 单位:格 ]
|
/// 施法距离 [ 单位:格 ]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[InitOptional]
|
[InitOptional]
|
||||||
public int CastRange { get; set; } = 5;
|
public int CastRange
|
||||||
|
{
|
||||||
|
get => Math.Max(1, CastAnywhere ? (GamingQueue?.Map != null ? GamingQueue.Map.Grids.Count : 999) : _CastRange);
|
||||||
|
set => _CastRange = Math.Max(1, value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 可选取自身
|
/// 可选取自身
|
||||||
@ -136,6 +140,24 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual int CanSelectTargetRange { get; set; } = 0;
|
public virtual int CanSelectTargetRange { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 如果为 true,表示非指向性技能,可以任意选取一个范围(<see cref="CanSelectTargetRange"/> = 0 时为单个格子)。<para/>
|
||||||
|
/// 如果为 false,表示必须选取一个角色作为目标,当 <see cref="CanSelectTargetRange"/> > 0 时,技能作用范围将根据目标位置覆盖 <see cref="SkillRangeType"/> 形状的区域;= 0 时正常选取目标。
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool IsNonDirectional { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 作用范围形状<para/>
|
||||||
|
/// <see cref="SkillRangeType.Diamond"/> - 菱形。默认的曼哈顿距离正方形<para/>
|
||||||
|
/// <see cref="SkillRangeType.Circle"/> - 圆形。基于欧几里得距离的圆形<para/>
|
||||||
|
/// <see cref="SkillRangeType.Square"/> - 正方形<para/>
|
||||||
|
/// <see cref="SkillRangeType.Line"/> - 施法者与目标之前的直线<para/>
|
||||||
|
/// <see cref="SkillRangeType.LinePass"/> - 施法者与目标所在的直线,贯穿至地图边缘<para/>
|
||||||
|
/// <see cref="SkillRangeType.Sector"/> - 扇形<para/>
|
||||||
|
/// 注意,该属性不影响选取目标的范围。选取目标的范围由 <see cref="Library.Common.Addon.GameMap"/> 决定。
|
||||||
|
/// </summary>
|
||||||
|
public virtual SkillRangeType SkillRangeType { get; set; } = SkillRangeType.Diamond;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 选取角色的条件
|
/// 选取角色的条件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -534,6 +556,10 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
{
|
{
|
||||||
builder.AppendLine($"{DispelDescription}");
|
builder.AppendLine($"{DispelDescription}");
|
||||||
}
|
}
|
||||||
|
if (GamingQueue?.Map != null && SkillType != SkillType.Passive)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"施法距离:{(CastAnywhere ? "全图" : CastRange)}");
|
||||||
|
}
|
||||||
if (IsActive && (Item?.IsInGameItem ?? true))
|
if (IsActive && (Item?.IsInGameItem ?? true))
|
||||||
{
|
{
|
||||||
if (SkillType == SkillType.Item)
|
if (SkillType == SkillType.Item)
|
||||||
@ -661,5 +687,10 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
/// 等级
|
/// 等级
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private int _Level = 0;
|
private int _Level = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 施法距离
|
||||||
|
/// </summary>
|
||||||
|
private int _CastRange = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,17 @@
|
|||||||
using Milimoe.FunGame.Core.Interface.Base;
|
namespace Milimoe.FunGame.Core.Entity
|
||||||
|
|
||||||
namespace Milimoe.FunGame.Core.Entity
|
|
||||||
{
|
{
|
||||||
public class Team(string name, IEnumerable<Character> charaters)
|
public class Team(string name, IEnumerable<Character> charaters)
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; } = Guid.Empty;
|
public Guid Id { get; set; } = Guid.Empty;
|
||||||
public string Name { get; set; } = name;
|
public string Name { get; set; } = name;
|
||||||
public List<Character> Members { get; } = new(charaters);
|
public List<Character> Members { get; } = [.. charaters];
|
||||||
public int Score { get; set; } = 0;
|
public int Score { get; set; } = 0;
|
||||||
public bool IsWinner { get; set; } = false;
|
public bool IsWinner { get; set; } = false;
|
||||||
public int Count => Members.Count;
|
public int Count => Members.Count;
|
||||||
|
|
||||||
public List<Character> GetActiveCharacters(IGamingQueue queue)
|
public List<Character> GetActiveCharacters()
|
||||||
{
|
{
|
||||||
return [.. Members.Where(queue.Queue.Contains)];
|
return [.. Members.Where(c => c.HP > 0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Character> GetTeammates(Character character)
|
public List<Character> GetTeammates(Character character)
|
||||||
@ -21,9 +19,9 @@ namespace Milimoe.FunGame.Core.Entity
|
|||||||
return [.. Members.Where(c => c != character)];
|
return [.. Members.Where(c => c != character)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Character> GetActiveTeammates(IGamingQueue queue, Character character)
|
public List<Character> GetActiveTeammates(Character character)
|
||||||
{
|
{
|
||||||
return [.. Members.Where(c => queue.Queue.Contains(c) && c != character)];
|
return [.. Members.Where(c => c.HP > 0 && c != character)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsOnThisTeam(Character character)
|
public bool IsOnThisTeam(Character character)
|
||||||
|
|||||||
@ -25,6 +25,11 @@ namespace Milimoe.FunGame.Core.Interface.Base
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<Guid, Character> Original { get; }
|
public Dictionary<Guid, Character> Original { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参与本次游戏的所有角色列表
|
||||||
|
/// </summary>
|
||||||
|
public List<Character> AllCharacters { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前的行动顺序
|
/// 当前的行动顺序
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -151,8 +156,19 @@ namespace Milimoe.FunGame.Core.Interface.Base
|
|||||||
/// <param name="caster"></param>
|
/// <param name="caster"></param>
|
||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
|
/// <param name="castRange"></param>
|
||||||
|
/// <param name="desiredTargets"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<bool> UseItemAsync(Item item, Character caster, List<Character> enemys, List<Character> teammates);
|
public Task<bool> UseItemAsync(Item item, Character caster, List<Character> enemys, List<Character> teammates, List<Grid> castRange, List<Character>? desiredTargets = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色移动
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character"></param>
|
||||||
|
/// <param name="target"></param>
|
||||||
|
/// <param name="startGrid"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task<bool> CharacterMoveAsync(Character character, Grid target, Grid? startGrid);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 选取移动目标
|
/// 选取移动目标
|
||||||
@ -161,8 +177,9 @@ namespace Milimoe.FunGame.Core.Interface.Base
|
|||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
/// <param name="map"></param>
|
/// <param name="map"></param>
|
||||||
|
/// <param name="moveRange"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<Grid> SelectTargetGridAsync(Character character, List<Character> enemys, List<Character> teammates, GameMap map);
|
public Task<Grid> SelectTargetGridAsync(Character character, List<Character> enemys, List<Character> teammates, GameMap map, List<Grid> moveRange);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 选取技能目标
|
/// 选取技能目标
|
||||||
@ -171,8 +188,9 @@ namespace Milimoe.FunGame.Core.Interface.Base
|
|||||||
/// <param name="skill"></param>
|
/// <param name="skill"></param>
|
||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
|
/// <param name="castRange"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<List<Character>> SelectTargetsAsync(Character caster, Skill skill, List<Character> enemys, List<Character> teammates);
|
public Task<List<Character>> SelectTargetsAsync(Character caster, Skill skill, List<Character> enemys, List<Character> teammates, List<Grid> castRange);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 选取普通攻击目标
|
/// 选取普通攻击目标
|
||||||
@ -181,8 +199,9 @@ namespace Milimoe.FunGame.Core.Interface.Base
|
|||||||
/// <param name="attack"></param>
|
/// <param name="attack"></param>
|
||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
|
/// <param name="attackRange"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<List<Character>> SelectTargetsAsync(Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates);
|
public Task<List<Character>> SelectTargetsAsync(Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates, List<Grid> attackRange);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 判断目标对于某个角色是否是队友
|
/// 判断目标对于某个角色是否是队友
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Milimoe.FunGame.Core.Entity;
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
|
|
||||||
namespace Milimoe.FunGame.Core.Interface.Entity
|
namespace Milimoe.FunGame.Core.Interface.Entity
|
||||||
{
|
{
|
||||||
@ -7,6 +8,11 @@ namespace Milimoe.FunGame.Core.Interface.Entity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISkill : IBaseEntity
|
public interface ISkill : IBaseEntity
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 所属的行动顺序表实例
|
||||||
|
/// </summary>
|
||||||
|
public IGamingQueue? GamingQueue { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 此技能所属的角色
|
/// 此技能所属的角色
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -344,7 +344,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Grid> Gq_SelectTargetGrid(GamingQueue queue, Character character, List<Character> enemys, List<Character> teammates, GameMap map)
|
private async Task<Grid> Gq_SelectTargetGrid(GamingQueue queue, Character character, List<Character> enemys, List<Character> teammates, GameMap map, List<Grid> canMoveGrids)
|
||||||
{
|
{
|
||||||
// 介入选择,假设这里更新界面,让玩家选择目的地
|
// 介入选择,假设这里更新界面,让玩家选择目的地
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
|
|||||||
@ -240,6 +240,50 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
|
|||||||
return grids;
|
return grids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取以某个格子为中心,最远距离的格子(曼哈顿距离),只考虑同一平面的格子。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="grid"></param>
|
||||||
|
/// <param name="range"></param>
|
||||||
|
/// <param name="includeCharacter"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual List<Grid> GetOuterGridsByRange(Grid grid, int range, bool includeCharacter = false)
|
||||||
|
{
|
||||||
|
List<Grid> grids = [];
|
||||||
|
|
||||||
|
if (range < 0)
|
||||||
|
{
|
||||||
|
return grids;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历以中心格子为中心的方形区域
|
||||||
|
// dx和dy的范围从 -range 到 +range
|
||||||
|
for (int dx = -range; dx <= range; ++dx)
|
||||||
|
{
|
||||||
|
for (int dy = -range; dy <= range; ++dy)
|
||||||
|
{
|
||||||
|
// 只有当曼哈顿距离恰好等于 range 时,才认为是最远距离的格子
|
||||||
|
if (Math.Abs(dx) + Math.Abs(dy) == range)
|
||||||
|
{
|
||||||
|
int x = grid.X + dx;
|
||||||
|
int y = grid.Y + dy;
|
||||||
|
int z = grid.Z; // 只考虑同一平面
|
||||||
|
|
||||||
|
// 检查格子是否存在于地图中
|
||||||
|
if (GridsByCoordinate.TryGetValue((x, y, z), out Grid? select) && select != null)
|
||||||
|
{
|
||||||
|
if (includeCharacter || select.Characters.Count == 0)
|
||||||
|
{
|
||||||
|
grids.Add(select);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grids;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取以某个格子为中心,一定半径内的格子(圆形范围,欧几里得距离),只考虑同一平面的格子。
|
/// 获取以某个格子为中心,一定半径内的格子(圆形范围,欧几里得距离),只考虑同一平面的格子。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -254,7 +298,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
|
|||||||
// 预计算半径的平方
|
// 预计算半径的平方
|
||||||
int rangeSquared = range * range;
|
int rangeSquared = range * range;
|
||||||
|
|
||||||
// 遍历以中心格子为中心的方形区域
|
// 遍历以中心格子为中心的区域
|
||||||
// 范围从 -range 到 +range,覆盖所有可能的圆形区域内的格子
|
// 范围从 -range 到 +range,覆盖所有可能的圆形区域内的格子
|
||||||
for (int dx = -range; dx <= range; ++dx)
|
for (int dx = -range; dx <= range; ++dx)
|
||||||
{
|
{
|
||||||
@ -281,6 +325,47 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
|
|||||||
return grids;
|
return grids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取以某个格子为中心,最远距离的格子(圆形范围,欧几里得距离),只考虑同一平面的格子。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="grid"></param>
|
||||||
|
/// <param name="range"></param>
|
||||||
|
/// <param name="includeCharacter"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual List<Grid> GetOuterGridsByCircleRange(Grid grid, int range, bool includeCharacter = false)
|
||||||
|
{
|
||||||
|
List<Grid> grids = [];
|
||||||
|
|
||||||
|
// 预计算半径的平方
|
||||||
|
int rangeSquared = range * range;
|
||||||
|
|
||||||
|
// 遍历以中心格子为中心的区域
|
||||||
|
// 范围从 -range 到 +range,覆盖所有可能的圆形区域内的格子
|
||||||
|
for (int dx = -range; dx <= range; ++dx)
|
||||||
|
{
|
||||||
|
for (int dy = -range; dy <= range; ++dy)
|
||||||
|
{
|
||||||
|
// 计算当前格子与中心格子的欧几里得距离的平方
|
||||||
|
if ((dx * dx) + (dy * dy) == rangeSquared)
|
||||||
|
{
|
||||||
|
int x = grid.X + dx;
|
||||||
|
int y = grid.Y + dy;
|
||||||
|
int z = grid.Z;
|
||||||
|
|
||||||
|
if (GridsByCoordinate.TryGetValue((x, y, z), out Grid? select) && select != null)
|
||||||
|
{
|
||||||
|
if (includeCharacter || select.Characters.Count == 0)
|
||||||
|
{
|
||||||
|
grids.Add(select);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grids;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置角色移动
|
/// 设置角色移动
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -296,9 +381,15 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
|
|||||||
}
|
}
|
||||||
|
|
||||||
Grid? realGrid = GetCharacterCurrentGrid(character);
|
Grid? realGrid = GetCharacterCurrentGrid(character);
|
||||||
|
Grid startGrid = current;
|
||||||
if (current.Id == target.Id)
|
if (realGrid != null && current.Id != realGrid.Id)
|
||||||
{
|
{
|
||||||
|
startGrid = realGrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startGrid.Id == target.Id)
|
||||||
|
{
|
||||||
|
SetCharacterCurrentGrid(character, startGrid);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,8 +399,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
|
|||||||
HashSet<long> visited = [];
|
HashSet<long> visited = [];
|
||||||
|
|
||||||
// 将起始格子加入队列,步数为0,并标记为已访问
|
// 将起始格子加入队列,步数为0,并标记为已访问
|
||||||
queue.Enqueue((current, 0));
|
queue.Enqueue((startGrid, 0));
|
||||||
visited.Add(current.Id);
|
visited.Add(startGrid.Id);
|
||||||
|
|
||||||
while (queue.Count > 0)
|
while (queue.Count > 0)
|
||||||
{
|
{
|
||||||
@ -319,9 +410,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
|
|||||||
if (currentGrid.Id == target.Id)
|
if (currentGrid.Id == target.Id)
|
||||||
{
|
{
|
||||||
realGrid?.Characters.Remove(character);
|
realGrid?.Characters.Remove(character);
|
||||||
current.Characters.Remove(character);
|
SetCharacterCurrentGrid(character, target);
|
||||||
target.Characters.Add(character);
|
|
||||||
Characters[character] = target;
|
|
||||||
return currentSteps;
|
return currentSteps;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,10 +458,15 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
|
|||||||
}
|
}
|
||||||
|
|
||||||
Grid? realGrid = GetCharacterCurrentGrid(character);
|
Grid? realGrid = GetCharacterCurrentGrid(character);
|
||||||
|
Grid startGrid = current;
|
||||||
if (current.Id == target.Id)
|
if (realGrid != null && current.Id != realGrid.Id)
|
||||||
{
|
{
|
||||||
SetCharacterCurrentGrid(character, current);
|
startGrid = realGrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startGrid.Id == target.Id)
|
||||||
|
{
|
||||||
|
SetCharacterCurrentGrid(character, startGrid);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,11 +477,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
|
|||||||
HashSet<long> visited = [];
|
HashSet<long> visited = [];
|
||||||
|
|
||||||
// 初始化 BFS 队列,将起始格子加入,步数为0
|
// 初始化 BFS 队列,将起始格子加入,步数为0
|
||||||
queue.Enqueue((current, 0));
|
queue.Enqueue((startGrid, 0));
|
||||||
visited.Add(current.Id);
|
visited.Add(startGrid.Id);
|
||||||
|
|
||||||
Grid? bestReachableGrid = current;
|
Grid? bestReachableGrid = current;
|
||||||
int minDistanceToTarget = CalculateManhattanDistance(current, target);
|
int minDistanceToTarget = CalculateManhattanDistance(startGrid, target);
|
||||||
int stepsToBestReachable = 0;
|
int stepsToBestReachable = 0;
|
||||||
|
|
||||||
// 定义平面移动的四个方向
|
// 定义平面移动的四个方向
|
||||||
|
|||||||
@ -83,6 +83,9 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
|
|||||||
case nameof(Character.ExHPPercentage):
|
case nameof(Character.ExHPPercentage):
|
||||||
result.ExHPPercentage = reader.GetDouble();
|
result.ExHPPercentage = reader.GetDouble();
|
||||||
break;
|
break;
|
||||||
|
case nameof(Character.HasMP):
|
||||||
|
result.HasMP = reader.GetBoolean();
|
||||||
|
break;
|
||||||
case nameof(Character.InitialMP):
|
case nameof(Character.InitialMP):
|
||||||
result.InitialMP = reader.GetDouble();
|
result.InitialMP = reader.GetDouble();
|
||||||
break;
|
break;
|
||||||
@ -197,11 +200,11 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
|
|||||||
case nameof(Character.ExCDR):
|
case nameof(Character.ExCDR):
|
||||||
result.ExCDR = reader.GetDouble();
|
result.ExCDR = reader.GetDouble();
|
||||||
break;
|
break;
|
||||||
case nameof(Character.ATR):
|
case nameof(Character.ExATR):
|
||||||
result.ATR = reader.GetInt32();
|
result.ExATR = reader.GetInt32();
|
||||||
break;
|
break;
|
||||||
case nameof(Character.MOV):
|
case nameof(Character.ExMOV):
|
||||||
result.MOV = reader.GetInt32();
|
result.ExMOV = reader.GetInt32();
|
||||||
break;
|
break;
|
||||||
case nameof(Character.ExCritRate):
|
case nameof(Character.ExCritRate):
|
||||||
result.ExCritRate = reader.GetDouble();
|
result.ExCritRate = reader.GetDouble();
|
||||||
@ -273,6 +276,7 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
|
|||||||
writer.WriteNumber(nameof(Character.InitialHP), value.InitialHP);
|
writer.WriteNumber(nameof(Character.InitialHP), value.InitialHP);
|
||||||
writer.WriteNumber(nameof(Character.ExHP2), value.ExHP2);
|
writer.WriteNumber(nameof(Character.ExHP2), value.ExHP2);
|
||||||
writer.WriteNumber(nameof(Character.ExHPPercentage), value.ExHPPercentage);
|
writer.WriteNumber(nameof(Character.ExHPPercentage), value.ExHPPercentage);
|
||||||
|
writer.WriteBoolean(nameof(Character.HasMP), value.HasMP);
|
||||||
writer.WriteNumber(nameof(Character.InitialMP), value.InitialMP);
|
writer.WriteNumber(nameof(Character.InitialMP), value.InitialMP);
|
||||||
writer.WriteNumber(nameof(Character.ExMP2), value.ExMP2);
|
writer.WriteNumber(nameof(Character.ExMP2), value.ExMP2);
|
||||||
writer.WriteNumber(nameof(Character.ExMPPercentage), value.ExMPPercentage);
|
writer.WriteNumber(nameof(Character.ExMPPercentage), value.ExMPPercentage);
|
||||||
@ -311,8 +315,8 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
|
|||||||
writer.WriteNumber(nameof(Character.ExActionCoefficient), value.ExActionCoefficient);
|
writer.WriteNumber(nameof(Character.ExActionCoefficient), value.ExActionCoefficient);
|
||||||
writer.WriteNumber(nameof(Character.ExAccelerationCoefficient), value.ExAccelerationCoefficient);
|
writer.WriteNumber(nameof(Character.ExAccelerationCoefficient), value.ExAccelerationCoefficient);
|
||||||
writer.WriteNumber(nameof(Character.ExCDR), value.ExCDR);
|
writer.WriteNumber(nameof(Character.ExCDR), value.ExCDR);
|
||||||
writer.WriteNumber(nameof(Character.ATR), value.ATR);
|
writer.WriteNumber(nameof(Character.ExATR), value.ExATR);
|
||||||
writer.WriteNumber(nameof(Character.MOV), value.MOV);
|
writer.WriteNumber(nameof(Character.ExMOV), value.ExMOV);
|
||||||
writer.WriteNumber(nameof(Character.ExCritRate), value.ExCritRate);
|
writer.WriteNumber(nameof(Character.ExCritRate), value.ExCritRate);
|
||||||
writer.WriteNumber(nameof(Character.ExCritDMG), value.ExCritDMG);
|
writer.WriteNumber(nameof(Character.ExCritDMG), value.ExCritDMG);
|
||||||
writer.WriteNumber(nameof(Character.ExEvadeRate), value.ExEvadeRate);
|
writer.WriteNumber(nameof(Character.ExEvadeRate), value.ExEvadeRate);
|
||||||
|
|||||||
@ -1063,4 +1063,14 @@ namespace Milimoe.FunGame.Core.Library.Constant
|
|||||||
Magical,
|
Magical,
|
||||||
True
|
True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum SkillRangeType
|
||||||
|
{
|
||||||
|
Diamond,
|
||||||
|
Circle,
|
||||||
|
Square,
|
||||||
|
Line,
|
||||||
|
LinePass,
|
||||||
|
Sector
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
Model/AIDecision.cs
Normal file
18
Model/AIDecision.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Library.Common.Addon;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
|
||||||
|
namespace Milimoe.FunGame.Core.Model
|
||||||
|
{
|
||||||
|
public class AIDecision
|
||||||
|
{
|
||||||
|
public CharacterActionType ActionType { get; set; } = CharacterActionType.EndTurn;
|
||||||
|
public Grid? TargetMoveGrid { get; set; } = null;
|
||||||
|
public ISkill? SkillToUse { get; set; } = null;
|
||||||
|
public Item? ItemToUse { get; set; } = null;
|
||||||
|
public List<Character> Targets { get; set; } = [];
|
||||||
|
public double Score { get; set; } = 0;
|
||||||
|
public bool IsPureMove { get; set; } = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -460,6 +460,86 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double HiddenWeaponHardness { get; set; } = 7;
|
public double HiddenWeaponHardness { get; set; } = 7;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 单手剑的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int OneHandedSwordAttackRange { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 双手剑的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int TwoHandedSwordAttackRange { get; set; } = 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 弓的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int BowAttackRange { get; set; } = 4;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 手枪的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int PistolAttackRange { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 步枪的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int RifleAttackRange { get; set; } = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 双持短刀的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int DualDaggersAttackRange { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 法器的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int TalismanAttackRange { get; set; } = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 法杖的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int StaffAttackRange { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 长柄武器的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int PolearmAttackRange { get; set; } = 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 拳套的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int GauntletAttackRange { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 暗器的攻击距离
|
||||||
|
/// </summary>
|
||||||
|
public int HiddenWeaponAttackRange { get; set; } = 4;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 核心角色的移动距离
|
||||||
|
/// </summary>
|
||||||
|
public int RoleMOV_Core { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 先锋角色的移动距离
|
||||||
|
/// </summary>
|
||||||
|
public int RoleMOV_Vanguard { get; set; } = 6;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 近卫角色的移动距离
|
||||||
|
/// </summary>
|
||||||
|
public int RoleMOV_Guardian { get; set; } = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 支援角色的移动距离
|
||||||
|
/// </summary>
|
||||||
|
public int RoleMOV_Support { get; set; } = 4;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 治疗角色的移动距离
|
||||||
|
/// </summary>
|
||||||
|
public int RoleMOV_Medic { get; set; } = 3;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 应用此游戏平衡常数给实体
|
/// 应用此游戏平衡常数给实体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using Milimoe.FunGame.Core.Api.Utility;
|
using Milimoe.FunGame.Core.Api.Utility;
|
||||||
|
using Milimoe.FunGame.Core.Controller;
|
||||||
using Milimoe.FunGame.Core.Entity;
|
using Milimoe.FunGame.Core.Entity;
|
||||||
using Milimoe.FunGame.Core.Interface.Base;
|
using Milimoe.FunGame.Core.Interface.Base;
|
||||||
using Milimoe.FunGame.Core.Interface.Entity;
|
using Milimoe.FunGame.Core.Interface.Entity;
|
||||||
@ -753,6 +754,8 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_eliminated.Remove(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 减少复活倒计时
|
// 减少复活倒计时
|
||||||
@ -812,11 +815,11 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
|
|
||||||
// 队友列表
|
// 队友列表
|
||||||
List<Character> allTeammates = GetTeammates(character);
|
List<Character> allTeammates = GetTeammates(character);
|
||||||
List<Character> teammates = [.. allTeammates.Where(_queue.Contains)];
|
List<Character> selecableTeammates = [.. allTeammates.Where(_queue.Contains)];
|
||||||
|
|
||||||
// 敌人列表
|
// 敌人列表
|
||||||
List<Character> allEnemys = [.. _allCharacters.Where(c => c != character && !teammates.Contains(c))];
|
List<Character> allEnemys = [.. _allCharacters.Where(c => c != character && !allTeammates.Contains(c))];
|
||||||
List<Character> enemys = [.. allEnemys.Where(c => _queue.Contains(c) && !c.IsUnselectable)];
|
List<Character> selectableEnemys = [.. allEnemys.Where(c => _queue.Contains(c) && !c.IsUnselectable)];
|
||||||
|
|
||||||
// 技能列表
|
// 技能列表
|
||||||
List<Skill> skills = [.. character.Skills.Where(s => s.Level > 0 && s.SkillType != SkillType.Passive && s.Enable && !s.IsInEffect && s.CurrentCD == 0 &&
|
List<Skill> skills = [.. character.Skills.Where(s => s.Level > 0 && s.SkillType != SkillType.Passive && s.Enable && !s.IsInEffect && s.CurrentCD == 0 &&
|
||||||
@ -828,7 +831,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
|
|
||||||
// 回合开始事件,允许事件返回 false 接管回合操作
|
// 回合开始事件,允许事件返回 false 接管回合操作
|
||||||
// 如果事件全程接管回合操作,需要注意触发特效
|
// 如果事件全程接管回合操作,需要注意触发特效
|
||||||
if (!await OnTurnStartAsync(character, enemys, teammates, skills, items))
|
if (!await OnTurnStartAsync(character, selectableEnemys, selecableTeammates, skills, items))
|
||||||
{
|
{
|
||||||
_isInRound = false;
|
_isInRound = false;
|
||||||
return _isGameEnd;
|
return _isGameEnd;
|
||||||
@ -836,13 +839,13 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
|
|
||||||
foreach (Skill skillTurnStart in skills)
|
foreach (Skill skillTurnStart in skills)
|
||||||
{
|
{
|
||||||
skillTurnStart.OnTurnStart(character, enemys, teammates, skills, items);
|
skillTurnStart.OnTurnStart(character, selectableEnemys, selecableTeammates, 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, enemys, teammates, skills, items);
|
effect.OnTurnStart(character, selectableEnemys, selecableTeammates, skills, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 此变量用于在取消选择时,能够重新行动
|
// 此变量用于在取消选择时,能够重新行动
|
||||||
@ -852,24 +855,33 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
// 此变量控制角色移动后可以继续选择其他的行动
|
// 此变量控制角色移动后可以继续选择其他的行动
|
||||||
bool moved = false;
|
bool moved = false;
|
||||||
|
|
||||||
Grid? currentGrid = null;
|
// AI 决策控制器,适用于启用战棋地图的情况
|
||||||
|
AIController? ai = null;
|
||||||
|
|
||||||
|
// 角色的起始地点,确保角色该回合移动的范围不超过 MOV
|
||||||
|
Grid? startGrid = null;
|
||||||
|
List<Grid> canMoveGrids = [];
|
||||||
|
// 并且要筛选最远可选取角色
|
||||||
|
List<Grid> canAttackGridsByStartGrid = [];
|
||||||
|
List<Grid> canCastGridsByStartGrid = [];
|
||||||
if (_map != null)
|
if (_map != null)
|
||||||
{
|
{
|
||||||
currentGrid = _map.GetCharacterCurrentGrid(character);
|
startGrid = _map.GetCharacterCurrentGrid(character);
|
||||||
}
|
|
||||||
|
|
||||||
// 行动开始前,可以修改可选取的角色列表
|
if (startGrid != null)
|
||||||
Dictionary<Character, int> continuousKillingTemp = new(_continuousKilling);
|
{
|
||||||
Dictionary<Character, int> earnedMoneyTemp = new(_earnedMoney);
|
canMoveGrids = _map.GetGridsByRange(startGrid, character.MOV, false);
|
||||||
effects = [.. character.Effects.Where(e => e.IsInEffect)];
|
canAttackGridsByStartGrid = _map.GetGridsByRange(startGrid, character.ATR, true);
|
||||||
foreach (Effect effect in effects)
|
Skill[] canCastSkills = [.. skills, .. items.Select(i => i.Skills.Active!)];
|
||||||
{
|
foreach (Skill skill in canCastSkills)
|
||||||
effect.AlterSelectListBeforeAction(character, enemys, teammates, skills, continuousKillingTemp, earnedMoneyTemp);
|
{
|
||||||
}
|
canCastGridsByStartGrid.AddRange(_map.GetGridsByRange(startGrid, skill.CastRange, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 这里筛掉重复角色
|
allEnemys = [.. allEnemys.Where(canAttackGridsByStartGrid.Union(canCastGridsByStartGrid).SelectMany(g => g.Characters).Contains)];
|
||||||
enemys = [.. enemys.Distinct()];
|
allTeammates = [.. allTeammates.Where(canAttackGridsByStartGrid.Union(canCastGridsByStartGrid).SelectMany(g => g.Characters).Contains)];
|
||||||
teammates = [.. teammates.Distinct()];
|
}
|
||||||
|
|
||||||
// 作出了什么行动
|
// 作出了什么行动
|
||||||
CharacterActionType type = CharacterActionType.None;
|
CharacterActionType type = CharacterActionType.None;
|
||||||
@ -880,6 +892,58 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
bool isAI = CharactersInAI.Contains(character);
|
bool isAI = CharactersInAI.Contains(character);
|
||||||
while (!decided && (!isAI || cancelTimes > 0))
|
while (!decided && (!isAI || cancelTimes > 0))
|
||||||
{
|
{
|
||||||
|
// 根据当前位置,更新可选取角色列表
|
||||||
|
Grid? realGrid = null;
|
||||||
|
List<Grid> canAttackGrids = [];
|
||||||
|
List<Grid> canCastGrids = [];
|
||||||
|
List<Grid> willMoveGridWithSkill = [];
|
||||||
|
List<Character> enemys = [];
|
||||||
|
List<Character> teammates = [];
|
||||||
|
if (_map != null)
|
||||||
|
{
|
||||||
|
if (isAI)
|
||||||
|
{
|
||||||
|
ai ??= new(this, _map);
|
||||||
|
}
|
||||||
|
|
||||||
|
realGrid = _map.GetCharacterCurrentGrid(character);
|
||||||
|
|
||||||
|
if (realGrid != null)
|
||||||
|
{
|
||||||
|
canAttackGrids = _map.GetGridsByRange(realGrid, character.ATR, true);
|
||||||
|
Skill[] canCastSkills = [.. skills, .. items.Select(i => i.Skills.Active!)];
|
||||||
|
foreach (Skill skill in canCastSkills)
|
||||||
|
{
|
||||||
|
canCastGrids.AddRange(_map.GetGridsByRange(realGrid, skill.CastRange, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enemys = [.. selectableEnemys.Where(canAttackGrids.Union(canCastGrids).SelectMany(g => g.Characters).Contains)];
|
||||||
|
teammates = [.. selecableTeammates.Where(canAttackGrids.Union(canCastGrids).SelectMany(g => g.Characters).Contains)];
|
||||||
|
willMoveGridWithSkill = [.. canMoveGrids.Where(g => canAttackGrids.Union(canCastGrids).Contains(g))];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
enemys = selectableEnemys;
|
||||||
|
teammates = selecableTeammates;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 决策结果(适用于启用战棋地图的情况)
|
||||||
|
AIDecision? aiDecision = null;
|
||||||
|
|
||||||
|
// 行动开始前,可以修改可选取的角色列表
|
||||||
|
Dictionary<Character, int> continuousKillingTemp = new(_continuousKilling);
|
||||||
|
Dictionary<Character, int> earnedMoneyTemp = new(_earnedMoney);
|
||||||
|
effects = [.. character.Effects.Where(e => e.IsInEffect)];
|
||||||
|
foreach (Effect effect in effects)
|
||||||
|
{
|
||||||
|
effect.AlterSelectListBeforeAction(character, enemys, teammates, skills, continuousKillingTemp, earnedMoneyTemp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这里筛掉重复角色
|
||||||
|
enemys = [.. enemys.Distinct()];
|
||||||
|
teammates = [.. teammates.Distinct()];
|
||||||
|
|
||||||
if (moved) moved = false;
|
if (moved) moved = false;
|
||||||
else cancelTimes--;
|
else cancelTimes--;
|
||||||
type = CharacterActionType.None;
|
type = CharacterActionType.None;
|
||||||
@ -996,8 +1060,17 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模组可以通过此事件来决定角色的行动
|
// 启用战棋地图时的专属 AI 决策方法
|
||||||
type = await OnDecideActionAsync(character, enemys, teammates, skills, items);
|
if (isAI && ai != null && startGrid != null)
|
||||||
|
{
|
||||||
|
aiDecision = await ai.DecideAIActionAsync(character, startGrid, canMoveGrids, skills, items, allEnemys, allTeammates);
|
||||||
|
type = aiDecision.ActionType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 模组可以通过此事件来决定角色的行动
|
||||||
|
type = await OnDecideActionAsync(character, enemys, teammates, skills, items);
|
||||||
|
}
|
||||||
// 若事件未完成决策,则将通过概率对角色进行自动化决策
|
// 若事件未完成决策,则将通过概率对角色进行自动化决策
|
||||||
if (type == CharacterActionType.None)
|
if (type == CharacterActionType.None)
|
||||||
{
|
{
|
||||||
@ -1021,7 +1094,34 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == CharacterActionType.NormalAttack)
|
if (aiDecision != null && aiDecision.ActionType != CharacterActionType.Move && aiDecision.TargetMoveGrid != null)
|
||||||
|
{
|
||||||
|
// 不是纯粹移动的情况,需要手动移动
|
||||||
|
moved = await CharacterMoveAsync(character, aiDecision.TargetMoveGrid, startGrid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == CharacterActionType.Move)
|
||||||
|
{
|
||||||
|
if (_map != null)
|
||||||
|
{
|
||||||
|
Grid target;
|
||||||
|
if (aiDecision != null && aiDecision.TargetMoveGrid != null)
|
||||||
|
{
|
||||||
|
target = aiDecision.TargetMoveGrid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target = await SelectTargetGridAsync(character, enemys, teammates, _map, canMoveGrids);
|
||||||
|
}
|
||||||
|
moved = await CharacterMoveAsync(character, target, startGrid);
|
||||||
|
}
|
||||||
|
if (isAI && aiDecision != null && cancelTimes == 0)
|
||||||
|
{
|
||||||
|
// 取消 AI 的移动
|
||||||
|
type = CharacterActionType.EndTurn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == CharacterActionType.NormalAttack)
|
||||||
{
|
{
|
||||||
if (!forceAction && (character.CharacterState == CharacterState.NotActionable ||
|
if (!forceAction && (character.CharacterState == CharacterState.NotActionable ||
|
||||||
character.CharacterState == CharacterState.ActionRestricted ||
|
character.CharacterState == CharacterState.ActionRestricted ||
|
||||||
@ -1033,7 +1133,22 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 使用普通攻击逻辑
|
// 使用普通攻击逻辑
|
||||||
List<Character> targets = await SelectTargetsAsync(character, character.NormalAttack, enemys, teammates);
|
List<Character> targets;
|
||||||
|
if (aiDecision != null)
|
||||||
|
{
|
||||||
|
targets = aiDecision.Targets;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Grid> attackRange = [];
|
||||||
|
if (_map != null && realGrid != null)
|
||||||
|
{
|
||||||
|
attackRange = _map.GetGridsByRange(realGrid, character.ATR, true);
|
||||||
|
enemys = [.. enemys.Where(attackRange.SelectMany(g => g.Characters).Contains)];
|
||||||
|
teammates = [.. teammates.Where(attackRange.SelectMany(g => g.Characters).Contains)];
|
||||||
|
}
|
||||||
|
targets = await SelectTargetsAsync(character, character.NormalAttack, enemys, teammates, attackRange);
|
||||||
|
}
|
||||||
if (targets.Count > 0)
|
if (targets.Count > 0)
|
||||||
{
|
{
|
||||||
LastRound.Targets = [.. targets];
|
LastRound.Targets = [.. targets];
|
||||||
@ -1063,7 +1178,15 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 预使用技能,即开始吟唱逻辑
|
// 预使用技能,即开始吟唱逻辑
|
||||||
Skill? skill = await OnSelectSkillAsync(character, skills);
|
Skill? skill;
|
||||||
|
if (aiDecision != null && aiDecision.SkillToUse is Skill s)
|
||||||
|
{
|
||||||
|
skill = s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
skill = await OnSelectSkillAsync(character, skills);
|
||||||
|
}
|
||||||
if (skill is null && CharactersInAI.Contains(character) && skills.Count > 0)
|
if (skill is null && CharactersInAI.Contains(character) && skills.Count > 0)
|
||||||
{
|
{
|
||||||
skill = skills[Random.Shared.Next(skills.Count)];
|
skill = skills[Random.Shared.Next(skills.Count)];
|
||||||
@ -1073,7 +1196,22 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
// 吟唱前需要先选取目标
|
// 吟唱前需要先选取目标
|
||||||
if (skill.SkillType == SkillType.Magic)
|
if (skill.SkillType == SkillType.Magic)
|
||||||
{
|
{
|
||||||
List<Character> targets = await SelectTargetsAsync(character, skill, enemys, teammates);
|
List<Character> targets;
|
||||||
|
if (aiDecision != null)
|
||||||
|
{
|
||||||
|
targets = aiDecision.Targets;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Grid> castRange = [];
|
||||||
|
if (_map != null && realGrid != null)
|
||||||
|
{
|
||||||
|
castRange = _map.GetGridsByRange(realGrid, skill.CastRange, true);
|
||||||
|
enemys = [.. enemys.Where(castRange.SelectMany(g => g.Characters).Contains)];
|
||||||
|
teammates = [.. teammates.Where(castRange.SelectMany(g => g.Characters).Contains)];
|
||||||
|
}
|
||||||
|
targets = await SelectTargetsAsync(character, skill, enemys, teammates, castRange);
|
||||||
|
}
|
||||||
if (targets.Count > 0)
|
if (targets.Count > 0)
|
||||||
{
|
{
|
||||||
// 免疫检定
|
// 免疫检定
|
||||||
@ -1100,7 +1238,22 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
// 只有魔法需要吟唱,战技和爆发技直接释放
|
// 只有魔法需要吟唱,战技和爆发技直接释放
|
||||||
if (CheckCanCast(character, skill, out double cost))
|
if (CheckCanCast(character, skill, out double cost))
|
||||||
{
|
{
|
||||||
List<Character> targets = await SelectTargetsAsync(character, skill, enemys, teammates);
|
List<Character> targets;
|
||||||
|
if (aiDecision != null)
|
||||||
|
{
|
||||||
|
targets = aiDecision.Targets;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Grid> castRange = [];
|
||||||
|
if (_map != null && realGrid != null)
|
||||||
|
{
|
||||||
|
castRange = _map.GetGridsByRange(realGrid, skill.CastRange, true);
|
||||||
|
enemys = [.. enemys.Where(castRange.SelectMany(g => g.Characters).Contains)];
|
||||||
|
teammates = [.. teammates.Where(castRange.SelectMany(g => g.Characters).Contains)];
|
||||||
|
}
|
||||||
|
targets = await SelectTargetsAsync(character, skill, enemys, teammates, castRange);
|
||||||
|
}
|
||||||
if (targets.Count > 0)
|
if (targets.Count > 0)
|
||||||
{
|
{
|
||||||
// 免疫检定
|
// 免疫检定
|
||||||
@ -1211,7 +1364,8 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
if (CheckCanCast(character, skill, out double cost))
|
if (CheckCanCast(character, skill, out double cost))
|
||||||
{
|
{
|
||||||
// 预释放的爆发技不可取消
|
// 预释放的爆发技不可取消
|
||||||
List<Character> targets = await SelectTargetsAsync(character, skill, enemys, teammates);
|
List<Grid> castRange = _map != null && realGrid != null ? _map.GetGridsByRange(realGrid, skill.CastRange, true) : [];
|
||||||
|
List<Character> targets = await SelectTargetsAsync(character, skill, enemys, teammates, castRange);
|
||||||
// 免疫检定
|
// 免疫检定
|
||||||
await CheckSkilledImmuneAsync(character, targets, skill);
|
await CheckSkilledImmuneAsync(character, targets, skill);
|
||||||
LastRound.Targets = [.. targets];
|
LastRound.Targets = [.. targets];
|
||||||
@ -1246,7 +1400,15 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
else if (type == CharacterActionType.UseItem)
|
else if (type == CharacterActionType.UseItem)
|
||||||
{
|
{
|
||||||
// 使用物品逻辑
|
// 使用物品逻辑
|
||||||
Item? item = await OnSelectItemAsync(character, items);
|
Item? item;
|
||||||
|
if (aiDecision != null && aiDecision.ItemToUse != null)
|
||||||
|
{
|
||||||
|
item = aiDecision.ItemToUse;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item = await OnSelectItemAsync(character, items);
|
||||||
|
}
|
||||||
if (item is null && CharactersInAI.Contains(character) && items.Count > 0)
|
if (item is null && CharactersInAI.Contains(character) && items.Count > 0)
|
||||||
{
|
{
|
||||||
// AI 控制下随机选取一个物品
|
// AI 控制下随机选取一个物品
|
||||||
@ -1255,7 +1417,14 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
if (item != null && item.Skills.Active != null)
|
if (item != null && item.Skills.Active != null)
|
||||||
{
|
{
|
||||||
Skill skill = item.Skills.Active;
|
Skill skill = item.Skills.Active;
|
||||||
if (await UseItemAsync(item, character, enemys, teammates))
|
List<Grid> castRange = [];
|
||||||
|
if (_map != null && realGrid != null)
|
||||||
|
{
|
||||||
|
castRange = _map.GetGridsByRange(realGrid, skill.CastRange, true);
|
||||||
|
enemys = [.. enemys.Where(castRange.SelectMany(g => g.Characters).Contains)];
|
||||||
|
teammates = [.. teammates.Where(castRange.SelectMany(g => g.Characters).Contains)];
|
||||||
|
}
|
||||||
|
if (await UseItemAsync(item, character, enemys, teammates, castRange, aiDecision?.Targets))
|
||||||
{
|
{
|
||||||
decided = true;
|
decided = true;
|
||||||
LastRound.Item = item;
|
LastRound.Item = item;
|
||||||
@ -1282,20 +1451,6 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
WriteLine($"[ {character} ] 结束了回合!");
|
WriteLine($"[ {character} ] 结束了回合!");
|
||||||
await OnCharacterDoNothingAsync(character);
|
await OnCharacterDoNothingAsync(character);
|
||||||
}
|
}
|
||||||
else if (type == CharacterActionType.Move)
|
|
||||||
{
|
|
||||||
if (_map != null)
|
|
||||||
{
|
|
||||||
Grid target = await SelectTargetGridAsync(character, enemys, teammates, _map);
|
|
||||||
if (target.Id != -1)
|
|
||||||
{
|
|
||||||
int steps = _map.CharacterMove(character, currentGrid, target);
|
|
||||||
moved = true;
|
|
||||||
WriteLine($"[ {character} ] 移动了 {steps} 步!");
|
|
||||||
await OnCharacterMoveAsync(character, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (baseTime == 0) baseTime += 8;
|
if (baseTime == 0) baseTime += 8;
|
||||||
@ -2213,8 +2368,10 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// <param name="character"></param>
|
/// <param name="character"></param>
|
||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
|
/// <param name="castRange"></param>
|
||||||
|
/// <param name="desiredTargets"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> UseItemAsync(Item item, Character character, List<Character> enemys, List<Character> teammates)
|
public async Task<bool> UseItemAsync(Item item, Character character, List<Character> enemys, List<Character> teammates, List<Grid> castRange, List<Character>? desiredTargets = null)
|
||||||
{
|
{
|
||||||
if (CheckCanCast(character, item, out double costMP, out double costEP))
|
if (CheckCanCast(character, item, out double costMP, out double costEP))
|
||||||
{
|
{
|
||||||
@ -2222,7 +2379,15 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
if (skill != null)
|
if (skill != null)
|
||||||
{
|
{
|
||||||
skill.GamingQueue = this;
|
skill.GamingQueue = this;
|
||||||
List<Character> targets = await SelectTargetsAsync(character, skill, enemys, teammates);
|
List<Character> targets;
|
||||||
|
if (desiredTargets != null)
|
||||||
|
{
|
||||||
|
targets = desiredTargets;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targets = await SelectTargetsAsync(character, skill, enemys, teammates, castRange);
|
||||||
|
}
|
||||||
if (targets.Count > 0)
|
if (targets.Count > 0)
|
||||||
{
|
{
|
||||||
// 免疫检定
|
// 免疫检定
|
||||||
@ -2277,6 +2442,28 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色移动实际逻辑
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character"></param>
|
||||||
|
/// <param name="target"></param>
|
||||||
|
/// <param name="startGrid"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> CharacterMoveAsync(Character character, Grid target, Grid? startGrid)
|
||||||
|
{
|
||||||
|
if (target.Id != -1)
|
||||||
|
{
|
||||||
|
int steps = _map?.CharacterMove(character, startGrid, target) ?? -1;
|
||||||
|
if (steps > 0)
|
||||||
|
{
|
||||||
|
WriteLine($"[ {character} ] 移动了 {steps} 步!");
|
||||||
|
await OnCharacterMoveAsync(character, target);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通过概率计算角色要干嘛
|
/// 通过概率计算角色要干嘛
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2329,25 +2516,25 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
/// <param name="map"></param>
|
/// <param name="map"></param>
|
||||||
|
/// <param name="moveRange"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<Grid> SelectTargetGridAsync(Character character, List<Character> enemys, List<Character> teammates, GameMap map)
|
public async Task<Grid> SelectTargetGridAsync(Character character, List<Character> enemys, List<Character> teammates, GameMap map, List<Grid> moveRange)
|
||||||
{
|
{
|
||||||
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.BeforeSelectTargetGrid(character, enemys, teammates, map);
|
effect.BeforeSelectTargetGrid(character, enemys, teammates, map, moveRange);
|
||||||
}
|
}
|
||||||
Grid target = await OnSelectTargetGridAsync(character, enemys, teammates, map);
|
Grid target = await OnSelectTargetGridAsync(character, enemys, teammates, map, moveRange);
|
||||||
if (target.Id != -1)
|
if (target.Id != -1)
|
||||||
{
|
{
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
else if (target.Id == -2 && map.Characters.TryGetValue(character, out Grid? current) && current != null)
|
else if (target.Id == -2 && map.Characters.TryGetValue(character, out Grid? current) && current != null)
|
||||||
{
|
{
|
||||||
List<Grid> grids = map.GetGridsByRange(current, character.MOV);
|
if (moveRange.Count > 0)
|
||||||
if (grids.Count > 0)
|
|
||||||
{
|
{
|
||||||
return grids[Random.Shared.Next(grids.Count)];
|
return moveRange[Random.Shared.Next(moveRange.Count)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Grid.Empty;
|
return Grid.Empty;
|
||||||
@ -2360,15 +2547,16 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// <param name="skill"></param>
|
/// <param name="skill"></param>
|
||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
|
/// <param name="castRange"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<List<Character>> SelectTargetsAsync(Character caster, Skill skill, List<Character> enemys, List<Character> teammates)
|
public async Task<List<Character>> SelectTargetsAsync(Character caster, Skill skill, List<Character> enemys, List<Character> teammates, List<Grid> castRange)
|
||||||
{
|
{
|
||||||
List<Effect> effects = [.. caster.Effects.Where(e => e.IsInEffect)];
|
List<Effect> effects = [.. caster.Effects.Where(e => e.IsInEffect)];
|
||||||
foreach (Effect effect in effects)
|
foreach (Effect effect in effects)
|
||||||
{
|
{
|
||||||
effect.AlterSelectListBeforeSelection(caster, skill, enemys, teammates);
|
effect.AlterSelectListBeforeSelection(caster, skill, enemys, teammates);
|
||||||
}
|
}
|
||||||
List<Character> targets = await OnSelectSkillTargetsAsync(caster, skill, enemys, teammates);
|
List<Character> targets = await OnSelectSkillTargetsAsync(caster, skill, enemys, teammates, castRange);
|
||||||
if (targets.Count == 0 && CharactersInAI.Contains(caster))
|
if (targets.Count == 0 && CharactersInAI.Contains(caster))
|
||||||
{
|
{
|
||||||
targets = skill.SelectTargets(caster, enemys, teammates);
|
targets = skill.SelectTargets(caster, enemys, teammates);
|
||||||
@ -2383,15 +2571,16 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// <param name="attack"></param>
|
/// <param name="attack"></param>
|
||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
|
/// <param name="attackRange"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<List<Character>> SelectTargetsAsync(Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates)
|
public async Task<List<Character>> SelectTargetsAsync(Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates, List<Grid> attackRange)
|
||||||
{
|
{
|
||||||
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.AlterSelectListBeforeSelection(character, attack, enemys, teammates);
|
effect.AlterSelectListBeforeSelection(character, attack, enemys, teammates);
|
||||||
}
|
}
|
||||||
List<Character> targets = await OnSelectNormalAttackTargetsAsync(character, attack, enemys, teammates);
|
List<Character> targets = await OnSelectNormalAttackTargetsAsync(character, attack, enemys, teammates, attackRange);
|
||||||
if (targets.Count == 0 && CharactersInAI.Contains(character))
|
if (targets.Count == 0 && CharactersInAI.Contains(character))
|
||||||
{
|
{
|
||||||
targets = character.NormalAttack.SelectTargets(character, enemys, teammates);
|
targets = character.NormalAttack.SelectTargets(character, enemys, teammates);
|
||||||
@ -3359,7 +3548,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
return await (SelectItem?.Invoke(this, character, items) ?? Task.FromResult<Item?>(null));
|
return await (SelectItem?.Invoke(this, character, items) ?? Task.FromResult<Item?>(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate Task<Grid> SelectTargetGridEventHandler(GamingQueue queue, Character character, List<Character> enemys, List<Character> teammates, GameMap map);
|
public delegate Task<Grid> SelectTargetGridEventHandler(GamingQueue queue, Character character, List<Character> enemys, List<Character> teammates, GameMap map, List<Grid> moveRange);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 选取移动目标事件
|
/// 选取移动目标事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -3371,13 +3560,14 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
/// <param name="map"></param>
|
/// <param name="map"></param>
|
||||||
|
/// <param name="moveRange"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected async Task<Grid> OnSelectTargetGridAsync(Character character, List<Character> enemys, List<Character> teammates, GameMap map)
|
protected async Task<Grid> OnSelectTargetGridAsync(Character character, List<Character> enemys, List<Character> teammates, GameMap map, List<Grid> moveRange)
|
||||||
{
|
{
|
||||||
return await (SelectTargetGrid?.Invoke(this, character, enemys, teammates, map) ?? Task.FromResult(Grid.Empty));
|
return await (SelectTargetGrid?.Invoke(this, character, enemys, teammates, map, moveRange) ?? Task.FromResult(Grid.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate Task<List<Character>> SelectSkillTargetsEventHandler(GamingQueue queue, Character caster, Skill skill, List<Character> enemys, List<Character> teammates);
|
public delegate Task<List<Character>> SelectSkillTargetsEventHandler(GamingQueue queue, Character caster, Skill skill, List<Character> enemys, List<Character> teammates, List<Grid> castRange);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 选取技能目标事件
|
/// 选取技能目标事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -3389,13 +3579,14 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// <param name="skill"></param>
|
/// <param name="skill"></param>
|
||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
|
/// <param name="castRange"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected async Task<List<Character>> OnSelectSkillTargetsAsync(Character caster, Skill skill, List<Character> enemys, List<Character> teammates)
|
protected async Task<List<Character>> OnSelectSkillTargetsAsync(Character caster, Skill skill, List<Character> enemys, List<Character> teammates, List<Grid> castRange)
|
||||||
{
|
{
|
||||||
return await (SelectSkillTargets?.Invoke(this, caster, skill, enemys, teammates) ?? Task.FromResult(new List<Character>()));
|
return await (SelectSkillTargets?.Invoke(this, caster, skill, enemys, teammates, castRange) ?? Task.FromResult(new List<Character>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate Task<List<Character>> SelectNormalAttackTargetsEventHandler(GamingQueue queue, Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates);
|
public delegate Task<List<Character>> SelectNormalAttackTargetsEventHandler(GamingQueue queue, Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates, List<Grid> attackRange);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 选取普通攻击目标事件
|
/// 选取普通攻击目标事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -3407,10 +3598,11 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
/// <param name="attack"></param>
|
/// <param name="attack"></param>
|
||||||
/// <param name="enemys"></param>
|
/// <param name="enemys"></param>
|
||||||
/// <param name="teammates"></param>
|
/// <param name="teammates"></param>
|
||||||
|
/// <param name="attackRange"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected async Task<List<Character>> OnSelectNormalAttackTargetsAsync(Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates)
|
protected async Task<List<Character>> OnSelectNormalAttackTargetsAsync(Character character, NormalAttack attack, List<Character> enemys, List<Character> teammates, List<Grid> attackRange)
|
||||||
{
|
{
|
||||||
return await (SelectNormalAttackTargets?.Invoke(this, character, attack, enemys, teammates) ?? Task.FromResult(new List<Character>()));
|
return await (SelectNormalAttackTargets?.Invoke(this, character, attack, enemys, teammates, attackRange) ?? Task.FromResult(new List<Character>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate Task InterruptCastingEventHandler(GamingQueue queue, Character cast, Skill? skill, Character interrupter);
|
public delegate Task InterruptCastingEventHandler(GamingQueue queue, Character cast, Skill? skill, Character interrupter);
|
||||||
|
|||||||
@ -147,7 +147,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
{
|
{
|
||||||
string[] teamActive = [.. Teams.OrderByDescending(kv => kv.Value.Score).Select(kv =>
|
string[] teamActive = [.. Teams.OrderByDescending(kv => kv.Value.Score).Select(kv =>
|
||||||
{
|
{
|
||||||
int activeCount = kv.Value.GetActiveCharacters(this).Count;
|
int activeCount = kv.Value.GetActiveCharacters().Count;
|
||||||
if (kv.Value == killTeam)
|
if (kv.Value == killTeam)
|
||||||
{
|
{
|
||||||
activeCount += 1;
|
activeCount += 1;
|
||||||
@ -159,7 +159,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
|
|
||||||
if (deathTeam != null)
|
if (deathTeam != null)
|
||||||
{
|
{
|
||||||
List<Character> remain = deathTeam.GetActiveCharacters(this);
|
List<Character> remain = deathTeam.GetActiveCharacters();
|
||||||
int remainCount = remain.Count;
|
int remainCount = remain.Count;
|
||||||
if (remainCount == 0)
|
if (remainCount == 0)
|
||||||
{
|
{
|
||||||
@ -175,7 +175,7 @@ namespace Milimoe.FunGame.Core.Model
|
|||||||
|
|
||||||
if (killTeam != null)
|
if (killTeam != null)
|
||||||
{
|
{
|
||||||
List<Character> actives = killTeam.GetActiveCharacters(this);
|
List<Character> actives = killTeam.GetActiveCharacters();
|
||||||
int remainCount = actives.Count;
|
int remainCount = actives.Count;
|
||||||
if (remainCount > 0 && MaxRespawnTimes == 0)
|
if (remainCount > 0 && MaxRespawnTimes == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user