From 2c0153ef5fd726292cc4ab4a8c6b4bcedfd5a41d Mon Sep 17 00:00:00 2001 From: milimoe <110188673+milimoe@users.noreply.github.com> Date: Sat, 12 Apr 2025 00:21:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=82=E6=AD=A5=E7=89=88=E8=A1=8C=E5=8A=A8?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F=E8=A1=A8=20(#126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Entity/Item/Item.cs | 4 +- Entity/Skill/Effect.cs | 12 +- Entity/Skill/NormalAttack.cs | 2 +- Interface/Base/IGamingQueue.cs | 20 +- Model/ActionQueue.cs | 554 ++++++++++++++++++++++++--------- 5 files changed, 431 insertions(+), 161 deletions(-) diff --git a/Entity/Item/Item.cs b/Entity/Item/Item.cs index 2e07bda..7e14082 100644 --- a/Entity/Item/Item.cs +++ b/Entity/Item/Item.cs @@ -307,7 +307,7 @@ namespace Milimoe.FunGame.Core.Entity /// 局内使用物品触发 /// /// - public bool UseItem(IGamingQueue queue, Character character, List enemys, List teammates) + public async Task UseItem(IGamingQueue queue, Character character, List enemys, List teammates) { bool cancel = false; bool used = false; @@ -318,7 +318,7 @@ namespace Milimoe.FunGame.Core.Entity } if (result && Skills.Active != null) { - used = queue.UseItem(this, character, enemys, teammates); + used = await queue.UseItemAsync(this, character, enemys, teammates); } if (used) { diff --git a/Entity/Skill/Effect.cs b/Entity/Skill/Effect.cs index 4bf9ee4..3ea8b0f 100644 --- a/Entity/Skill/Effect.cs +++ b/Entity/Skill/Effect.cs @@ -420,7 +420,7 @@ namespace Milimoe.FunGame.Core.Entity } /// - /// 对敌人造成技能伤害 [ 强烈建议使用此方法造成伤害而不是自行调用 ] + /// 对敌人造成技能伤害 [ 强烈建议使用此方法造成伤害而不是自行调用 ] /// /// /// @@ -432,12 +432,12 @@ namespace Milimoe.FunGame.Core.Entity { if (GamingQueue is null) return DamageResult.Evaded; DamageResult result = !isMagic ? GamingQueue.CalculatePhysicalDamage(actor, enemy, false, expectedDamage, out double damage) : GamingQueue.CalculateMagicalDamage(actor, enemy, false, MagicType, expectedDamage, out damage); - GamingQueue.DamageToEnemy(actor, enemy, damage, false, isMagic, magicType, result); + GamingQueue.DamageToEnemyAsync(actor, enemy, damage, false, isMagic, magicType, result); return result; } /// - /// 治疗一个目标 [ 强烈建议使用此方法而不是自行调用 ] + /// 治疗一个目标 [ 强烈建议使用此方法而不是自行调用 ] /// /// /// @@ -445,17 +445,17 @@ namespace Milimoe.FunGame.Core.Entity /// public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false) { - GamingQueue?.HealToTarget(actor, target, heal, canRespawn); + GamingQueue?.HealToTargetAsync(actor, target, heal, canRespawn); } /// - /// 打断施法 [ 尽可能的调用此方法而不是直接调用 ,以防止中断性变更 ] + /// 打断施法 [ 尽可能的调用此方法而不是直接调用 ,以防止中断性变更 ] /// /// /// public void InterruptCasting(Character caster, Character interrupter) { - GamingQueue?.InterruptCasting(caster, interrupter); + GamingQueue?.InterruptCastingAsync(caster, interrupter); } /// diff --git a/Entity/Skill/NormalAttack.cs b/Entity/Skill/NormalAttack.cs index 8089464..46b1657 100644 --- a/Entity/Skill/NormalAttack.cs +++ b/Entity/Skill/NormalAttack.cs @@ -72,7 +72,7 @@ namespace Milimoe.FunGame.Core.Entity queue.WriteLine("[ " + Character + $" ] 对 [ {enemy} ] 发起了普通攻击!"); double expected = Damage; DamageResult result = IsMagic ? queue.CalculateMagicalDamage(attacker, enemy, true, MagicType, expected, out double damage) : queue.CalculatePhysicalDamage(attacker, enemy, true, expected, out damage); - queue.DamageToEnemy(attacker, enemy, damage, true, IsMagic, MagicType, result); + queue.DamageToEnemyAsync(attacker, enemy, damage, true, IsMagic, MagicType, result); } } } diff --git a/Interface/Base/IGamingQueue.cs b/Interface/Base/IGamingQueue.cs index 160a236..2486ddc 100644 --- a/Interface/Base/IGamingQueue.cs +++ b/Interface/Base/IGamingQueue.cs @@ -70,7 +70,7 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// /// - public bool ProcessTurn(Character character); + public Task ProcessTurnAsync(Character character); /// /// 造成伤害 @@ -82,8 +82,8 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// /// - public void DamageToEnemy(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal); - + public Task DamageToEnemyAsync(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal); + /// /// 治疗一个目标 /// @@ -91,8 +91,8 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// /// - public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false); - + public Task HealToTargetAsync(Character actor, Character target, double heal, bool canRespawn = false); + /// /// 计算物理伤害 /// @@ -121,14 +121,14 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// /// - public void DeathCalculation(Character killer, Character death); + public Task DeathCalculationAsync(Character killer, Character death); /// /// 打断施法 /// /// /// - public void InterruptCasting(Character caster, Character interrupter); + public Task InterruptCastingAsync(Character caster, Character interrupter); /// /// 使用物品 @@ -138,7 +138,7 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// /// - public bool UseItem(Item item, Character caster, List enemys, List teammates); + public Task UseItemAsync(Item item, Character caster, List enemys, List teammates); /// /// 选取技能目标 @@ -148,7 +148,7 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// /// - public List SelectTargets(Character caster, Skill skill, List enemys, List teammates); + public Task> SelectTargetsAsync(Character caster, Skill skill, List enemys, List teammates); /// /// 选取普通攻击目标 @@ -158,6 +158,6 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// /// - public List SelectTargets(Character character, NormalAttack attack, List enemys, List teammates); + public Task> SelectTargetsAsync(Character character, NormalAttack attack, List enemys, List teammates); } } diff --git a/Model/ActionQueue.cs b/Model/ActionQueue.cs index e01da10..b878476 100644 --- a/Model/ActionQueue.cs +++ b/Model/ActionQueue.cs @@ -542,9 +542,9 @@ namespace Milimoe.FunGame.Core.Model /// 回合开始前触发 /// /// - public virtual bool BeforeTurn(Character character) + public virtual async Task BeforeTurnAsync(Character character) { - return true; + return await Task.FromResult(true); } /// @@ -552,12 +552,12 @@ namespace Milimoe.FunGame.Core.Model /// /// /// 是否结束游戏 - public bool ProcessTurn(Character character) + public async Task ProcessTurnAsync(Character character) { LastRound.Actor = character; _roundDeaths.Clear(); - if (!BeforeTurn(character)) + if (!await BeforeTurnAsync(character)) { return _isGameEnd; } @@ -587,7 +587,7 @@ namespace Milimoe.FunGame.Core.Model i.Skills.Active.SkillType == SkillType.Item && i.Skills.Active.Enable && !i.Skills.Active.IsInEffect && i.Skills.Active.CurrentCD == 0 && i.Skills.Active.RealMPCost <= character.MP && i.Skills.Active.RealEPCost <= character.EP)]; // 回合开始事件,允许事件返回 false 接管回合操作 - if (!OnTurnStart(character, enemys, teammates, skills, items)) + if (!await OnTurnStartAsync(character, enemys, teammates, skills, items)) { return _isGameEnd; } @@ -634,8 +634,8 @@ namespace Milimoe.FunGame.Core.Model if (character.CharacterState != CharacterState.NotActionable && character.CharacterState != CharacterState.Casting && character.CharacterState != CharacterState.PreCastSuperSkill) { // 模组可以通过以下事件来决定角色的行动 - type = OnDecideAction(character, pUseItem, pCastSkill, pNormalAttack); - if (type != CharacterActionType.None) + type = await OnDecideActionAsync(character, enemys, teammates, skills, items); + if (type == CharacterActionType.None) { // 若事件未完成决策,则将通过概率对角色进行自动化决策 if (character.CharacterState == CharacterState.Actionable) @@ -742,7 +742,7 @@ namespace Milimoe.FunGame.Core.Model if (type == CharacterActionType.NormalAttack) { // 使用普通攻击逻辑 - List targets = SelectTargets(character, character.NormalAttack, enemys, teammates); + List targets = await SelectTargetsAsync(character, character.NormalAttack, enemys, teammates); if (targets.Count == 0 && _charactersInAI.Contains(character) && enemys.Count > 0) { // 如果没有选取目标,且角色在 AI 控制下,则随机选取一个目标 @@ -752,6 +752,9 @@ namespace Milimoe.FunGame.Core.Model { LastRound.Targets = [.. targets]; decided = true; + + await OnCharacterActingAsync(character, CharacterActionType.NormalAttack, targets); + character.NormalAttack.Attack(this, character, targets); baseTime = character.NormalAttack.HardnessTime; effects = [.. character.Effects.Where(e => e.Level > 0)]; @@ -764,8 +767,8 @@ namespace Milimoe.FunGame.Core.Model else if (type == CharacterActionType.PreCastSkill) { // 预使用技能,即开始吟唱逻辑 - Skill? skill = OnSelectSkill(character, skills); - if (_charactersInAI.Contains(character)) + Skill? skill = await OnSelectSkillAsync(character, skills); + if (skill is null && _charactersInAI.Contains(character) && skills.Count > 0) { skill = skills[Random.Shared.Next(skills.Count)]; } @@ -774,7 +777,7 @@ namespace Milimoe.FunGame.Core.Model // 吟唱前需要先选取目标 if (skill.SkillType == SkillType.Magic) { - List targets = SelectTargets(character, skill, enemys, teammates); + List targets = await SelectTargetsAsync(character, skill, enemys, teammates); if (targets.Count == 0 && _charactersInAI.Contains(character) && enemys.Count > 0) { // 如果没有选取目标,且角色在 AI 控制下,则随机选取一个目标 @@ -784,8 +787,12 @@ namespace Milimoe.FunGame.Core.Model { LastRound.Targets = [.. targets]; decided = true; + character.CharacterState = CharacterState.Casting; - _castingSkills.Add(character, new(skill, targets)); + SkillTarget skillTarget = new(skill, targets); + await OnCharacterActingAsync(character, CharacterActionType.PreCastSkill, skillTarget); + + _castingSkills.Add(character, skillTarget); baseTime = skill.CastTime; skill.OnSkillCasting(this, character, targets); } @@ -795,7 +802,7 @@ namespace Milimoe.FunGame.Core.Model // 只有魔法需要吟唱,战技和爆发技直接释放 if (CheckCanCast(character, skill, out double cost)) { - List targets = SelectTargets(character, skill, enemys, teammates); + List targets = await SelectTargetsAsync(character, skill, enemys, teammates); if (targets.Count == 0 && _charactersInAI.Contains(character) && enemys.Count > 0) { // 如果没有选取目标,且角色在 AI 控制下,则随机选取一个目标 @@ -805,6 +812,10 @@ namespace Milimoe.FunGame.Core.Model { LastRound.Targets = [.. targets]; decided = true; + + SkillTarget skillTarget = new(skill, targets); + await OnCharacterActingAsync(character, CharacterActionType.PreCastSkill, skillTarget); + skill.OnSkillCasting(this, character, targets); skill.BeforeSkillCasted(); @@ -813,8 +824,10 @@ namespace Milimoe.FunGame.Core.Model skill.CurrentCD = skill.RealCD; skill.Enable = false; LastRound.SkillCost = $"{-cost:0.##} EP"; - WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量,释放了{(skill.IsSuperSkill ? "爆发技" : "战技")} [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); + + await OnCharacterActingAsync(character, CharacterActionType.CastSkill, skillTarget, cost); + skill.OnSkillCasted(this, character, targets); effects = [.. character.Effects.Where(e => e.Level > 0)]; foreach (Effect effect in effects) @@ -849,8 +862,10 @@ namespace Milimoe.FunGame.Core.Model skill.CurrentCD = skill.RealCD; skill.Enable = false; LastRound.SkillCost = $"{-cost:0.##} MP"; - WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点魔法值,释放了魔法 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); + + await OnCharacterActingAsync(character, CharacterActionType.CastSkill, skillTarget, cost); + skill.OnSkillCasted(this, character, targets); } else @@ -879,7 +894,7 @@ namespace Milimoe.FunGame.Core.Model if (CheckCanCast(character, skill, out double cost)) { // 预释放的爆发技不可取消 - List targets = SelectTargets(character, skill, enemys, teammates); + List targets = await SelectTargetsAsync(character, skill, enemys, teammates); LastRound.Targets = [.. targets]; skill.BeforeSkillCasted(); @@ -889,8 +904,11 @@ namespace Milimoe.FunGame.Core.Model skill.CurrentCD = skill.RealCD; skill.Enable = false; LastRound.SkillCost = $"{-cost:0.##} EP"; - WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量值,释放了爆发技 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); + + SkillTarget skillTarget = new(skill, targets); + await OnCharacterActingAsync(character, CharacterActionType.CastSkill, skillTarget, cost); + skill.OnSkillCasted(this, character, targets); } else @@ -909,11 +927,16 @@ namespace Milimoe.FunGame.Core.Model else if (type == CharacterActionType.UseItem) { // 使用物品逻辑 - Item? item = OnSelectItem(character, items); + Item? item = await OnSelectItemAsync(character, items); + if (item is null && _charactersInAI.Contains(character) && items.Count > 0) + { + // AI 控制下随机选取一个物品 + item = items[Random.Shared.Next(items.Count)]; + } if (item != null && item.Skills.Active != null) { Skill skill = item.Skills.Active; - if (UseItem(item, character, enemys, teammates)) + if (await UseItemAsync(item, character, enemys, teammates)) { decided = true; LastRound.Item = item; @@ -926,17 +949,24 @@ namespace Milimoe.FunGame.Core.Model } } } + else if (type == CharacterActionType.EndTurn) + { + decided = true; + WriteLine("[ " + character + $" ] 结束了回合!"); + await OnCharacterActingAsync(character, CharacterActionType.EndTurn); + } } if (!decided || type == CharacterActionType.None) { WriteLine("[ " + character + $" ] 放弃了行动!"); + await OnCharacterActingAsync(character, CharacterActionType.None); } LastRound.ActionType = type; // 统一在回合结束时处理角色的死亡 - ProcessCharacterDeath(character); + await ProcessCharacterDeathAsync(character); if (_isGameEnd) { @@ -957,6 +987,7 @@ namespace Milimoe.FunGame.Core.Model LastRound.CastTime = newHardnessTime; } AddCharacter(character, newHardnessTime, isCheckProtected); + await OnQueueUpdatedAsync(_queue, character, "设置角色行动后的硬直时间。"); LastRound.HardnessTime = newHardnessTime; effects = [.. character.Effects.Where(e => e.Level > 0)]; @@ -985,12 +1016,12 @@ namespace Milimoe.FunGame.Core.Model } // 有人想要插队吗? - WillPreCastSuperSkill(character); + await WillPreCastSuperSkill(character); // 回合结束事件 - OnTurnEnd(character); + await OnTurnEndAsync(character); - AfterTurn(character); + await AfterTurnAsync(character); WriteLine(""); return _isGameEnd; @@ -1000,16 +1031,16 @@ namespace Milimoe.FunGame.Core.Model /// 回合结束后触发 /// /// - public virtual void AfterTurn(Character character) + public virtual async Task AfterTurnAsync(Character character) { - + await Task.CompletedTask; } /// /// 时间进行流逝,减少硬直时间,减少技能冷却时间,角色也会因此回复状态 /// /// 流逝的时间 - public double TimeLapse() + public async Task TimeLapse() { if (_queue.Count == 0) return 0; @@ -1034,16 +1065,7 @@ namespace Milimoe.FunGame.Core.Model _respawnCountdown[character] = Calculation.Round2Digits(_respawnCountdown[character] - timeToReduce); if (_respawnCountdown[character] <= 0) { - double hardnessTime = 5; - character.Respawn(_original[character.Guid]); - WriteLine($"[ {character} ] 已复活!获得 {hardnessTime} {GameplayEquilibriumConstant.InGameTime}的硬直时间。"); - AddCharacter(character, hardnessTime, false); - LastRound.Respawns.Add(character); - _respawnCountdown.Remove(character); - if (!_respawnTimes.TryAdd(character, 1)) - { - _respawnTimes[character] += 1; - } + await SetCharacterRespawn(character); } } @@ -1143,7 +1165,7 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public void DamageToEnemy(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal) + public async Task DamageToEnemyAsync(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage = false, MagicType magicType = MagicType.None, DamageResult damageResult = DamageResult.Normal) { // 如果敌人在结算伤害之前就已经死亡,将不会继续下去 if (enemy.HP <= 0) @@ -1205,6 +1227,8 @@ namespace Milimoe.FunGame.Core.Model enemy.EP += ep; } + await OnDamageToEnemyAsync(actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, damageResult); + effects = [.. actor.Effects.Union(enemy.Effects).Where(e => e.Level > 0)]; foreach (Effect effect in effects) { @@ -1215,7 +1239,7 @@ namespace Milimoe.FunGame.Core.Model { LastRound.HasKill = true; _roundDeaths.Add(enemy); - DeathCalculation(actor, enemy); + await DeathCalculationAsync(actor, enemy); } } @@ -1226,7 +1250,7 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false) + public async Task HealToTargetAsync(Character actor, Character target, double heal, bool canRespawn = false) { if (target.HP == target.MaxHP) { @@ -1245,7 +1269,8 @@ namespace Milimoe.FunGame.Core.Model } } - if (isDead && canRespawn) + bool isRespawn = isDead && canRespawn; + if (isRespawn) { if (target != actor) { @@ -1255,11 +1280,14 @@ namespace Milimoe.FunGame.Core.Model { WriteLine($"[ {target} ] 复苏了,并回复了 {heal:0.##} 点生命值!!"); } + await SetCharacterRespawn(target); } else { WriteLine($"[ {target} ] 回复了 {heal:0.##} 点生命值!"); } + + await OnHealToTargetAsync(actor, target, heal, isRespawn); } /// @@ -1497,22 +1525,27 @@ namespace Milimoe.FunGame.Core.Model /// 处理角色死亡 /// /// - public void ProcessCharacterDeath(Character character) + public async Task ProcessCharacterDeathAsync(Character character) { - foreach (Character enemy in _roundDeaths) + foreach (Character death in _roundDeaths) { + if(!await OnCharacterDeathAsync(character, death)) + { + continue; + } + // 给所有角色的特效广播角色死亡结算 List effects = [.. _queue.SelectMany(c => c.Effects.Where(e => e.Level > 0))]; foreach (Effect effect in effects) { - effect.AfterDeathCalculation(enemy, character, _continuousKilling, _earnedMoney); + effect.AfterDeathCalculation(death, character, _continuousKilling, _earnedMoney); } // 将死者移出队列 - _queue.Remove(enemy); + _queue.Remove(death); if (_isTeamMode) { Team? killTeam = GetTeam(character); - Team? deathTeam = GetTeam(enemy); + Team? deathTeam = GetTeam(death); if (MaxRespawnTimes != 0) { @@ -1556,7 +1589,7 @@ namespace Milimoe.FunGame.Core.Model if (!_teams.Keys.Where(str => str != killTeam.Name).Any()) { // 没有其他的团队了,游戏结束 - EndGameInfo(killTeam); + await EndGameInfo(killTeam); return; } if (MaxScoreToWin > 0 && killTeam.Score >= MaxScoreToWin) @@ -1565,7 +1598,7 @@ namespace Milimoe.FunGame.Core.Model combinedTeams.Remove(killTeam); _eliminatedTeams.Clear(); _eliminatedTeams.AddRange(combinedTeams.OrderByDescending(t => t.Score)); - EndGameInfo(killTeam); + await EndGameInfo(killTeam); return; } } @@ -1575,7 +1608,7 @@ namespace Milimoe.FunGame.Core.Model if (!_queue.Where(c => c != character).Any()) { // 没有其他的角色了,游戏结束 - EndGameInfo(character); + await EndGameInfo(character); } } } @@ -1586,8 +1619,13 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public void DeathCalculation(Character killer, Character death) + public async Task DeathCalculationAsync(Character killer, Character death) { + if (!await OnDeathCalculationAsync(killer, death)) + { + return; + } + if (!_continuousKilling.TryAdd(killer, 1)) _continuousKilling[killer] += 1; if (!_maxContinuousKilling.TryAdd(killer, 1) && _continuousKilling[killer] > _maxContinuousKilling[killer]) { @@ -1741,11 +1779,17 @@ namespace Milimoe.FunGame.Core.Model /// /// 游戏结束信息 /// - public void EndGameInfo(Character winner) + public async Task EndGameInfo(Character winner) { WriteLine("[ " + winner + " ] 是胜利者。"); _queue.Remove(winner); _eliminated.Add(winner); + + if (!await OnGameEndAsync(winner)) + { + return; + } + int top = 1; WriteLine(""); WriteLine("=== 排名 ==="); @@ -1792,10 +1836,15 @@ namespace Milimoe.FunGame.Core.Model /// /// 游戏结束信息 [ 团队版 ] /// - public void EndGameInfo(Team winner) + public async Task EndGameInfo(Team winner) { WriteLine("[ " + winner + " ] 是胜利者。"); + if (!await OnGameEndTeamAsync(winner)) + { + return; + } + int top = 1; WriteLine(""); WriteLine("=== 排名 ==="); @@ -1953,7 +2002,7 @@ namespace Milimoe.FunGame.Core.Model /// /// 当前正在行动的角色 /// - public void WillPreCastSuperSkill(Character character) + public async Task WillPreCastSuperSkill(Character character) { // 选取除了回合内角色之外的 AI 控制角色 foreach (Character other in _queue.Where(c => c != character && c.CharacterState == CharacterState.Actionable && _charactersInAI.Contains(c)).ToList()) @@ -1965,7 +2014,7 @@ namespace Milimoe.FunGame.Core.Model if (skills.Count > 0) { Skill skill = skills[Random.Shared.Next(skills.Count)]; - SetCharacterPreCastSuperSkill(other, skill); + await SetCharacterPreCastSuperSkill(other, skill); } } } @@ -1976,7 +2025,7 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public void InterruptCasting(Character caster, Character interrupter) + public async Task InterruptCastingAsync(Character caster, Character interrupter) { Skill? skill = null; if (_castingSkills.TryGetValue(caster, out SkillTarget target)) @@ -1997,6 +2046,7 @@ namespace Milimoe.FunGame.Core.Model e.OnSkillCastInterrupted(caster, skill, interrupter); } } + await OnInterruptCastingAsync(caster, skill, interrupter); } /// @@ -2136,14 +2186,14 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public bool UseItem(Item item, Character character, List enemys, List teammates) + public async Task UseItemAsync(Item item, Character character, List enemys, List teammates) { - Skill? skill = item.Skills.Active; - if (skill != null) + if (CheckCanCast(character, item, out double costMP, out double costEP)) { - if (CheckCanCast(character, skill, out double cost)) + Skill? skill = item.Skills.Active; + if (skill != null) { - List targets = SelectTargets(character, skill, enemys, teammates); + List targets = await SelectTargetsAsync(character, skill, enemys, teammates); if (targets.Count == 0 && _charactersInAI.Contains(character) && enemys.Count > 0) { // 如果没有选取目标,且角色在 AI 控制下,则随机选取一个目标 @@ -2153,15 +2203,37 @@ namespace Milimoe.FunGame.Core.Model { LastRound.Targets = [.. targets]; + await OnCharacterActingAsync(character, CharacterActionType.UseItem, item, targets); + + string line = $"[ {character} ] 使用了物品 [ {item.Name} ]!\r\n[ {character} ] "; + skill.OnSkillCasting(this, character, targets); skill.BeforeSkillCasted(); - character.EP -= cost; skill.CurrentCD = skill.RealCD; skill.Enable = false; - LastRound.SkillCost = $"{-cost:0.##} EP"; - WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量,释放了{(skill.IsSuperSkill ? "爆发技" : "战技")} [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); + if (costMP > 0) + { + character.MP -= costMP; + LastRound.SkillCost = $"{-costMP:0.##} MP"; + line += $"消耗了 {costMP:0.##} 点魔法值,"; + } + + if (costEP > 0) + { + character.EP -= costEP; + if (LastRound.SkillCost != "") LastRound.SkillCost += " / "; + LastRound.SkillCost += $"{-costEP:0.##} EP"; + line += $"消耗了 {costEP:0.##} 点能量,"; + } + + line += $"释放了物品技能 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"; + WriteLine(line); + + SkillTarget skillTarget = new(skill, targets); + await OnCharacterActingAsync(character, CharacterActionType.CastSkill, skillTarget, costMP, costEP); + skill.OnSkillCasted(this, character, targets); return true; } @@ -2170,12 +2242,31 @@ namespace Milimoe.FunGame.Core.Model return false; } + /// + /// 设置角色复活 + /// + /// + public async Task SetCharacterRespawn(Character character) + { + double hardnessTime = 5; + character.Respawn(_original[character.Guid]); + WriteLine($"[ {character} ] 已复活!获得 {hardnessTime} {GameplayEquilibriumConstant.InGameTime}的硬直时间。"); + AddCharacter(character, hardnessTime, false); + await OnQueueUpdatedAsync(_queue, character, "设置角色复活后的硬直时间。"); + LastRound.Respawns.Add(character); + _respawnCountdown.Remove(character); + if (!_respawnTimes.TryAdd(character, 1)) + { + _respawnTimes[character] += 1; + } + } + /// /// 设置角色将预释放爆发技 /// /// /// - public void SetCharacterPreCastSuperSkill(Character character, Skill skill) + public async Task SetCharacterPreCastSuperSkill(Character character, Skill skill) { if (character.CharacterState == CharacterState.Actionable) { @@ -2184,6 +2275,7 @@ namespace Milimoe.FunGame.Core.Model _queue.Remove(character); _cutCount.Remove(character); AddCharacter(character, 0, false); + await OnQueueUpdatedAsync(_queue, character, "设置角色预释放爆发技的硬直时间。"); WriteLine("[ " + character + " ] 预释放了爆发技!!"); foreach (Character c in _hardnessTimes.Keys) { @@ -2224,10 +2316,10 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public virtual List SelectTargets(Character caster, Skill skill, List enemys, List teammates) + public virtual async Task> SelectTargetsAsync(Character caster, Skill skill, List enemys, List teammates) { - List targets = OnSelectSkillTargets(caster, skill, enemys, teammates); - if (targets.Count == 0) + List targets = await OnSelectSkillTargetsAsync(caster, skill, enemys, teammates); + if (targets.Count == 0 && _charactersInAI.Contains(caster)) { targets = skill.SelectTargets(caster, enemys, teammates); } @@ -2242,84 +2334,18 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public virtual List SelectTargets(Character character, NormalAttack attack, List enemys, List teammates) + public virtual async Task> SelectTargetsAsync(Character character, NormalAttack attack, List enemys, List teammates) { - List targets = OnSelectNormalAttackTargets(character, attack, enemys, teammates); + List targets = await OnSelectNormalAttackTargetsAsync(character, attack, enemys, teammates); return targets; } - public delegate CharacterActionType DecideActionEventHandler(Character character, double pUseItem, double pCastSkill, double pNormalAttack); - public event DecideActionEventHandler? DecideAction; - /// - /// 决定角色的行动事件 - /// - /// - /// - /// - /// - /// - public CharacterActionType OnDecideAction(Character character, double pUseItem, double pCastSkill, double pNormalAttack) - { - return DecideAction?.Invoke(character, pUseItem, pCastSkill, pNormalAttack) ?? CharacterActionType.None; - } + #region 事件 - public delegate Skill SelectSkillEventHandler(Character character, List skills); - public event SelectSkillEventHandler? SelectSkill; + public delegate Task TurnStartEventHandler(Character character, List enemys, List teammates, List skills, List items); /// - /// 角色需要选择一个技能 + /// 回合开始事件 /// - /// - /// - /// - public Skill? OnSelectSkill(Character character, List skills) - { - return SelectSkill?.Invoke(character, skills); - } - - public delegate Item SelectItemEventHandler(Character character, List items); - public event SelectItemEventHandler? SelectItem; - /// - /// 角色需要选择一个物品 - /// - /// - /// - /// - public Item? OnSelectItem(Character character, List items) - { - return SelectItem?.Invoke(character, items); - } - - public delegate List SelectSkillTargetsEventHandler(Character caster, Skill skill, List enemys, List teammates); - public event SelectSkillTargetsEventHandler? SelectSkillTargets; - /// - /// 选取技能目标事件 - /// - /// - /// - /// - /// - /// - public List OnSelectSkillTargets(Character caster, Skill skill, List enemys, List teammates) - { - return SelectSkillTargets?.Invoke(caster, skill, enemys, teammates) ?? []; - } - - public delegate List SelectNormalAttackTargetsEventHandler(Character character, NormalAttack attack, List enemys, List teammates); - public event SelectNormalAttackTargetsEventHandler? SelectNormalAttackTargets; - /// - /// 选取普通攻击目标事件 - /// - /// - /// - /// - /// - /// - public List OnSelectNormalAttackTargets(Character character, NormalAttack attack, List enemys, List teammates) - { - return SelectNormalAttackTargets?.Invoke(character, attack, enemys, teammates) ?? []; - } - - public delegate bool TurnStartEventHandler(Character character, List enemys, List teammates, List skills, List items); public event TurnStartEventHandler? TurnStart; /// /// 回合开始事件 @@ -2330,21 +2356,265 @@ namespace Milimoe.FunGame.Core.Model /// /// /// - public bool OnTurnStart(Character character, List enemys, List teammates, List skills, List items) + public async Task OnTurnStartAsync(Character character, List enemys, List teammates, List skills, List items) { - return TurnStart?.Invoke(character, enemys, teammates, skills, items) ?? true; + return await (TurnStart?.Invoke(character, enemys, teammates, skills, items) ?? Task.FromResult(true)); } - public delegate void TurnEndEventHandler(Character character); + public delegate Task TurnEndEventHandler(Character character); + /// + /// 回合结束事件 + /// public event TurnEndEventHandler? TurnEnd; /// /// 回合结束事件 /// /// /// - public void OnTurnEnd(Character character) + public async Task OnTurnEndAsync(Character character) { - TurnEnd?.Invoke(character); + await (TurnEnd?.Invoke(character) ?? Task.CompletedTask); } + + public delegate Task DecideActionEventHandler(Character character, List enemys, List teammates, List skills, List items); + /// + /// 决定角色的行动事件 + /// + public event DecideActionEventHandler? DecideAction; + /// + /// 决定角色的行动事件 + /// + /// + /// + /// + /// + /// + /// + public async Task OnDecideActionAsync(Character character, List enemys, List teammates, List skills, List items) + { + return await (DecideAction?.Invoke(character, enemys, teammates, skills, items) ?? Task.FromResult(CharacterActionType.None)); + } + + public delegate Task SelectSkillEventHandler(Character character, List skills); + /// + /// 角色需要选择一个技能 + /// + public event SelectSkillEventHandler? SelectSkill; + /// + /// 角色需要选择一个技能 + /// + /// + /// + /// + public async Task OnSelectSkillAsync(Character character, List skills) + { + return await (SelectSkill?.Invoke(character, skills) ?? Task.FromResult(null)); + } + + public delegate Task SelectItemEventHandler(Character character, List items); + /// + /// 角色需要选择一个物品 + /// + public event SelectItemEventHandler? SelectItem; + /// + /// 角色需要选择一个物品 + /// + /// + /// + /// + public async Task OnSelectItemAsync(Character character, List items) + { + return await (SelectItem?.Invoke(character, items) ?? Task.FromResult(null)); + } + + public delegate Task> SelectSkillTargetsEventHandler(Character caster, Skill skill, List enemys, List teammates); + /// + /// 选取技能目标事件 + /// + public event SelectSkillTargetsEventHandler? SelectSkillTargets; + /// + /// 选取技能目标事件 + /// + /// + /// + /// + /// + /// + public async Task> OnSelectSkillTargetsAsync(Character caster, Skill skill, List enemys, List teammates) + { + return await (SelectSkillTargets?.Invoke(caster, skill, enemys, teammates) ?? Task.FromResult(new List())); + } + + public delegate Task> SelectNormalAttackTargetsEventHandler(Character character, NormalAttack attack, List enemys, List teammates); + /// + /// 选取普通攻击目标事件 + /// + public event SelectNormalAttackTargetsEventHandler? SelectNormalAttackTargets; + /// + /// 选取普通攻击目标事件 + /// + /// + /// + /// + /// + /// + public async Task> OnSelectNormalAttackTargetsAsync(Character character, NormalAttack attack, List enemys, List teammates) + { + return await (SelectNormalAttackTargets?.Invoke(character, attack, enemys, teammates) ?? Task.FromResult(new List())); + } + + public delegate Task InterruptCastingEventHandler(Character cast, Skill? skill, Character interrupter); + /// + /// 打断施法事件 + /// + public event InterruptCastingEventHandler? InterruptCasting; + /// + /// 打断施法事件 + /// + /// + /// + /// + /// + public async Task OnInterruptCastingAsync(Character cast, Skill? skill, Character interrupter) + { + await (InterruptCasting?.Invoke(cast, skill, interrupter) ?? Task.CompletedTask); + } + + public delegate Task DeathCalculationEventHandler(Character killer, Character death); + /// + /// 死亡结算事件 + /// + public event DeathCalculationEventHandler? DeathCalculation; + /// + /// 死亡结算事件 + /// + /// + /// + /// + public async Task OnDeathCalculationAsync(Character killer, Character death) + { + return await (DeathCalculation?.Invoke(killer, death) ?? Task.FromResult(true)); + } + + public delegate Task CharacterDeathEventHandler(Character current, Character death); + /// + /// 角色死亡事件,此事件位于 之后 + /// + public event CharacterDeathEventHandler? CharacterDeath; + /// + /// 角色死亡事件,此事件位于 之后 + /// + /// + /// + /// + public async Task OnCharacterDeathAsync(Character current, Character death) + { + return await (CharacterDeath?.Invoke(current, death) ?? Task.FromResult(true)); + } + + public delegate Task HealToTargetEventHandler(Character actor, Character target, double heal, bool isRespawn); + /// + /// 治疗事件 + /// + public event HealToTargetEventHandler? HealToTarget; + /// + /// 治疗事件 + /// + /// + /// + /// + /// + /// + public async Task OnHealToTargetAsync(Character actor, Character target, double heal, bool isRespawn) + { + await (HealToTarget?.Invoke(actor, target, heal, isRespawn) ?? Task.CompletedTask); + } + + public delegate Task DamageToEnemyEventHandler(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, DamageResult damageResult); + /// + /// 造成伤害事件 + /// + public event DamageToEnemyEventHandler? DamageToEnemy; + /// + /// 造成伤害事件 + /// + /// + /// + /// + /// + /// + /// + /// + /// + public async Task OnDamageToEnemyAsync(Character actor, Character enemy, double damage, bool isNormalAttack, bool isMagicDamage, MagicType magicType, DamageResult damageResult) + { + await (DamageToEnemy?.Invoke(actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, damageResult) ?? Task.CompletedTask); + } + + public delegate Task CharacterActingEventHandler(Character actor, CharacterActionType type, params object[] args); + /// + /// 角色行动事件 + /// + public event CharacterActingEventHandler? CharacterActing; + /// + /// 角色行动事件 + /// + /// + /// + /// + /// + public async Task OnCharacterActingAsync(Character actor, CharacterActionType type, params object[] args) + { + await (CharacterActing?.Invoke(actor, type, args) ?? Task.CompletedTask); + } + + public delegate Task GameEndEventHandler(Character winner); + /// + /// 游戏结束事件 + /// + public event GameEndEventHandler? GameEnd; + /// + /// 游戏结束事件 + /// + /// + /// + public async Task OnGameEndAsync(Character winner) + { + return await (GameEnd?.Invoke(winner) ?? Task.FromResult(true)); + } + + public delegate Task GameEndTeamEventHandler(Team winner); + /// + /// 游戏结束事件(团队版) + /// + public event GameEndTeamEventHandler? GameEndTeam; + /// + /// 游戏结束事件(团队版) + /// + /// + /// + public async Task OnGameEndTeamAsync(Team winner) + { + return await (GameEndTeam?.Invoke(winner) ?? Task.FromResult(true)); + } + + public delegate Task QueueUpdatedEventHandler(List queue, Character character, string reason); + /// + /// 行动顺序表更新事件 + /// + public event QueueUpdatedEventHandler? QueueUpdated; + /// + /// 行动顺序表更新事件 + /// + /// + /// + /// + /// + public async Task OnQueueUpdatedAsync(List queue, Character character, string reason) + { + await (QueueUpdated?.Invoke(queue, character, reason) ?? Task.CompletedTask); + } + + #endregion } }