diff --git a/Entity/Item/Item.cs b/Entity/Item/Item.cs
index 8d5ce3c..0b59c15 100644
--- a/Entity/Item/Item.cs
+++ b/Entity/Item/Item.cs
@@ -307,6 +307,17 @@ namespace Milimoe.FunGame.Core.Entity
}
}
+ ///
+ /// 在选取目标前向角色(玩家)发起询问
+ ///
+ ///
+ ///
+ ///
+ public virtual InquiryOptions? InquiryBeforeTargetSelection(Character character, Item item)
+ {
+ return null;
+ }
+
///
/// 局内使用物品触发
///
diff --git a/Entity/Skill/Effect.cs b/Entity/Skill/Effect.cs
index 57755bb..e1142a8 100644
--- a/Entity/Skill/Effect.cs
+++ b/Entity/Skill/Effect.cs
@@ -546,7 +546,8 @@ namespace Milimoe.FunGame.Core.Entity
///
///
///
- public virtual void AfterDeathCalculation(Character death, Character? killer, Dictionary continuousKilling, Dictionary earnedMoney)
+ ///
+ public virtual void AfterDeathCalculation(Character death, Character? killer, Dictionary continuousKilling, Dictionary earnedMoney, Character[] assists)
{
}
@@ -865,6 +866,20 @@ namespace Milimoe.FunGame.Core.Entity
return true;
}
+ ///
+ /// 在特效豁免检定时
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// false:跳过豁免检定
+ public virtual bool OnExemptionCheck(Character character, Character? source, Effect effect, bool isEvade, ref double throwingBonus)
+ {
+ return true;
+ }
+
///
/// 在角色行动后触发
///
diff --git a/Entity/Skill/NormalAttack.cs b/Entity/Skill/NormalAttack.cs
index dae84a4..bb5dace 100644
--- a/Entity/Skill/NormalAttack.cs
+++ b/Entity/Skill/NormalAttack.cs
@@ -478,6 +478,24 @@ namespace Milimoe.FunGame.Core.Entity
///
public override string ToString() => GetInfo(true);
+ ///
+ /// 在选取目标前向角色(玩家)发起询问的事件
+ ///
+ ///
+ ///
+ ///
+ public delegate InquiryOptions? NormalAttackInquiryOptionsDelegate(Character character, NormalAttack normalAttack);
+ public event NormalAttackInquiryOptionsDelegate? InquiryBeforeTargetSelectionEvent;
+ ///
+ /// 触发选择目标前的询问事件
+ ///
+ ///
+ ///
+ public InquiryOptions? OnInquiryBeforeTargetSelection(Character character, NormalAttack normalAttack)
+ {
+ return InquiryBeforeTargetSelectionEvent?.Invoke(character, normalAttack);
+ }
+
///
/// 等级
///
diff --git a/Entity/Skill/Skill.cs b/Entity/Skill/Skill.cs
index b389010..b2b3757 100644
--- a/Entity/Skill/Skill.cs
+++ b/Entity/Skill/Skill.cs
@@ -4,6 +4,7 @@ using Milimoe.FunGame.Core.Interface.Base;
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.Entity
{
@@ -379,6 +380,17 @@ namespace Milimoe.FunGame.Core.Entity
}
+ ///
+ /// 在选取目标前向角色(玩家)发起询问
+ ///
+ ///
+ ///
+ ///
+ public virtual InquiryOptions? InquiryBeforeTargetSelection(Character character, Skill skill)
+ {
+ return null;
+ }
+
///
/// 获取可选择的目标列表
///
diff --git a/Entity/System/Team.cs b/Entity/System/Team.cs
index af41865..6441185 100644
--- a/Entity/System/Team.cs
+++ b/Entity/System/Team.cs
@@ -26,6 +26,10 @@
public bool IsOnThisTeam(Character character)
{
+ if (character.Master != null)
+ {
+ character = character.Master;
+ }
return Members.Contains(character);
}
diff --git a/Model/GamingQueue.cs b/Model/GamingQueue.cs
index b21ba66..d372122 100644
--- a/Model/GamingQueue.cs
+++ b/Model/GamingQueue.cs
@@ -258,9 +258,9 @@ namespace Milimoe.FunGame.Core.Model
protected readonly Dictionary _respawnCountdown = [];
///
- /// 当前回合死亡角色
+ /// 当前回合死亡角色和参与击杀的人
///
- protected readonly List _roundDeaths = [];
+ protected readonly Dictionary _roundDeaths = [];
///
/// 回合奖励
@@ -352,6 +352,45 @@ namespace Milimoe.FunGame.Core.Model
_map = map.InitGamingQueue(this);
}
+ ///
+ /// 将角色从地图上移除
+ ///
+ public void RemoveCharacterFromMap(params IEnumerable 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);
+ }
+ }
+ }
+
+ ///
+ /// 将角色拥有的单位从地图上移除
+ ///
+ public void RemoveCharactersUnitFromMap(params IEnumerable characters)
+ {
+ if (Map is null)
+ {
+ return;
+ }
+
+ foreach (Character character in characters)
+ {
+ Character[] willRemove = [.. _queue.Where(c => c.Master == character)];
+ RemoveCharacterFromMap(willRemove);
+ }
+ }
+
#endregion
#region 队列框架
@@ -365,6 +404,7 @@ namespace Milimoe.FunGame.Core.Model
// 保存原始的角色信息。用于复活时还原状态
foreach (Character character in characters)
{
+ if (character.IsUnit) continue;
// 添加角色引用到所有角色列表
_allCharacters.Add(character);
// 复制原始角色对象
@@ -384,7 +424,7 @@ namespace Milimoe.FunGame.Core.Model
}
// 获取 HP 小于等于 0 的角色
- List deadCharacters = [.. characters.Where(c => c.HP <= 0)];
+ List deadCharacters = [.. characters.Where(c => c.HP <= 0 && !c.IsUnit)];
foreach (Character death in deadCharacters)
{
_eliminated.Add(death);
@@ -588,6 +628,9 @@ namespace Milimoe.FunGame.Core.Model
}
}
}
+
+ // 重新排序
+ _queue.Sort((a, b) => _hardnessTimes[a].CompareTo(_hardnessTimes[b]));
}
///
@@ -615,6 +658,19 @@ namespace Milimoe.FunGame.Core.Model
_charactersInAIByUser.Clear();
}
+ ///
+ /// 将角色彻底移出行动顺序表
+ ///
+ ///
+ public void RemoveCharacterFromQueue(params IEnumerable characters)
+ {
+ foreach (Character character in characters)
+ {
+ _queue.Remove(character);
+ _hardnessTimes.Remove(character);
+ }
+ }
+
#endregion
#region 时间流逝
@@ -696,11 +752,19 @@ namespace Milimoe.FunGame.Core.Model
}
// 统计
- _stats[character].LiveRound += 1;
- _stats[character].LiveTime += timeToReduce;
- _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;
- _stats[character].DamagePerSecond = _stats[character].LiveTime == 0 ? 0 : _stats[character].TotalDamage / _stats[character].LiveTime;
+ Character statsCharacter = character;
+ if (character.Master != null)
+ {
+ statsCharacter = character.Master;
+ }
+ 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;
@@ -725,19 +789,19 @@ namespace Milimoe.FunGame.Core.Model
{
character.HP += reallyReHP;
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
{
if (reallyReHP > 0)
{
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)
{
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)
{
- _stats[effect.Source].ControlTime += timeToReduce;
- _assistDetail[effect.Source][character, TotalTime] += 1;
+ Character source = effect.Source;
+ 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)
@@ -878,6 +950,11 @@ namespace Milimoe.FunGame.Core.Model
_isInRound = true;
LastRound.Actor = character;
_roundDeaths.Clear();
+ Character statsCharacter = character;
+ if (character.Master != null)
+ {
+ statsCharacter = character.Master;
+ }
if (!BeforeTurn(character))
{
@@ -899,7 +976,7 @@ namespace Milimoe.FunGame.Core.Model
List allTeammates = GetTeammates(character);
// 敌人列表
- List allEnemys = [.. _allCharacters.Where(c => c != character && !allTeammates.Contains(c))];
+ List allEnemys = [.. _allCharacters.Union(_queue).Distinct().Where(c => c != character && !allTeammates.Contains(c) && !_eliminated.Contains(c) && c.Master != character && character.Master != c)];
// 取得可选列表
(List selectableTeammates, List selectableEnemys, List skills, List- items) = GetTurnStartNeedyList(character, allTeammates, allEnemys);
@@ -941,7 +1018,7 @@ namespace Milimoe.FunGame.Core.Model
// 是否结束回合
bool endTurn = false;
- bool isAI = CharactersInAI.Contains(character);
+ bool isAI = IsCharacterInAIControlling(character);
// 循环条件:未结束回合、决策点大于0(AI控制下为0时自动结束)或角色处于吟唱态
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
// 手动控制下:未决策
- isAI = CharactersInAI.Contains(character);
+ isAI = IsCharacterInAIControlling(character);
while (!decided && (!isAI || cancelTimes > 0))
{
// 根据当前位置,更新可选取角色列表
@@ -1245,6 +1322,13 @@ namespace Milimoe.FunGame.Core.Model
else
{
// 使用普通攻击逻辑
+ // 如果有询问,先进行询问
+ character.NormalAttack.GamingQueue = this;
+ if (character.NormalAttack.OnInquiryBeforeTargetSelection(character, character.NormalAttack) is InquiryOptions inquiry)
+ {
+ Inquiry(character, inquiry);
+ }
+ // 选择目标
List targets;
if (aiDecision != null)
{
@@ -1265,8 +1349,8 @@ namespace Milimoe.FunGame.Core.Model
{
LastRound.Targets[CharacterActionType.NormalAttack] = [.. targets];
LastRound.ActionTypes.Add(CharacterActionType.NormalAttack);
- _stats[character].UseDecisionPoints += costDP;
- _stats[character].TurnDecisions++;
+ _stats[statsCharacter].UseDecisionPoints += costDP;
+ _stats[statsCharacter].TurnDecisions++;
dp.AddActionType(CharacterActionType.NormalAttack);
dp.CurrentDecisionPoints -= costDP;
decided = true;
@@ -1304,7 +1388,7 @@ namespace Milimoe.FunGame.Core.Model
{
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)];
}
@@ -1320,46 +1404,55 @@ namespace Milimoe.FunGame.Core.Model
}
else if (skill.SkillType == SkillType.Magic)
{
- // 吟唱前需要先选取目标
- List castRange = [];
- if (_map != null && realGrid != null)
+ if (CheckCanCast(character, skill, out double cost))
{
- 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, grids) = GetSelectedSkillTargetsList(character, skill, enemys, teammates, castRange, allEnemys, allTeammates, aiDecision);
+ // 如果有询问,先进行询问
+ if (skill.InquiryBeforeTargetSelection(character, skill) is InquiryOptions inquiry)
+ {
+ Inquiry(character, inquiry);
+ }
- if (targets.Count > 0)
- {
- // 免疫检定
- CheckSkilledImmune(character, targets, skill);
- }
- bool hasTarget = targets.Count > 0 || (skill.IsNonDirectional && grids.Count > 0 && (skill.AllowSelectNoCharacterGrid || !skill.AllowSelectNoCharacterGrid && targets.Count > 0));
- if (hasTarget)
- {
- LastRound.Skills[CharacterActionType.PreCastSkill] = skill;
- LastRound.Targets[CharacterActionType.PreCastSkill] = [.. targets];
- LastRound.ActionTypes.Add(CharacterActionType.PreCastSkill);
- _stats[character].UseDecisionPoints += costDP;
- _stats[character].TurnDecisions++;
- dp.AddActionType(CharacterActionType.PreCastSkill);
- dp.CurrentDecisionPoints -= costDP;
- decided = true;
- endTurn = true;
+ // 吟唱前需要先选取目标
+ List 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, grids) = GetSelectedSkillTargetsList(character, skill, enemys, teammates, castRange, allEnemys, allTeammates, aiDecision);
- character.CharacterState = CharacterState.Casting;
- SkillTarget skillTarget = new(skill, targets, grids);
- OnCharacterPreCastSkillEvent(character, dp, skillTarget);
+ if (targets.Count > 0)
+ {
+ // 免疫检定
+ CheckSkilledImmune(character, targets, skill);
+ }
+ bool hasTarget = targets.Count > 0 || (skill.IsNonDirectional && grids.Count > 0 && (skill.AllowSelectNoCharacterGrid || !skill.AllowSelectNoCharacterGrid && targets.Count > 0));
+ if (hasTarget)
+ {
+ LastRound.Skills[CharacterActionType.PreCastSkill] = skill;
+ LastRound.Targets[CharacterActionType.PreCastSkill] = [.. targets];
+ LastRound.ActionTypes.Add(CharacterActionType.PreCastSkill);
+ _stats[statsCharacter].UseDecisionPoints += costDP;
+ _stats[statsCharacter].TurnDecisions++;
+ dp.AddActionType(CharacterActionType.PreCastSkill);
+ dp.CurrentDecisionPoints -= costDP;
+ decided = true;
+ endTurn = true;
- _castingSkills[character] = skillTarget;
- baseTime += skill.RealCastTime;
- isCheckProtected = false;
- skill.OnSkillCasting(this, character, targets, grids);
- }
- else
- {
- if (IsDebug) WriteLine($"[ {character} ] 想要吟唱 [ {skill.Name} ],但是没有目标!");
+ character.CharacterState = CharacterState.Casting;
+ SkillTarget skillTarget = new(skill, targets, grids);
+ OnCharacterPreCastSkillEvent(character, dp, skillTarget);
+
+ _castingSkills[character] = skillTarget;
+ baseTime += skill.RealCastTime;
+ isCheckProtected = false;
+ skill.OnSkillCasting(this, character, targets, grids);
+ }
+ else
+ {
+ if (IsDebug) WriteLine($"[ {character} ] 想要吟唱 [ {skill.Name} ],但是没有目标!");
+ }
}
}
else if (skill is CourageCommandSkill && dp.CourageCommandSkill)
@@ -1379,6 +1472,12 @@ namespace Milimoe.FunGame.Core.Model
// 只有魔法需要吟唱,战技和爆发技直接释放
if (CheckCanCast(character, skill, out double cost))
{
+ // 如果有询问,先进行询问
+ if (skill.InquiryBeforeTargetSelection(character, skill) is InquiryOptions inquiry)
+ {
+ Inquiry(character, inquiry);
+ }
+
List castRange = [];
if (_map != null && realGrid != null)
{
@@ -1402,8 +1501,8 @@ namespace Milimoe.FunGame.Core.Model
LastRound.ActionTypes.Add(skillType);
if (skill is not CourageCommandSkill)
{
- _stats[character].UseDecisionPoints += costDP;
- _stats[character].TurnDecisions++;
+ _stats[statsCharacter].UseDecisionPoints += costDP;
+ _stats[statsCharacter].TurnDecisions++;
dp.AddActionType(skillType);
dp.CurrentDecisionPoints -= costDP;
}
@@ -1513,6 +1612,8 @@ namespace Milimoe.FunGame.Core.Model
WriteLine($"[ {character} ] 想要释放 [ {skill.Name} ],但是没有目标!");
}
WriteLine($"[ {character} ] 放弃释放技能!");
+ character.CharacterState = CharacterState.Actionable;
+ character.UpdateCharacterState();
// 放弃释放技能会获得3的硬直时间
if (baseTime == 0) baseTime = 3;
decided = true;
@@ -1528,7 +1629,7 @@ namespace Milimoe.FunGame.Core.Model
}
else if (type == CharacterActionType.CastSuperSkill)
{
- _stats[character].TurnDecisions++;
+ _stats[statsCharacter].TurnDecisions++;
dp.AddActionType(CharacterActionType.CastSuperSkill);
LastRound.ActionTypes.Add(CharacterActionType.CastSuperSkill);
decided = true;
@@ -1573,6 +1674,8 @@ namespace Milimoe.FunGame.Core.Model
else
{
WriteLine($"[ {character} ] 因能量不足放弃释放爆发技!");
+ character.CharacterState = CharacterState.Actionable;
+ character.UpdateCharacterState();
// 放弃释放技能会获得3的硬直时间
if (baseTime == 0) baseTime = 3;
decided = true;
@@ -1591,7 +1694,7 @@ namespace Milimoe.FunGame.Core.Model
{
item = OnSelectItemEvent(character, items);
}
- if (item is null && CharactersInAI.Contains(character) && items.Count > 0)
+ if (item is null && IsCharacterInAIControlling(character) && items.Count > 0)
{
// AI 控制下随机选取一个物品
item = items[Random.Shared.Next(items.Count)];
@@ -1599,6 +1702,18 @@ namespace Milimoe.FunGame.Core.Model
if (item != null && item.Skills.Active != null)
{
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 castRange = [];
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))
{
- _stats[character].UseDecisionPoints += costDP;
- _stats[character].TurnDecisions++;
+ _stats[statsCharacter].UseDecisionPoints += costDP;
+ _stats[statsCharacter].TurnDecisions++;
dp.AddActionType(CharacterActionType.UseItem);
dp.CurrentDecisionPoints -= costDP;
LastRound.ActionTypes.Add(CharacterActionType.UseItem);
@@ -1634,7 +1749,7 @@ namespace Milimoe.FunGame.Core.Model
}
else if (type == CharacterActionType.EndTurn)
{
- _stats[character].TurnDecisions++;
+ _stats[statsCharacter].TurnDecisions++;
SetOnlyMoveHardnessTime(character, dp, ref baseTime);
decided = true;
endTurn = true;
@@ -1690,7 +1805,10 @@ namespace Milimoe.FunGame.Core.Model
baseTime = dp.ActionsTaken > 1 ? (dp.ActionsHardnessTime.Max() + dp.ActionsTaken) : dp.ActionsHardnessTime.Max();
}
- _stats[character].ActionTurn += 1;
+ if (character.Master is null)
+ {
+ _stats[character].ActionTurn += 1;
+ }
AfterCharacterDecision(character, dp);
OnCharacterDecisionCompletedEvent(character, dp, LastRound);
@@ -1784,9 +1902,11 @@ namespace Milimoe.FunGame.Core.Model
///
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;
}
@@ -1795,12 +1915,12 @@ namespace Milimoe.FunGame.Core.Model
List effects = [.. _queue.SelectMany(c => c.Effects.Where(e => e.IsInEffect))];
foreach (Effect effect in effects)
{
- effect.AfterDeathCalculation(death, character, _continuousKilling, _earnedMoney);
+ effect.AfterDeathCalculation(death, character, _continuousKilling, _earnedMoney, assists);
}
// 将死者移出队列
_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)
{
List 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))
{
return false;
@@ -1846,9 +1966,10 @@ namespace Milimoe.FunGame.Core.Model
///
///
///
- protected virtual void AfterDeathCalculation(Character death, Character killer)
+ ///
+ 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 + " ] 是胜利者。");
@@ -2173,9 +2294,17 @@ namespace Milimoe.FunGame.Core.Model
}
// 统计护盾
- if (damage > actualDamage && _stats.TryGetValue(actor, out CharacterStatistics? stats) && stats != null)
+ if (damage > actualDamage)
{
- stats.TotalShield += 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;
+ }
}
}
@@ -2225,7 +2354,18 @@ namespace Milimoe.FunGame.Core.Model
// 计算助攻
if (actor != enemy && !IsTeammate(actor, enemy))
{
- _assistDetail[actor][enemy, TotalTime] += damage;
+ if (actor.Master != null)
+ {
+ actor = actor.Master;
+ }
+ if (enemy.Master != null)
+ {
+ enemy = enemy.Master;
+ }
+ if (actor != enemy)
+ {
+ _assistDetail[actor][enemy, TotalTime] += damage;
+ }
}
}
}
@@ -2249,7 +2389,7 @@ namespace Milimoe.FunGame.Core.Model
if (enemy.HP <= 0 && !_eliminated.Contains(enemy) && !_respawnCountdown.ContainsKey(enemy))
{
LastRound.HasKill = true;
- _roundDeaths.Add(enemy);
+ _roundDeaths.Add(enemy, []);
DeathCalculation(actor, enemy);
}
}
@@ -2261,6 +2401,26 @@ namespace Milimoe.FunGame.Core.Model
///
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))
{
DeathCalculationByTeammate(killer, death);
@@ -2272,11 +2432,8 @@ namespace Milimoe.FunGame.Core.Model
return;
}
- if (killer == death)
+ if (death.Master != null)
{
- _stats[death].Deaths += 1;
- WriteLine($"[ {death} ] 自杀了!");
- DealWithCharacterDied(killer, death);
return;
}
@@ -2387,6 +2544,7 @@ namespace Milimoe.FunGame.Core.Model
}
WriteLine(msg);
}
+ _roundDeaths[death] = assists;
if (FirstKiller is null)
{
@@ -2443,18 +2601,21 @@ namespace Milimoe.FunGame.Core.Model
return;
}
- _stats[death].Deaths += 1;
- string msg = $"[ {killer} ] 反补了 [ {death} ]![ {killer} ] 受到了击杀队友惩罚,扣除 200 {GameplayEquilibriumConstant.InGameCurrency}并且当前连杀计数减少一次!!";
- if (!_earnedMoney.TryAdd(killer, -200))
+ if (death.Master is null)
{
- _earnedMoney[killer] -= 200;
+ _stats[death].Deaths += 1;
+ string msg = $"[ {killer} ] 反补了 [ {death} ]![ {killer} ] 受到了击杀队友惩罚,扣除 200 {GameplayEquilibriumConstant.InGameCurrency}并且当前连杀计数减少一次!!";
+ if (!_earnedMoney.TryAdd(killer, -200))
+ {
+ _earnedMoney[killer] -= 200;
+ }
+ if (_continuousKilling.TryGetValue(killer, out int times) && times > 0)
+ {
+ _continuousKilling[killer] -= 1;
+ }
+ LastRound.DeathContinuousKilling.Add(msg);
+ WriteLine(msg);
}
- if (_continuousKilling.TryGetValue(killer, out int times) && times > 0)
- {
- _continuousKilling[killer] -= 1;
- }
- LastRound.DeathContinuousKilling.Add(msg);
- WriteLine(msg);
DealWithCharacterDied(killer, death);
}
@@ -2537,7 +2698,12 @@ namespace Milimoe.FunGame.Core.Model
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;
}
@@ -2629,30 +2795,33 @@ namespace Milimoe.FunGame.Core.Model
death.EP = 0;
- // 清除对死者的助攻数据
- List ads = [.. _assistDetail.Values.Where(ad => ad.Character != death)];
- foreach (AssistDetail ad in ads)
+ if (death.Master is null)
{
- ad[death, 0] = 0;
- }
+ // 清除对死者的助攻数据
+ List ads = [.. _assistDetail.Values.Where(ad => ad.Character != death)];
+ foreach (AssistDetail ad in ads)
+ {
+ ad[death, 0] = 0;
+ }
- _continuousKilling.Remove(death);
- _eliminated.Add(death);
- if (MaxRespawnTimes == 0)
- {
- // do nothing
- }
- else if (_respawnTimes.TryGetValue(death, out int times) && MaxRespawnTimes != -1 && times == MaxRespawnTimes)
- {
- WriteLine($"[ {death} ] 已达到复活次数上限,将不能再复活!!");
- }
- else
- {
- // 进入复活倒计时
- double respawnTime = GetRespawnTime(death, times);
- _respawnCountdown.TryAdd(death, respawnTime);
- LastRound.RespawnCountdowns.TryAdd(death, respawnTime);
- WriteLine($"[ {death} ] 进入复活倒计时:{respawnTime:0.##} {GameplayEquilibriumConstant.InGameTime}!");
+ _continuousKilling.Remove(death);
+ _eliminated.Add(death);
+ if (MaxRespawnTimes == 0)
+ {
+ // do nothing
+ }
+ else if (_respawnTimes.TryGetValue(death, out int times) && MaxRespawnTimes != -1 && times == MaxRespawnTimes)
+ {
+ WriteLine($"[ {death} ] 已达到复活次数上限,将不能再复活!!");
+ }
+ else
+ {
+ // 进入复活倒计时
+ double respawnTime = GetRespawnTime(death, times);
+ _respawnCountdown.TryAdd(death, respawnTime);
+ LastRound.RespawnCountdowns.TryAdd(death, respawnTime);
+ WriteLine($"[ {death} ] 进入复活倒计时:{respawnTime:0.##} {GameplayEquilibriumConstant.InGameTime}!");
+ }
}
// 移除死者的施法
@@ -2793,7 +2962,7 @@ namespace Milimoe.FunGame.Core.Model
{
pNormalAttack = 0;
}
-
+
if (!dp.CheckActionTypeQuota(CharacterActionType.UseItem) || dp.CurrentDecisionPoints < dp.GameplayEquilibriumConstant.DecisionPointsCostItem)
{
pUseItem = 0;
@@ -2805,7 +2974,7 @@ namespace Milimoe.FunGame.Core.Model
{
pCastSkill = 0;
}
-
+
if (pUseItem == 0 && pCastSkill == 0 && pNormalAttack == 0)
{
return CharacterActionType.EndTurn;
@@ -2890,7 +3059,7 @@ namespace Milimoe.FunGame.Core.Model
effect.AlterSelectListBeforeSelection(caster, skill, enemys, teammates);
}
List 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);
}
@@ -2909,7 +3078,7 @@ namespace Milimoe.FunGame.Core.Model
public List SelectNonDirectionalSkillTargetGrid(Character caster, Skill skill, List enemys, List teammates, List castRange)
{
List 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);
}
@@ -2933,7 +3102,7 @@ namespace Milimoe.FunGame.Core.Model
effect.AlterSelectListBeforeSelection(character, attack, enemys, teammates);
}
List 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);
}
@@ -3291,7 +3460,7 @@ namespace Milimoe.FunGame.Core.Model
public List GetEnemies(Character character)
{
List 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)];
}
///
@@ -3301,7 +3470,7 @@ namespace Milimoe.FunGame.Core.Model
///
public virtual List 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)))];
}
///
@@ -3313,7 +3482,7 @@ namespace Milimoe.FunGame.Core.Model
public bool IsTeammate(Character character, Character target)
{
List 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));
}
///
@@ -3524,7 +3693,7 @@ namespace Milimoe.FunGame.Core.Model
protected void WillPreCastSuperSkill()
{
// 选取所有 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))
{
@@ -3595,13 +3764,14 @@ namespace Milimoe.FunGame.Core.Model
///
public void InterruptCasting(Character interrupter)
{
+ WriteLine($"[ {interrupter} ] 人间蒸发了。");
foreach (Character caster in _castingSkills.Keys)
{
SkillTarget skillTarget = _castingSkills[caster];
if (skillTarget.Targets.Contains(interrupter))
{
Skill skill = skillTarget.Skill;
- WriteLine($"[ {interrupter} ] 人间蒸发了。[ {caster} ] 丢失了施法目标!!");
+ WriteLine($"[ {caster} ] 丢失了施法目标!!");
List effects = [.. caster.Effects.Union(interrupter.Effects).Distinct().Where(e => e.IsInEffect)];
foreach (Effect effect in effects)
{
@@ -3618,23 +3788,26 @@ namespace Milimoe.FunGame.Core.Model
///
public void SetCharacterRespawn(Character character)
{
- double hardnessTime = 5;
- character.Respawn(_original[character.Guid]);
- _eliminated.Remove(character);
- WriteLine($"[ {character} ] 已复活!获得 {hardnessTime} {GameplayEquilibriumConstant.InGameTime}的硬直时间。");
- AddCharacter(character, hardnessTime, false);
- LastRound.Respawns.Add(character);
- _respawnCountdown.Remove(character);
- if (!_respawnTimes.TryAdd(character, 1))
+ if (_original.TryGetValue(character.Guid, out Character? original) && original != null)
{
- _respawnTimes[character] += 1;
+ double hardnessTime = 5;
+ character.Respawn(_original[character.Guid]);
+ _eliminated.Remove(character);
+ WriteLine($"[ {character} ] 已复活!获得 {hardnessTime} {GameplayEquilibriumConstant.InGameTime}的硬直时间。");
+ AddCharacter(character, hardnessTime, false);
+ LastRound.Respawns.Add(character);
+ _respawnCountdown.Remove(character);
+ if (!_respawnTimes.TryAdd(character, 1))
+ {
+ _respawnTimes[character] += 1;
+ }
+ if (!_decisionPoints.TryGetValue(character, out DecisionPoints? dp) || dp is null)
+ {
+ dp = new();
+ _decisionPoints[character] = dp;
+ }
+ OnQueueUpdatedEvent(_queue, character, dp, hardnessTime, QueueUpdatedReason.Respawn, "设置角色复活后的硬直时间。");
}
- if (!_decisionPoints.TryGetValue(character, out DecisionPoints? dp) || dp is null)
- {
- dp = new();
- _decisionPoints[character] = dp;
- }
- OnQueueUpdatedEvent(_queue, character, dp, hardnessTime, QueueUpdatedReason.Respawn, "设置角色复活后的硬直时间。");
}
///
@@ -3667,8 +3840,11 @@ namespace Milimoe.FunGame.Core.Model
if (character.CharacterState == CharacterState.Actionable)
{
dp.CurrentDecisionPoints -= GameplayEquilibriumConstant.DecisionPointsCostSuperSkillOutOfTurn;
- _stats[character].UseDecisionPoints += GameplayEquilibriumConstant.DecisionPointsCostSuperSkillOutOfTurn;
- _stats[character].TurnDecisions++;
+ if (character.Master is Character statsCharacter)
+ {
+ _stats[statsCharacter].UseDecisionPoints += GameplayEquilibriumConstant.DecisionPointsCostSuperSkillOutOfTurn;
+ _stats[statsCharacter].TurnDecisions++;
+ }
_castingSuperSkills[character] = skill;
character.CharacterState = CharacterState.PreCastSuperSkill;
_queue.Remove(character);
@@ -3719,7 +3895,17 @@ namespace Milimoe.FunGame.Core.Model
foreach (Character target in targets)
{
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;
}
- double hardnessTime = _hardnessTimes[character];
+ if (!_hardnessTimes.TryGetValue(character, out double hardnessTime))
+ {
+ hardnessTime = 0;
+ }
if (isPercentage)
{
addValue = hardnessTime * addValue;
@@ -3802,6 +3991,14 @@ namespace Milimoe.FunGame.Core.Model
///
public bool IsCharacterInAIControlling(Character character)
{
+ if (character.Master != null)
+ {
+ if (_charactersInAIBySystem.Contains(character))
+ {
+ return true;
+ }
+ character = character.Master;
+ }
return CharactersInAI.Contains(character);
}
@@ -3916,8 +4113,27 @@ namespace Milimoe.FunGame.Core.Model
PrimaryAttribute.INT => character.INTExemption,
_ => 0
};
- double dice = Random.Shared.NextDouble();
- if (dice < exemption)
+ 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();
+ if (dice < (exemption + throwingBonus))
+ {
+ exempted = true;
+ }
+ }
+ if (exempted)
{
if (isEvade)
{
@@ -3949,9 +4165,8 @@ namespace Milimoe.FunGame.Core.Model
WriteLine($"[ {character} ] 的{CharacterSet.GetPrimaryAttributeName(effect.ExemptionType)}豁免检定通过!{description}");
}
OnCharacterExemptionEvent(character, source, effect.Skill, effect.Skill.Item, isEvade);
- return true;
}
- return false;
+ return exempted;
}
#endregion
@@ -3964,6 +4179,10 @@ namespace Milimoe.FunGame.Core.Model
public void CalculateCharacterDamageStatistics(Character character, Character characterTaken, double damage, DamageType damageType, double takenDamage = -1)
{
if (takenDamage == -1) takenDamage = damage;
+ if (character.Master != null)
+ {
+ character = character.Master;
+ }
if (_stats.TryGetValue(character, out CharacterStatistics? stats) && stats != null)
{
if (damageType == DamageType.True)
@@ -4294,7 +4513,7 @@ namespace Milimoe.FunGame.Core.Model
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);
///
/// 角色死亡事件,此事件位于 之后
///
@@ -4304,10 +4523,11 @@ namespace Milimoe.FunGame.Core.Model
///
///
///
+ ///
///
- 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);
@@ -4596,7 +4816,7 @@ namespace Milimoe.FunGame.Core.Model
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);
///
/// 角色询问反应事件
///
diff --git a/Model/MixGamingQueue.cs b/Model/MixGamingQueue.cs
index 0add851..4e107bc 100644
--- a/Model/MixGamingQueue.cs
+++ b/Model/MixGamingQueue.cs
@@ -13,8 +13,9 @@ namespace Milimoe.FunGame.Core.Model
///
///
///
+ ///
///
- protected override void AfterDeathCalculation(Character death, Character killer)
+ protected override void AfterDeathCalculation(Character death, Character killer, Character[] assists)
{
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}");
}
- if (!_queue.Where(c => c != killer).Any())
+ if (!_queue.Any(c => c != killer && c.Master != killer && killer.Master != c))
{
// 没有其他的角色了,游戏结束
EndGameInfo(killer);
diff --git a/Model/TeamGamingQueue.cs b/Model/TeamGamingQueue.cs
index 0225534..fbf34e0 100644
--- a/Model/TeamGamingQueue.cs
+++ b/Model/TeamGamingQueue.cs
@@ -83,7 +83,9 @@ namespace Milimoe.FunGame.Core.Model
{
if (_teams[team].IsOnThisTeam(character))
{
- return _teams[team].GetTeammates(character);
+ List list = _teams[team].GetTeammates(character);
+ list.AddRange(_queue.Where(c => c.Master != null && _teams[team].IsOnThisTeam(c.Master)));
+ return list;
}
}
return [];
@@ -155,8 +157,9 @@ namespace Milimoe.FunGame.Core.Model
///
///
///
+ ///
///
- protected override void AfterDeathCalculation(Character death, Character killer)
+ protected override void AfterDeathCalculation(Character death, Character killer, Character[] assists)
{
Team? killTeam = GetTeam(killer);
Team? deathTeam = GetTeam(death);