行动顺序表兼容单位设计 (#147)

* 初步添加单位判断

* 完成单位的特殊处理;新增技能释放前的询问钩子
This commit is contained in:
milimoe 2026-01-14 19:03:52 +08:00 committed by GitHub
parent d72ccb2dd3
commit 6325e9a956
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 437 additions and 153 deletions

View File

@ -307,6 +307,17 @@ namespace Milimoe.FunGame.Core.Entity
} }
} }
/// <summary>
/// 在选取目标前向角色(玩家)发起询问
/// </summary>
/// <param name="character"></param>
/// <param name="item"></param>
/// <returns></returns>
public virtual InquiryOptions? InquiryBeforeTargetSelection(Character character, Item item)
{
return null;
}
/// <summary> /// <summary>
/// 局内使用物品触发 /// 局内使用物品触发
/// </summary> /// </summary>

View File

@ -546,7 +546,8 @@ namespace Milimoe.FunGame.Core.Entity
/// <param name="killer"></param> /// <param name="killer"></param>
/// <param name="continuousKilling"></param> /// <param name="continuousKilling"></param>
/// <param name="earnedMoney"></param> /// <param name="earnedMoney"></param>
public virtual void AfterDeathCalculation(Character death, Character? killer, Dictionary<Character, int> continuousKilling, Dictionary<Character, int> earnedMoney) /// <param name="assists"></param>
public virtual void AfterDeathCalculation(Character death, Character? killer, Dictionary<Character, int> continuousKilling, Dictionary<Character, int> earnedMoney, Character[] assists)
{ {
} }
@ -865,6 +866,20 @@ namespace Milimoe.FunGame.Core.Entity
return true; return true;
} }
/// <summary>
/// 在特效豁免检定时
/// </summary>
/// <param name="character"></param>
/// <param name="source"></param>
/// <param name="effect"></param>
/// <param name="isEvade"></param>
/// <param name="throwingBonus"></param>
/// <returns>false跳过豁免检定</returns>
public virtual bool OnExemptionCheck(Character character, Character? source, Effect effect, bool isEvade, ref double throwingBonus)
{
return true;
}
/// <summary> /// <summary>
/// 在角色行动后触发 /// 在角色行动后触发
/// </summary> /// </summary>

View File

@ -478,6 +478,24 @@ namespace Milimoe.FunGame.Core.Entity
/// <returns></returns> /// <returns></returns>
public override string ToString() => GetInfo(true); public override string ToString() => GetInfo(true);
/// <summary>
/// 在选取目标前向角色(玩家)发起询问的事件
/// </summary>
/// <param name="character"></param>
/// <param name="normalAttack"></param>
/// <returns></returns>
public delegate InquiryOptions? NormalAttackInquiryOptionsDelegate(Character character, NormalAttack normalAttack);
public event NormalAttackInquiryOptionsDelegate? InquiryBeforeTargetSelectionEvent;
/// <summary>
/// 触发选择目标前的询问事件
/// </summary>
/// <param name="character"></param>
/// <param name="normalAttack"></param>
public InquiryOptions? OnInquiryBeforeTargetSelection(Character character, NormalAttack normalAttack)
{
return InquiryBeforeTargetSelectionEvent?.Invoke(character, normalAttack);
}
/// <summary> /// <summary>
/// 等级 /// 等级
/// </summary> /// </summary>

View File

@ -4,6 +4,7 @@ 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.Common.Addon;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model;
namespace Milimoe.FunGame.Core.Entity namespace Milimoe.FunGame.Core.Entity
{ {
@ -379,6 +380,17 @@ namespace Milimoe.FunGame.Core.Entity
} }
/// <summary>
/// 在选取目标前向角色(玩家)发起询问
/// </summary>
/// <param name="character"></param>
/// <param name="skill"></param>
/// <returns></returns>
public virtual InquiryOptions? InquiryBeforeTargetSelection(Character character, Skill skill)
{
return null;
}
/// <summary> /// <summary>
/// 获取可选择的目标列表 /// 获取可选择的目标列表
/// </summary> /// </summary>

View File

@ -26,6 +26,10 @@
public bool IsOnThisTeam(Character character) public bool IsOnThisTeam(Character character)
{ {
if (character.Master != null)
{
character = character.Master;
}
return Members.Contains(character); return Members.Contains(character);
} }

View File

@ -258,9 +258,9 @@ namespace Milimoe.FunGame.Core.Model
protected readonly Dictionary<Character, double> _respawnCountdown = []; protected readonly Dictionary<Character, double> _respawnCountdown = [];
/// <summary> /// <summary>
/// 当前回合死亡角色 /// 当前回合死亡角色和参与击杀的人
/// </summary> /// </summary>
protected readonly List<Character> _roundDeaths = []; protected readonly Dictionary<Character, Character[]> _roundDeaths = [];
/// <summary> /// <summary>
/// 回合奖励 /// 回合奖励
@ -352,6 +352,45 @@ namespace Milimoe.FunGame.Core.Model
_map = map.InitGamingQueue(this); _map = map.InitGamingQueue(this);
} }
/// <summary>
/// 将角色从地图上移除
/// </summary>
public void RemoveCharacterFromMap(params IEnumerable<Character> characters)
{
if (Map is null)
{
return;
}
foreach (Character character in characters)
{
RemoveCharacterFromQueue(character);
Map.Characters.Remove(character);
Grid[] grids = [.. Map.Grids.Values.Where(g => g.Characters.Contains(character))];
foreach (Grid grid in grids)
{
grid.Characters.Remove(character);
}
}
}
/// <summary>
/// 将角色拥有的单位从地图上移除
/// </summary>
public void RemoveCharactersUnitFromMap(params IEnumerable<Character> characters)
{
if (Map is null)
{
return;
}
foreach (Character character in characters)
{
Character[] willRemove = [.. _queue.Where(c => c.Master == character)];
RemoveCharacterFromMap(willRemove);
}
}
#endregion #endregion
#region #region
@ -365,6 +404,7 @@ namespace Milimoe.FunGame.Core.Model
// 保存原始的角色信息。用于复活时还原状态 // 保存原始的角色信息。用于复活时还原状态
foreach (Character character in characters) foreach (Character character in characters)
{ {
if (character.IsUnit) continue;
// 添加角色引用到所有角色列表 // 添加角色引用到所有角色列表
_allCharacters.Add(character); _allCharacters.Add(character);
// 复制原始角色对象 // 复制原始角色对象
@ -384,7 +424,7 @@ namespace Milimoe.FunGame.Core.Model
} }
// 获取 HP 小于等于 0 的角色 // 获取 HP 小于等于 0 的角色
List<Character> deadCharacters = [.. characters.Where(c => c.HP <= 0)]; List<Character> deadCharacters = [.. characters.Where(c => c.HP <= 0 && !c.IsUnit)];
foreach (Character death in deadCharacters) foreach (Character death in deadCharacters)
{ {
_eliminated.Add(death); _eliminated.Add(death);
@ -588,6 +628,9 @@ namespace Milimoe.FunGame.Core.Model
} }
} }
} }
// 重新排序
_queue.Sort((a, b) => _hardnessTimes[a].CompareTo(_hardnessTimes[b]));
} }
/// <summary> /// <summary>
@ -615,6 +658,19 @@ namespace Milimoe.FunGame.Core.Model
_charactersInAIByUser.Clear(); _charactersInAIByUser.Clear();
} }
/// <summary>
/// 将角色彻底移出行动顺序表
/// </summary>
/// <param name="characters"></param>
public void RemoveCharacterFromQueue(params IEnumerable<Character> characters)
{
foreach (Character character in characters)
{
_queue.Remove(character);
_hardnessTimes.Remove(character);
}
}
#endregion #endregion
#region #region
@ -696,11 +752,19 @@ namespace Milimoe.FunGame.Core.Model
} }
// 统计 // 统计
_stats[character].LiveRound += 1; Character statsCharacter = character;
_stats[character].LiveTime += timeToReduce; if (character.Master != null)
_stats[character].DamagePerRound = _stats[character].LiveRound == 0 ? 0 : _stats[character].TotalDamage / _stats[character].LiveRound; {
_stats[character].DamagePerTurn = _stats[character].ActionTurn == 0 ? 0 : _stats[character].TotalDamage / _stats[character].ActionTurn; statsCharacter = character.Master;
_stats[character].DamagePerSecond = _stats[character].LiveTime == 0 ? 0 : _stats[character].TotalDamage / _stats[character].LiveTime; }
if (character.Master is null)
{
_stats[statsCharacter].LiveRound += 1;
_stats[statsCharacter].LiveTime += timeToReduce;
}
_stats[statsCharacter].DamagePerRound = _stats[statsCharacter].LiveRound == 0 ? 0 : _stats[statsCharacter].TotalDamage / _stats[statsCharacter].LiveRound;
_stats[statsCharacter].DamagePerTurn = _stats[statsCharacter].ActionTurn == 0 ? 0 : _stats[statsCharacter].TotalDamage / _stats[statsCharacter].ActionTurn;
_stats[statsCharacter].DamagePerSecond = _stats[statsCharacter].LiveTime == 0 ? 0 : _stats[statsCharacter].TotalDamage / _stats[statsCharacter].LiveTime;
// 回血回蓝 // 回血回蓝
double recoveryHP = character.HR * timeToReduce; double recoveryHP = character.HR * timeToReduce;
@ -725,19 +789,19 @@ namespace Milimoe.FunGame.Core.Model
{ {
character.HP += reallyReHP; character.HP += reallyReHP;
character.MP += reallyReMP; character.MP += reallyReMP;
if (IsDebug) WriteLine($"角色 {character.Name} 回血:{recoveryHP:0.##} [{character.HP:0.##} / {character.MaxHP:0.##}] / 回蓝:{recoveryMP:0.##} [{character.MP:0.##} / {character.MaxMP:0.##}] / 当前能量:{character.EP:0.##}"); if (IsDebug) WriteLine($"角色 {character} 回血:{recoveryHP:0.##} [{character.HP:0.##} / {character.MaxHP:0.##}] / 回蓝:{recoveryMP:0.##} [{character.MP:0.##} / {character.MaxMP:0.##}] / 当前能量:{character.EP:0.##}");
} }
else else
{ {
if (reallyReHP > 0) if (reallyReHP > 0)
{ {
character.HP += reallyReHP; character.HP += reallyReHP;
if (IsDebug) WriteLine($"角色 {character.Name} 回血:{recoveryHP:0.##} [{character.HP:0.##} / {character.MaxHP:0.##}] / 当前能量:{character.EP:0.##}"); if (IsDebug) WriteLine($"角色 {character} 回血:{recoveryHP:0.##} [{character.HP:0.##} / {character.MaxHP:0.##}] / 当前能量:{character.EP:0.##}");
} }
if (reallyReMP > 0) if (reallyReMP > 0)
{ {
character.MP += reallyReMP; character.MP += reallyReMP;
if (IsDebug) WriteLine($"角色 {character.Name} 回蓝:{recoveryMP:0.##} [{character.MP:0.##} / {character.MaxMP:0.##}] / 当前能量:{character.EP:0.##}"); if (IsDebug) WriteLine($"角色 {character} 回蓝:{recoveryMP:0.##} [{character.MP:0.##} / {character.MaxMP:0.##}] / 当前能量:{character.EP:0.##}");
} }
} }
} }
@ -798,8 +862,16 @@ namespace Milimoe.FunGame.Core.Model
// 统计控制时长 // 统计控制时长
if (effect.Source != null && SkillSet.GetCharacterStateByEffectType(effect.EffectType) != CharacterState.Actionable) if (effect.Source != null && SkillSet.GetCharacterStateByEffectType(effect.EffectType) != CharacterState.Actionable)
{ {
_stats[effect.Source].ControlTime += timeToReduce; Character source = effect.Source;
_assistDetail[effect.Source][character, TotalTime] += 1; if (effect.Source.Master != null)
{
source = effect.Source.Master;
}
_stats[source].ControlTime += timeToReduce;
if (character.Master is null)
{
_assistDetail[source][character, TotalTime] += 1;
}
} }
if (effect.Durative) if (effect.Durative)
@ -878,6 +950,11 @@ namespace Milimoe.FunGame.Core.Model
_isInRound = true; _isInRound = true;
LastRound.Actor = character; LastRound.Actor = character;
_roundDeaths.Clear(); _roundDeaths.Clear();
Character statsCharacter = character;
if (character.Master != null)
{
statsCharacter = character.Master;
}
if (!BeforeTurn(character)) if (!BeforeTurn(character))
{ {
@ -899,7 +976,7 @@ namespace Milimoe.FunGame.Core.Model
List<Character> allTeammates = GetTeammates(character); List<Character> allTeammates = GetTeammates(character);
// 敌人列表 // 敌人列表
List<Character> allEnemys = [.. _allCharacters.Where(c => c != character && !allTeammates.Contains(c))]; List<Character> allEnemys = [.. _allCharacters.Union(_queue).Distinct().Where(c => c != character && !allTeammates.Contains(c) && !_eliminated.Contains(c) && c.Master != character && character.Master != c)];
// 取得可选列表 // 取得可选列表
(List<Character> selectableTeammates, List<Character> selectableEnemys, List<Skill> skills, List<Item> items) = GetTurnStartNeedyList(character, allTeammates, allEnemys); (List<Character> selectableTeammates, List<Character> selectableEnemys, List<Skill> skills, List<Item> items) = GetTurnStartNeedyList(character, allTeammates, allEnemys);
@ -941,7 +1018,7 @@ namespace Milimoe.FunGame.Core.Model
// 是否结束回合 // 是否结束回合
bool endTurn = false; bool endTurn = false;
bool isAI = CharactersInAI.Contains(character); bool isAI = IsCharacterInAIControlling(character);
// 循环条件未结束回合、决策点大于0AI控制下为0时自动结束或角色处于吟唱态 // 循环条件未结束回合、决策点大于0AI控制下为0时自动结束或角色处于吟唱态
while (!endTurn && (!isAI || dp.CurrentDecisionPoints > 0 || character.CharacterState == CharacterState.Casting || character.CharacterState == CharacterState.PreCastSuperSkill)) while (!endTurn && (!isAI || dp.CurrentDecisionPoints > 0 || character.CharacterState == CharacterState.Casting || character.CharacterState == CharacterState.PreCastSuperSkill))
@ -975,7 +1052,7 @@ namespace Milimoe.FunGame.Core.Model
// 循环条件: // 循环条件:
// AI 控制下未决策、取消次数大于0 // AI 控制下未决策、取消次数大于0
// 手动控制下:未决策 // 手动控制下:未决策
isAI = CharactersInAI.Contains(character); isAI = IsCharacterInAIControlling(character);
while (!decided && (!isAI || cancelTimes > 0)) while (!decided && (!isAI || cancelTimes > 0))
{ {
// 根据当前位置,更新可选取角色列表 // 根据当前位置,更新可选取角色列表
@ -1245,6 +1322,13 @@ namespace Milimoe.FunGame.Core.Model
else else
{ {
// 使用普通攻击逻辑 // 使用普通攻击逻辑
// 如果有询问,先进行询问
character.NormalAttack.GamingQueue = this;
if (character.NormalAttack.OnInquiryBeforeTargetSelection(character, character.NormalAttack) is InquiryOptions inquiry)
{
Inquiry(character, inquiry);
}
// 选择目标
List<Character> targets; List<Character> targets;
if (aiDecision != null) if (aiDecision != null)
{ {
@ -1265,8 +1349,8 @@ namespace Milimoe.FunGame.Core.Model
{ {
LastRound.Targets[CharacterActionType.NormalAttack] = [.. targets]; LastRound.Targets[CharacterActionType.NormalAttack] = [.. targets];
LastRound.ActionTypes.Add(CharacterActionType.NormalAttack); LastRound.ActionTypes.Add(CharacterActionType.NormalAttack);
_stats[character].UseDecisionPoints += costDP; _stats[statsCharacter].UseDecisionPoints += costDP;
_stats[character].TurnDecisions++; _stats[statsCharacter].TurnDecisions++;
dp.AddActionType(CharacterActionType.NormalAttack); dp.AddActionType(CharacterActionType.NormalAttack);
dp.CurrentDecisionPoints -= costDP; dp.CurrentDecisionPoints -= costDP;
decided = true; decided = true;
@ -1304,7 +1388,7 @@ namespace Milimoe.FunGame.Core.Model
{ {
skill = OnSelectSkillEvent(character, skills); skill = OnSelectSkillEvent(character, skills);
} }
if (skill is null && CharactersInAI.Contains(character) && skills.Count > 0) if (skill is null && IsCharacterInAIControlling(character) && skills.Count > 0)
{ {
skill = skills[Random.Shared.Next(skills.Count)]; skill = skills[Random.Shared.Next(skills.Count)];
} }
@ -1320,6 +1404,14 @@ namespace Milimoe.FunGame.Core.Model
} }
else if (skill.SkillType == SkillType.Magic) else if (skill.SkillType == SkillType.Magic)
{ {
if (CheckCanCast(character, skill, out double cost))
{
// 如果有询问,先进行询问
if (skill.InquiryBeforeTargetSelection(character, skill) is InquiryOptions inquiry)
{
Inquiry(character, inquiry);
}
// 吟唱前需要先选取目标 // 吟唱前需要先选取目标
List<Grid> castRange = []; List<Grid> castRange = [];
if (_map != null && realGrid != null) if (_map != null && realGrid != null)
@ -1341,8 +1433,8 @@ namespace Milimoe.FunGame.Core.Model
LastRound.Skills[CharacterActionType.PreCastSkill] = skill; LastRound.Skills[CharacterActionType.PreCastSkill] = skill;
LastRound.Targets[CharacterActionType.PreCastSkill] = [.. targets]; LastRound.Targets[CharacterActionType.PreCastSkill] = [.. targets];
LastRound.ActionTypes.Add(CharacterActionType.PreCastSkill); LastRound.ActionTypes.Add(CharacterActionType.PreCastSkill);
_stats[character].UseDecisionPoints += costDP; _stats[statsCharacter].UseDecisionPoints += costDP;
_stats[character].TurnDecisions++; _stats[statsCharacter].TurnDecisions++;
dp.AddActionType(CharacterActionType.PreCastSkill); dp.AddActionType(CharacterActionType.PreCastSkill);
dp.CurrentDecisionPoints -= costDP; dp.CurrentDecisionPoints -= costDP;
decided = true; decided = true;
@ -1362,6 +1454,7 @@ namespace Milimoe.FunGame.Core.Model
if (IsDebug) WriteLine($"[ {character} ] 想要吟唱 [ {skill.Name} ],但是没有目标!"); if (IsDebug) WriteLine($"[ {character} ] 想要吟唱 [ {skill.Name} ],但是没有目标!");
} }
} }
}
else if (skill is CourageCommandSkill && dp.CourageCommandSkill) else if (skill is CourageCommandSkill && dp.CourageCommandSkill)
{ {
if (IsDebug) WriteLine($"角色 [ {character} ] 该回合已经使用过勇气指令,无法再次使用勇气指令!"); if (IsDebug) WriteLine($"角色 [ {character} ] 该回合已经使用过勇气指令,无法再次使用勇气指令!");
@ -1379,6 +1472,12 @@ namespace Milimoe.FunGame.Core.Model
// 只有魔法需要吟唱,战技和爆发技直接释放 // 只有魔法需要吟唱,战技和爆发技直接释放
if (CheckCanCast(character, skill, out double cost)) if (CheckCanCast(character, skill, out double cost))
{ {
// 如果有询问,先进行询问
if (skill.InquiryBeforeTargetSelection(character, skill) is InquiryOptions inquiry)
{
Inquiry(character, inquiry);
}
List<Grid> castRange = []; List<Grid> castRange = [];
if (_map != null && realGrid != null) if (_map != null && realGrid != null)
{ {
@ -1402,8 +1501,8 @@ namespace Milimoe.FunGame.Core.Model
LastRound.ActionTypes.Add(skillType); LastRound.ActionTypes.Add(skillType);
if (skill is not CourageCommandSkill) if (skill is not CourageCommandSkill)
{ {
_stats[character].UseDecisionPoints += costDP; _stats[statsCharacter].UseDecisionPoints += costDP;
_stats[character].TurnDecisions++; _stats[statsCharacter].TurnDecisions++;
dp.AddActionType(skillType); dp.AddActionType(skillType);
dp.CurrentDecisionPoints -= costDP; dp.CurrentDecisionPoints -= costDP;
} }
@ -1513,6 +1612,8 @@ namespace Milimoe.FunGame.Core.Model
WriteLine($"[ {character} ] 想要释放 [ {skill.Name} ],但是没有目标!"); WriteLine($"[ {character} ] 想要释放 [ {skill.Name} ],但是没有目标!");
} }
WriteLine($"[ {character} ] 放弃释放技能!"); WriteLine($"[ {character} ] 放弃释放技能!");
character.CharacterState = CharacterState.Actionable;
character.UpdateCharacterState();
// 放弃释放技能会获得3的硬直时间 // 放弃释放技能会获得3的硬直时间
if (baseTime == 0) baseTime = 3; if (baseTime == 0) baseTime = 3;
decided = true; decided = true;
@ -1528,7 +1629,7 @@ namespace Milimoe.FunGame.Core.Model
} }
else if (type == CharacterActionType.CastSuperSkill) else if (type == CharacterActionType.CastSuperSkill)
{ {
_stats[character].TurnDecisions++; _stats[statsCharacter].TurnDecisions++;
dp.AddActionType(CharacterActionType.CastSuperSkill); dp.AddActionType(CharacterActionType.CastSuperSkill);
LastRound.ActionTypes.Add(CharacterActionType.CastSuperSkill); LastRound.ActionTypes.Add(CharacterActionType.CastSuperSkill);
decided = true; decided = true;
@ -1573,6 +1674,8 @@ namespace Milimoe.FunGame.Core.Model
else else
{ {
WriteLine($"[ {character} ] 因能量不足放弃释放爆发技!"); WriteLine($"[ {character} ] 因能量不足放弃释放爆发技!");
character.CharacterState = CharacterState.Actionable;
character.UpdateCharacterState();
// 放弃释放技能会获得3的硬直时间 // 放弃释放技能会获得3的硬直时间
if (baseTime == 0) baseTime = 3; if (baseTime == 0) baseTime = 3;
decided = true; decided = true;
@ -1591,7 +1694,7 @@ namespace Milimoe.FunGame.Core.Model
{ {
item = OnSelectItemEvent(character, items); item = OnSelectItemEvent(character, items);
} }
if (item is null && CharactersInAI.Contains(character) && items.Count > 0) if (item is null && IsCharacterInAIControlling(character) && items.Count > 0)
{ {
// AI 控制下随机选取一个物品 // AI 控制下随机选取一个物品
item = items[Random.Shared.Next(items.Count)]; item = items[Random.Shared.Next(items.Count)];
@ -1599,6 +1702,18 @@ 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 (item.InquiryBeforeTargetSelection(character, item) is InquiryOptions inquiry)
{
Inquiry(character, inquiry);
}
if (skill.InquiryBeforeTargetSelection(character, skill) is InquiryOptions inquiry2)
{
Inquiry(character, inquiry2);
}
List<Grid> castRange = []; List<Grid> castRange = [];
if (_map != null && realGrid != null) if (_map != null && realGrid != null)
{ {
@ -1616,8 +1731,8 @@ namespace Milimoe.FunGame.Core.Model
} }
else if (UseItem(item, character, dp, enemys, teammates, castRange, allEnemys, allTeammates, aiDecision)) else if (UseItem(item, character, dp, enemys, teammates, castRange, allEnemys, allTeammates, aiDecision))
{ {
_stats[character].UseDecisionPoints += costDP; _stats[statsCharacter].UseDecisionPoints += costDP;
_stats[character].TurnDecisions++; _stats[statsCharacter].TurnDecisions++;
dp.AddActionType(CharacterActionType.UseItem); dp.AddActionType(CharacterActionType.UseItem);
dp.CurrentDecisionPoints -= costDP; dp.CurrentDecisionPoints -= costDP;
LastRound.ActionTypes.Add(CharacterActionType.UseItem); LastRound.ActionTypes.Add(CharacterActionType.UseItem);
@ -1634,7 +1749,7 @@ namespace Milimoe.FunGame.Core.Model
} }
else if (type == CharacterActionType.EndTurn) else if (type == CharacterActionType.EndTurn)
{ {
_stats[character].TurnDecisions++; _stats[statsCharacter].TurnDecisions++;
SetOnlyMoveHardnessTime(character, dp, ref baseTime); SetOnlyMoveHardnessTime(character, dp, ref baseTime);
decided = true; decided = true;
endTurn = true; endTurn = true;
@ -1690,7 +1805,10 @@ namespace Milimoe.FunGame.Core.Model
baseTime = dp.ActionsTaken > 1 ? (dp.ActionsHardnessTime.Max() + dp.ActionsTaken) : dp.ActionsHardnessTime.Max(); baseTime = dp.ActionsTaken > 1 ? (dp.ActionsHardnessTime.Max() + dp.ActionsTaken) : dp.ActionsHardnessTime.Max();
} }
if (character.Master is null)
{
_stats[character].ActionTurn += 1; _stats[character].ActionTurn += 1;
}
AfterCharacterDecision(character, dp); AfterCharacterDecision(character, dp);
OnCharacterDecisionCompletedEvent(character, dp, LastRound); OnCharacterDecisionCompletedEvent(character, dp, LastRound);
@ -1784,9 +1902,11 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="character"></param> /// <param name="character"></param>
protected void ProcessCharacterDeath(Character character) protected void ProcessCharacterDeath(Character character)
{ {
foreach (Character death in _roundDeaths) foreach (Character death in _roundDeaths.Keys)
{ {
if (!OnCharacterDeathEvent(character, death)) Character[] assists = _roundDeaths[death];
if (!OnCharacterDeathEvent(character, death, assists))
{ {
continue; continue;
} }
@ -1795,12 +1915,12 @@ namespace Milimoe.FunGame.Core.Model
List<Effect> effects = [.. _queue.SelectMany(c => c.Effects.Where(e => e.IsInEffect))]; List<Effect> effects = [.. _queue.SelectMany(c => c.Effects.Where(e => e.IsInEffect))];
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
effect.AfterDeathCalculation(death, character, _continuousKilling, _earnedMoney); effect.AfterDeathCalculation(death, character, _continuousKilling, _earnedMoney, assists);
} }
// 将死者移出队列 // 将死者移出队列
_queue.Remove(death); _queue.Remove(death);
AfterDeathCalculation(death, character); AfterDeathCalculation(death, character, assists);
} }
} }
@ -1813,7 +1933,7 @@ namespace Milimoe.FunGame.Core.Model
protected virtual bool AfterCharacterAction(Character character, CharacterActionType type) protected virtual bool AfterCharacterAction(Character character, CharacterActionType type)
{ {
List<Character> allTeammates = GetTeammates(character); List<Character> allTeammates = GetTeammates(character);
Character[] allEnemys = [.. _allCharacters.Where(c => c != character && !allTeammates.Contains(c) && !_eliminated.Contains(c))]; Character[] allEnemys = [.. _allCharacters.Union(_queue).Distinct().Where(c => c != character && !allTeammates.Contains(c) && !_eliminated.Contains(c) && c.Master != character && character.Master != c)];
if (!allEnemys.Any(c => c.HP > 0)) if (!allEnemys.Any(c => c.HP > 0))
{ {
return false; return false;
@ -1846,9 +1966,10 @@ namespace Milimoe.FunGame.Core.Model
/// </summary> /// </summary>
/// <param name="death"></param> /// <param name="death"></param>
/// <param name="killer"></param> /// <param name="killer"></param>
protected virtual void AfterDeathCalculation(Character death, Character killer) /// <param name="assists"></param>
protected virtual void AfterDeathCalculation(Character death, Character killer, Character[] assists)
{ {
if (!_queue.Where(c => c != killer).Any()) if (!_queue.Any(c => c != killer && c.Master != killer && killer.Master != c))
{ {
// 没有其他的角色了,游戏结束 // 没有其他的角色了,游戏结束
WriteLine("[ " + killer + " ] 是胜利者。"); WriteLine("[ " + killer + " ] 是胜利者。");
@ -2173,11 +2294,19 @@ namespace Milimoe.FunGame.Core.Model
} }
// 统计护盾 // 统计护盾
if (damage > actualDamage && _stats.TryGetValue(actor, out CharacterStatistics? stats) && stats != null) if (damage > actualDamage)
{
Character statsCharacter = actor;
if (statsCharacter.Master != null)
{
statsCharacter = statsCharacter.Master;
}
if (_stats.TryGetValue(statsCharacter, out CharacterStatistics? stats) && stats != null)
{ {
stats.TotalShield += damage - actualDamage; stats.TotalShield += damage - actualDamage;
} }
} }
}
enemy.HP -= actualDamage; enemy.HP -= actualDamage;
string strDamageMessage = $"[ {enemy} ] 受到了 {actualDamage:0.##} 点{damageTypeString}{shieldMsg}"; string strDamageMessage = $"[ {enemy} ] 受到了 {actualDamage:0.##} 点{damageTypeString}{shieldMsg}";
@ -2224,11 +2353,22 @@ namespace Milimoe.FunGame.Core.Model
// 计算助攻 // 计算助攻
if (actor != enemy && !IsTeammate(actor, enemy)) if (actor != enemy && !IsTeammate(actor, enemy))
{
if (actor.Master != null)
{
actor = actor.Master;
}
if (enemy.Master != null)
{
enemy = enemy.Master;
}
if (actor != enemy)
{ {
_assistDetail[actor][enemy, TotalTime] += damage; _assistDetail[actor][enemy, TotalTime] += damage;
} }
} }
} }
}
else else
{ {
LastRound.IsEvaded[enemy] = true; LastRound.IsEvaded[enemy] = true;
@ -2249,7 +2389,7 @@ namespace Milimoe.FunGame.Core.Model
if (enemy.HP <= 0 && !_eliminated.Contains(enemy) && !_respawnCountdown.ContainsKey(enemy)) if (enemy.HP <= 0 && !_eliminated.Contains(enemy) && !_respawnCountdown.ContainsKey(enemy))
{ {
LastRound.HasKill = true; LastRound.HasKill = true;
_roundDeaths.Add(enemy); _roundDeaths.Add(enemy, []);
DeathCalculation(actor, enemy); DeathCalculation(actor, enemy);
} }
} }
@ -2261,6 +2401,26 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="death"></param> /// <param name="death"></param>
public void DeathCalculation(Character killer, Character death) public void DeathCalculation(Character killer, Character death)
{ {
if (killer == death)
{
if (!OnDeathCalculationEvent(killer, death))
{
return;
}
if (killer.Master is null)
{
_stats[death].Deaths += 1;
}
WriteLine($"[ {death} ] 自杀了!");
DealWithCharacterDied(killer, death);
return;
}
if (killer.Master != null)
{
killer = killer.Master;
}
if (IsTeammate(killer, death)) if (IsTeammate(killer, death))
{ {
DeathCalculationByTeammate(killer, death); DeathCalculationByTeammate(killer, death);
@ -2272,11 +2432,8 @@ namespace Milimoe.FunGame.Core.Model
return; return;
} }
if (killer == death) if (death.Master != null)
{ {
_stats[death].Deaths += 1;
WriteLine($"[ {death} ] 自杀了!");
DealWithCharacterDied(killer, death);
return; return;
} }
@ -2387,6 +2544,7 @@ namespace Milimoe.FunGame.Core.Model
} }
WriteLine(msg); WriteLine(msg);
} }
_roundDeaths[death] = assists;
if (FirstKiller is null) if (FirstKiller is null)
{ {
@ -2443,6 +2601,8 @@ namespace Milimoe.FunGame.Core.Model
return; return;
} }
if (death.Master is null)
{
_stats[death].Deaths += 1; _stats[death].Deaths += 1;
string msg = $"[ {killer} ] 反补了 [ {death} ][ {killer} ] 受到了击杀队友惩罚,扣除 200 {GameplayEquilibriumConstant.InGameCurrency}并且当前连杀计数减少一次!!"; string msg = $"[ {killer} ] 反补了 [ {death} ][ {killer} ] 受到了击杀队友惩罚,扣除 200 {GameplayEquilibriumConstant.InGameCurrency}并且当前连杀计数减少一次!!";
if (!_earnedMoney.TryAdd(killer, -200)) if (!_earnedMoney.TryAdd(killer, -200))
@ -2455,6 +2615,7 @@ namespace Milimoe.FunGame.Core.Model
} }
LastRound.DeathContinuousKilling.Add(msg); LastRound.DeathContinuousKilling.Add(msg);
WriteLine(msg); WriteLine(msg);
}
DealWithCharacterDied(killer, death); DealWithCharacterDied(killer, death);
} }
@ -2537,7 +2698,12 @@ namespace Milimoe.FunGame.Core.Model
SetNotDamageAssistTime(actor, target); SetNotDamageAssistTime(actor, target);
// 统计数据 // 统计数据
if (_stats.TryGetValue(actor, out CharacterStatistics? stats) && stats != null) Character statsCharacter = actor;
if (statsCharacter.Master != null)
{
statsCharacter = statsCharacter.Master;
}
if (_stats.TryGetValue(statsCharacter, out CharacterStatistics? stats) && stats != null)
{ {
stats.TotalHeal += heal; stats.TotalHeal += heal;
} }
@ -2629,6 +2795,8 @@ namespace Milimoe.FunGame.Core.Model
death.EP = 0; death.EP = 0;
if (death.Master is null)
{
// 清除对死者的助攻数据 // 清除对死者的助攻数据
List<AssistDetail> ads = [.. _assistDetail.Values.Where(ad => ad.Character != death)]; List<AssistDetail> ads = [.. _assistDetail.Values.Where(ad => ad.Character != death)];
foreach (AssistDetail ad in ads) foreach (AssistDetail ad in ads)
@ -2654,6 +2822,7 @@ namespace Milimoe.FunGame.Core.Model
LastRound.RespawnCountdowns.TryAdd(death, respawnTime); LastRound.RespawnCountdowns.TryAdd(death, respawnTime);
WriteLine($"[ {death} ] 进入复活倒计时:{respawnTime:0.##} {GameplayEquilibriumConstant.InGameTime}"); WriteLine($"[ {death} ] 进入复活倒计时:{respawnTime:0.##} {GameplayEquilibriumConstant.InGameTime}");
} }
}
// 移除死者的施法 // 移除死者的施法
_castingSkills.Remove(death); _castingSkills.Remove(death);
@ -2890,7 +3059,7 @@ namespace Milimoe.FunGame.Core.Model
effect.AlterSelectListBeforeSelection(caster, skill, enemys, teammates); effect.AlterSelectListBeforeSelection(caster, skill, enemys, teammates);
} }
List<Character> targets = OnSelectSkillTargetsEvent(caster, skill, enemys, teammates, castRange); List<Character> targets = OnSelectSkillTargetsEvent(caster, skill, enemys, teammates, castRange);
if (targets.Count == 0 && CharactersInAI.Contains(caster)) if (targets.Count == 0 && IsCharacterInAIControlling(caster))
{ {
targets = skill.SelectTargets(caster, enemys, teammates); targets = skill.SelectTargets(caster, enemys, teammates);
} }
@ -2909,7 +3078,7 @@ namespace Milimoe.FunGame.Core.Model
public List<Grid> SelectNonDirectionalSkillTargetGrid(Character caster, Skill skill, List<Character> enemys, List<Character> teammates, List<Grid> castRange) public List<Grid> SelectNonDirectionalSkillTargetGrid(Character caster, Skill skill, List<Character> enemys, List<Character> teammates, List<Grid> castRange)
{ {
List<Grid> targets = OnSelectNonDirectionalSkillTargetsEvent(caster, skill, enemys, teammates, castRange); List<Grid> targets = OnSelectNonDirectionalSkillTargetsEvent(caster, skill, enemys, teammates, castRange);
if (targets.Count == 0 && CharactersInAI.Contains(caster) && castRange.Count > 0) if (targets.Count == 0 && IsCharacterInAIControlling(caster) && castRange.Count > 0)
{ {
targets = skill.SelectNonDirectionalTargets(caster, castRange.OrderBy(r => Random.Shared.Next()).FirstOrDefault(r => r.Characters.Count > 0) ?? castRange.First(), skill.SelectIncludeCharacterGrid); targets = skill.SelectNonDirectionalTargets(caster, castRange.OrderBy(r => Random.Shared.Next()).FirstOrDefault(r => r.Characters.Count > 0) ?? castRange.First(), skill.SelectIncludeCharacterGrid);
} }
@ -2933,7 +3102,7 @@ namespace Milimoe.FunGame.Core.Model
effect.AlterSelectListBeforeSelection(character, attack, enemys, teammates); effect.AlterSelectListBeforeSelection(character, attack, enemys, teammates);
} }
List<Character> targets = OnSelectNormalAttackTargetsEvent(character, attack, enemys, teammates, attackRange); List<Character> targets = OnSelectNormalAttackTargetsEvent(character, attack, enemys, teammates, attackRange);
if (targets.Count == 0 && CharactersInAI.Contains(character)) if (targets.Count == 0 && IsCharacterInAIControlling(character))
{ {
targets = character.NormalAttack.SelectTargets(character, enemys, teammates); targets = character.NormalAttack.SelectTargets(character, enemys, teammates);
} }
@ -3291,7 +3460,7 @@ namespace Milimoe.FunGame.Core.Model
public List<Character> GetEnemies(Character character) public List<Character> GetEnemies(Character character)
{ {
List<Character> teammates = GetTeammates(character); List<Character> teammates = GetTeammates(character);
return [.. _allCharacters.Where(c => c != character && !teammates.Contains(c))]; return [.. _allCharacters.Union(_queue).Distinct().Where(c => c != character && !teammates.Contains(c) && !_eliminated.Contains(c) && c.Master != character && character.Master != c)];
} }
/// <summary> /// <summary>
@ -3301,7 +3470,7 @@ namespace Milimoe.FunGame.Core.Model
/// <returns></returns> /// <returns></returns>
public virtual List<Character> GetTeammates(Character character) public virtual List<Character> GetTeammates(Character character)
{ {
return []; return [.. _queue.Where(c => c != character && (character.Master == c || c == character.Master || (c.Master != null && character.Master != null && c.Master == character.Master)))];
} }
/// <summary> /// <summary>
@ -3313,7 +3482,7 @@ namespace Milimoe.FunGame.Core.Model
public bool IsTeammate(Character character, Character target) public bool IsTeammate(Character character, Character target)
{ {
List<Character> teammates = GetTeammates(character); List<Character> teammates = GetTeammates(character);
return teammates.Contains(target); return teammates.Contains(target) && (character.Master == target || target == character.Master || (target.Master != null && character.Master != null && target.Master == character.Master));
} }
/// <summary> /// <summary>
@ -3524,7 +3693,7 @@ namespace Milimoe.FunGame.Core.Model
protected void WillPreCastSuperSkill() protected void WillPreCastSuperSkill()
{ {
// 选取所有 AI 控制角色 // 选取所有 AI 控制角色
foreach (Character other in _queue.Where(c => c.CharacterState == CharacterState.Actionable && CharactersInAI.Contains(c)).ToList()) foreach (Character other in _queue.Where(c => c.CharacterState == CharacterState.Actionable && IsCharacterInAIControlling(c)).ToList())
{ {
if (!_decisionPoints.TryGetValue(other, out DecisionPoints? dp) || dp is null || dp.CurrentDecisionPoints < dp.GetActionPointCost(CharacterActionType.CastSuperSkill)) if (!_decisionPoints.TryGetValue(other, out DecisionPoints? dp) || dp is null || dp.CurrentDecisionPoints < dp.GetActionPointCost(CharacterActionType.CastSuperSkill))
{ {
@ -3595,13 +3764,14 @@ namespace Milimoe.FunGame.Core.Model
/// <param name="interrupter"></param> /// <param name="interrupter"></param>
public void InterruptCasting(Character interrupter) public void InterruptCasting(Character interrupter)
{ {
WriteLine($"[ {interrupter} ] 人间蒸发了。");
foreach (Character caster in _castingSkills.Keys) foreach (Character caster in _castingSkills.Keys)
{ {
SkillTarget skillTarget = _castingSkills[caster]; SkillTarget skillTarget = _castingSkills[caster];
if (skillTarget.Targets.Contains(interrupter)) if (skillTarget.Targets.Contains(interrupter))
{ {
Skill skill = skillTarget.Skill; Skill skill = skillTarget.Skill;
WriteLine($"[ {interrupter} ] 人间蒸发了。[ {caster} ] 丢失了施法目标!!"); WriteLine($"[ {caster} ] 丢失了施法目标!!");
List<Effect> effects = [.. caster.Effects.Union(interrupter.Effects).Distinct().Where(e => e.IsInEffect)]; List<Effect> effects = [.. caster.Effects.Union(interrupter.Effects).Distinct().Where(e => e.IsInEffect)];
foreach (Effect effect in effects) foreach (Effect effect in effects)
{ {
@ -3617,6 +3787,8 @@ namespace Milimoe.FunGame.Core.Model
/// </summary> /// </summary>
/// <param name="character"></param> /// <param name="character"></param>
public void SetCharacterRespawn(Character character) public void SetCharacterRespawn(Character character)
{
if (_original.TryGetValue(character.Guid, out Character? original) && original != null)
{ {
double hardnessTime = 5; double hardnessTime = 5;
character.Respawn(_original[character.Guid]); character.Respawn(_original[character.Guid]);
@ -3636,6 +3808,7 @@ namespace Milimoe.FunGame.Core.Model
} }
OnQueueUpdatedEvent(_queue, character, dp, hardnessTime, QueueUpdatedReason.Respawn, "设置角色复活后的硬直时间。"); OnQueueUpdatedEvent(_queue, character, dp, hardnessTime, QueueUpdatedReason.Respawn, "设置角色复活后的硬直时间。");
} }
}
/// <summary> /// <summary>
/// 设置角色将预释放爆发技 /// 设置角色将预释放爆发技
@ -3667,8 +3840,11 @@ namespace Milimoe.FunGame.Core.Model
if (character.CharacterState == CharacterState.Actionable) if (character.CharacterState == CharacterState.Actionable)
{ {
dp.CurrentDecisionPoints -= GameplayEquilibriumConstant.DecisionPointsCostSuperSkillOutOfTurn; dp.CurrentDecisionPoints -= GameplayEquilibriumConstant.DecisionPointsCostSuperSkillOutOfTurn;
_stats[character].UseDecisionPoints += GameplayEquilibriumConstant.DecisionPointsCostSuperSkillOutOfTurn; if (character.Master is Character statsCharacter)
_stats[character].TurnDecisions++; {
_stats[statsCharacter].UseDecisionPoints += GameplayEquilibriumConstant.DecisionPointsCostSuperSkillOutOfTurn;
_stats[statsCharacter].TurnDecisions++;
}
_castingSuperSkills[character] = skill; _castingSuperSkills[character] = skill;
character.CharacterState = CharacterState.PreCastSuperSkill; character.CharacterState = CharacterState.PreCastSuperSkill;
_queue.Remove(character); _queue.Remove(character);
@ -3719,7 +3895,17 @@ namespace Milimoe.FunGame.Core.Model
foreach (Character target in targets) foreach (Character target in targets)
{ {
if (character == target || IsTeammate(character, target)) continue; if (character == target || IsTeammate(character, target)) continue;
_assistDetail[character].NotDamageAssistLastTime[target] = TotalTime; Character c = character, t = target;
if (character.Master != null)
{
c = character.Master;
}
if (target.Master != null)
{
t = target.Master;
}
if (c == t) continue;
_assistDetail[c].NotDamageAssistLastTime[t] = TotalTime;
} }
} }
@ -3736,7 +3922,10 @@ namespace Milimoe.FunGame.Core.Model
{ {
return; return;
} }
double hardnessTime = _hardnessTimes[character]; if (!_hardnessTimes.TryGetValue(character, out double hardnessTime))
{
hardnessTime = 0;
}
if (isPercentage) if (isPercentage)
{ {
addValue = hardnessTime * addValue; addValue = hardnessTime * addValue;
@ -3802,6 +3991,14 @@ namespace Milimoe.FunGame.Core.Model
/// <returns></returns> /// <returns></returns>
public bool IsCharacterInAIControlling(Character character) public bool IsCharacterInAIControlling(Character character)
{ {
if (character.Master != null)
{
if (_charactersInAIBySystem.Contains(character))
{
return true;
}
character = character.Master;
}
return CharactersInAI.Contains(character); return CharactersInAI.Contains(character);
} }
@ -3916,8 +4113,27 @@ namespace Milimoe.FunGame.Core.Model
PrimaryAttribute.INT => character.INTExemption, PrimaryAttribute.INT => character.INTExemption,
_ => 0 _ => 0
}; };
bool exempted = false;
bool checkExempted = true;
double throwingBonus = 0;
Character[] characters = source != null ? [character, source] : [character];
Effect[] effects = [.. characters.SelectMany(c => c.Effects.Where(e => e.IsInEffect)).Distinct()];
foreach (Effect e in effects)
{
if (!e.OnExemptionCheck(character, source, effect, isEvade, ref throwingBonus))
{
checkExempted = false;
}
}
if (checkExempted)
{
double dice = Random.Shared.NextDouble(); double dice = Random.Shared.NextDouble();
if (dice < exemption) if (dice < (exemption + throwingBonus))
{
exempted = true;
}
}
if (exempted)
{ {
if (isEvade) if (isEvade)
{ {
@ -3949,9 +4165,8 @@ namespace Milimoe.FunGame.Core.Model
WriteLine($"[ {character} ] 的{CharacterSet.GetPrimaryAttributeName(effect.ExemptionType)}豁免检定通过!{description}"); WriteLine($"[ {character} ] 的{CharacterSet.GetPrimaryAttributeName(effect.ExemptionType)}豁免检定通过!{description}");
} }
OnCharacterExemptionEvent(character, source, effect.Skill, effect.Skill.Item, isEvade); OnCharacterExemptionEvent(character, source, effect.Skill, effect.Skill.Item, isEvade);
return true;
} }
return false; return exempted;
} }
#endregion #endregion
@ -3964,6 +4179,10 @@ namespace Milimoe.FunGame.Core.Model
public void CalculateCharacterDamageStatistics(Character character, Character characterTaken, double damage, DamageType damageType, double takenDamage = -1) public void CalculateCharacterDamageStatistics(Character character, Character characterTaken, double damage, DamageType damageType, double takenDamage = -1)
{ {
if (takenDamage == -1) takenDamage = damage; if (takenDamage == -1) takenDamage = damage;
if (character.Master != null)
{
character = character.Master;
}
if (_stats.TryGetValue(character, out CharacterStatistics? stats) && stats != null) if (_stats.TryGetValue(character, out CharacterStatistics? stats) && stats != null)
{ {
if (damageType == DamageType.True) if (damageType == DamageType.True)
@ -4294,7 +4513,7 @@ namespace Milimoe.FunGame.Core.Model
return DeathCalculationByTeammateEvent?.Invoke(this, killer, death) ?? true; return DeathCalculationByTeammateEvent?.Invoke(this, killer, death) ?? true;
} }
public delegate bool CharacterDeathEventHandler(GamingQueue queue, Character current, Character death); public delegate bool CharacterDeathEventHandler(GamingQueue queue, Character current, Character death, Character[] assists);
/// <summary> /// <summary>
/// 角色死亡事件,此事件位于 <see cref="DeathCalculation"/> 之后 /// 角色死亡事件,此事件位于 <see cref="DeathCalculation"/> 之后
/// </summary> /// </summary>
@ -4304,10 +4523,11 @@ namespace Milimoe.FunGame.Core.Model
/// </summary> /// </summary>
/// <param name="current"></param> /// <param name="current"></param>
/// <param name="death"></param> /// <param name="death"></param>
/// <param name="assists"></param>
/// <returns></returns> /// <returns></returns>
protected bool OnCharacterDeathEvent(Character current, Character death) protected bool OnCharacterDeathEvent(Character current, Character death, Character[] assists)
{ {
return CharacterDeathEvent?.Invoke(this, current, death) ?? true; return CharacterDeathEvent?.Invoke(this, current, death, assists) ?? true;
} }
public delegate void HealToTargetEventHandler(GamingQueue queue, Character actor, Character target, double heal, bool isRespawn); public delegate void HealToTargetEventHandler(GamingQueue queue, Character actor, Character target, double heal, bool isRespawn);
@ -4596,7 +4816,7 @@ namespace Milimoe.FunGame.Core.Model
CharacterDecisionCompletedEvent?.Invoke(this, actor, dp, record); CharacterDecisionCompletedEvent?.Invoke(this, actor, dp, record);
} }
public delegate InquiryResponse CharacterInquiryEventHandler(GamingQueue character, Character actor, DecisionPoints dp, InquiryOptions options); public delegate InquiryResponse CharacterInquiryEventHandler(GamingQueue queue, Character character, DecisionPoints dp, InquiryOptions options);
/// <summary> /// <summary>
/// 角色询问反应事件 /// 角色询问反应事件
/// </summary> /// </summary>

View File

@ -13,8 +13,9 @@ namespace Milimoe.FunGame.Core.Model
/// </summary> /// </summary>
/// <param name="death"></param> /// <param name="death"></param>
/// <param name="killer"></param> /// <param name="killer"></param>
/// <param name="assists"></param>
/// <returns></returns> /// <returns></returns>
protected override void AfterDeathCalculation(Character death, Character killer) protected override void AfterDeathCalculation(Character death, Character killer, Character[] assists)
{ {
if (MaxRespawnTimes != 0 && MaxScoreToWin > 0) if (MaxRespawnTimes != 0 && MaxScoreToWin > 0)
{ {
@ -22,7 +23,7 @@ namespace Milimoe.FunGame.Core.Model
.Select(kv => $"[ {kv.Key} ] {kv.Value.Kills} 分"))}\r\n剩余存活人数{_queue.Count}"); .Select(kv => $"[ {kv.Key} ] {kv.Value.Kills} 分"))}\r\n剩余存活人数{_queue.Count}");
} }
if (!_queue.Where(c => c != killer).Any()) if (!_queue.Any(c => c != killer && c.Master != killer && killer.Master != c))
{ {
// 没有其他的角色了,游戏结束 // 没有其他的角色了,游戏结束
EndGameInfo(killer); EndGameInfo(killer);

View File

@ -83,7 +83,9 @@ namespace Milimoe.FunGame.Core.Model
{ {
if (_teams[team].IsOnThisTeam(character)) if (_teams[team].IsOnThisTeam(character))
{ {
return _teams[team].GetTeammates(character); List<Character> list = _teams[team].GetTeammates(character);
list.AddRange(_queue.Where(c => c.Master != null && _teams[team].IsOnThisTeam(c.Master)));
return list;
} }
} }
return []; return [];
@ -155,8 +157,9 @@ namespace Milimoe.FunGame.Core.Model
/// </summary> /// </summary>
/// <param name="death"></param> /// <param name="death"></param>
/// <param name="killer"></param> /// <param name="killer"></param>
/// <param name="assists"></param>
/// <returns></returns> /// <returns></returns>
protected override void AfterDeathCalculation(Character death, Character killer) protected override void AfterDeathCalculation(Character death, Character killer, Character[] assists)
{ {
Team? killTeam = GetTeam(killer); Team? killTeam = GetTeam(killer);
Team? deathTeam = GetTeam(death); Team? deathTeam = GetTeam(death);