From 88557e093bd9fc0277e357d1b8461b168f8f5ca8 Mon Sep 17 00:00:00 2001 From: milimoe Date: Mon, 14 Apr 2025 01:29:06 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=8C=E5=8A=A8=E9=A1=BA=E5=BA=8F=E8=A1=A8?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9B=B4=E5=A4=9A=E4=BA=8B=E4=BB=B6=E9=92=A9?= =?UTF-8?q?=E5=AD=90=EF=BC=8C=E6=8A=80=E8=83=BD=E3=80=81=E7=89=B9=E6=95=88?= =?UTF-8?q?=E9=92=A9=E5=AD=90=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Entity/Skill/Effect.cs | 6 +- Entity/Skill/NormalAttack.cs | 28 +++ Entity/Skill/Skill.cs | 57 +++-- Interface/Entity/Typical/ISkill.cs | 12 +- Model/ActionQueue.cs | 321 ++++++++++++++++++++--------- 5 files changed, 305 insertions(+), 119 deletions(-) diff --git a/Entity/Skill/Effect.cs b/Entity/Skill/Effect.cs index 3ea8b0f..1d9640a 100644 --- a/Entity/Skill/Effect.cs +++ b/Entity/Skill/Effect.cs @@ -288,7 +288,11 @@ namespace Milimoe.FunGame.Core.Entity /// 在特效持有者的回合开始前 /// /// - public virtual void OnTurnStart(Character character) + /// + /// + /// + /// + public virtual void OnTurnStart(Character character, List enemys, List teammates, List skills, List items) { } diff --git a/Entity/Skill/NormalAttack.cs b/Entity/Skill/NormalAttack.cs index 3ff8446..275a1d7 100644 --- a/Entity/Skill/NormalAttack.cs +++ b/Entity/Skill/NormalAttack.cs @@ -82,6 +82,34 @@ namespace Milimoe.FunGame.Core.Entity /// public double CanSelectTargetRange { get; set; } = 0; + /// + /// 获取可选择的目标列表 + /// + /// + /// + /// + /// + public List GetSelectableTargets(Character caster, List enemys, List teammates) + { + List selectable = []; + + if (CanSelectSelf) + { + selectable.Add(caster); + } + + if (CanSelectEnemy) + { + selectable.AddRange(enemys); + } + if (CanSelectTeammate) + { + selectable.AddRange(teammates); + } + + return selectable; + } + /// /// 对目标(或多个目标)发起普通攻击 /// diff --git a/Entity/Skill/Skill.cs b/Entity/Skill/Skill.cs index 4e51b5a..16da57c 100644 --- a/Entity/Skill/Skill.cs +++ b/Entity/Skill/Skill.cs @@ -275,6 +275,47 @@ namespace Milimoe.FunGame.Core.Entity OnLevelUp(); } + /// + /// 在技能持有者的回合开始前 + /// + /// + /// + /// + /// + /// + public virtual void OnTurnStart(Character character, List enemys, List teammates, List skills, List items) + { + + } + + /// + /// 获取可选择的目标列表 + /// + /// + /// + /// + /// + public virtual List GetSelectableTargets(Character caster, List enemys, List teammates) + { + List selectable = []; + + if (CanSelectSelf) + { + selectable.Add(caster); + } + + if (CanSelectEnemy) + { + selectable.AddRange(enemys); + } + if (CanSelectTeammate) + { + selectable.AddRange(teammates); + } + + return selectable; + } + /// /// 选取技能目标 /// @@ -284,21 +325,7 @@ namespace Milimoe.FunGame.Core.Entity /// public virtual List SelectTargets(Character caster, List enemys, List teammates) { - List tobeSelected = []; - - if (CanSelectSelf) - { - tobeSelected.Add(caster); - } - - if (CanSelectEnemy) - { - tobeSelected.AddRange(enemys); - } - if (CanSelectTeammate) - { - tobeSelected.AddRange(teammates); - } + List tobeSelected = GetSelectableTargets(caster, enemys, teammates); // 筛选出符合条件的角色 tobeSelected = [.. tobeSelected.Where(c => SelectTargetPredicates.All(f => f(c)))]; diff --git a/Interface/Entity/Typical/ISkill.cs b/Interface/Entity/Typical/ISkill.cs index 9fae0a4..c0dc96b 100644 --- a/Interface/Entity/Typical/ISkill.cs +++ b/Interface/Entity/Typical/ISkill.cs @@ -1,4 +1,6 @@ -namespace Milimoe.FunGame.Core.Interface.Entity +using Milimoe.FunGame.Core.Entity; + +namespace Milimoe.FunGame.Core.Interface.Entity { public interface ISkill { @@ -26,5 +28,13 @@ /// public double CanSelectTargetRange { get; } + /// + /// 获取可选择的目标列表 + /// + /// + /// + /// + /// + public List GetSelectableTargets(Character caster, List enemys, List teammates); } } diff --git a/Model/ActionQueue.cs b/Model/ActionQueue.cs index dfe2322..711e067 100644 --- a/Model/ActionQueue.cs +++ b/Model/ActionQueue.cs @@ -573,12 +573,6 @@ namespace Milimoe.FunGame.Core.Model return _isGameEnd; } - List effects = [.. character.Effects.Where(e => e.Level > 0)]; - foreach (Effect effect in effects) - { - effect.OnTurnStart(character); - } - // 基础硬直时间 double baseTime = 10; bool isCheckProtected = true; @@ -598,11 +592,23 @@ 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 (!await OnTurnStartAsync(character, enemys, teammates, skills, items)) { return _isGameEnd; } + foreach (Skill skillTurnStart in skills) + { + skillTurnStart.OnTurnStart(character, enemys, teammates, skills, items); + } + + List effects = [.. character.Effects.Where(e => e.Level > 0)]; + foreach (Effect effect in effects) + { + effect.OnTurnStart(character, enemys, teammates, skills, items); + } + // 此变量用于在取消选择时,能够重新行动 bool decided = false; // 最大取消次数 @@ -648,77 +654,84 @@ namespace Milimoe.FunGame.Core.Model { if (character.CharacterState != CharacterState.NotActionable && character.CharacterState != CharacterState.Casting && character.CharacterState != CharacterState.PreCastSuperSkill) { - // 模组可以通过以下事件来决定角色的行动 + // 根据角色状态,设置一些参数 + if (character.CharacterState == CharacterState.Actionable) + { + // 可以任意行动 + if (canUseItem && canCastSkill) + { + // 不做任何处理 + } + else if (canUseItem && !canCastSkill) + { + pCastSkill = 0; + } + else if (!canUseItem && canCastSkill) + { + pUseItem = 0; + } + else + { + pUseItem = 0; + pCastSkill = 0; + } + } + else if (character.CharacterState == CharacterState.ActionRestricted) + { + // 行动受限,只能使用特殊物品 + if (canUseItem) + { + pCastSkill = 0; + pNormalAttack = 0; + } + else + { + pUseItem = 0; + pCastSkill = 0; + pNormalAttack = 0; + } + } + else if (character.CharacterState == CharacterState.BattleRestricted) + { + // 战斗不能,只能使用物品 + enemys.Clear(); + teammates.Clear(); + skills.Clear(); + if (canUseItem) + { + pCastSkill = 0; + pNormalAttack = 0; + } + else + { + pUseItem = 0; + pCastSkill = 0; + pNormalAttack = 0; + } + } + else if (character.CharacterState == CharacterState.SkillRestricted) + { + // 技能受限,无法使用技能,可以普通攻击,可以使用物品 + skills.Clear(); + if (canUseItem) + { + pCastSkill = 0; + } + else + { + pUseItem = 0; + pCastSkill = 0; + } + } + + // 模组可以通过此事件来决定角色的行动 type = await OnDecideActionAsync(character, enemys, teammates, skills, items); + // 若事件未完成决策,则将通过概率对角色进行自动化决策 if (type == CharacterActionType.None) { - // 若事件未完成决策,则将通过概率对角色进行自动化决策 - if (character.CharacterState == CharacterState.Actionable) - { - // 可以任意行动 - if (canUseItem && canCastSkill) - { - // 不做任何处理 - } - else if (canUseItem && !canCastSkill) - { - pCastSkill = 0; - } - else if (!canUseItem && canCastSkill) - { - pUseItem = 0; - } - else - { - pUseItem = 0; - pCastSkill = 0; - } - } - else if (character.CharacterState == CharacterState.ActionRestricted) - { - // 行动受限,只能使用特殊物品 - if (canUseItem) - { - pCastSkill = 0; - pNormalAttack = 0; - } - else - { - pUseItem = 0; - pCastSkill = 0; - pNormalAttack = 0; - } - } - else if (character.CharacterState == CharacterState.BattleRestricted) - { - // 战斗不能,只能使用物品 - if (canUseItem) - { - pCastSkill = 0; - pNormalAttack = 0; - } - else - { - pUseItem = 0; - pCastSkill = 0; - pNormalAttack = 0; - } - } - else if (character.CharacterState == CharacterState.SkillRestricted) - { - // 技能受限,无法使用技能,可以使用物品 - if (canUseItem) - { - pCastSkill = 0; - } - else - { - pUseItem = 0; - pCastSkill = 0; - } - } type = GetActionType(pUseItem, pCastSkill, pNormalAttack); } + _stats[character].ActionTurn += 1; } else if (character.CharacterState == CharacterState.Casting) @@ -733,7 +746,7 @@ namespace Milimoe.FunGame.Core.Model } else { - WriteLine("[ " + character + $" ] 完全行动不能!"); + // 完全行动不能 type = CharacterActionType.None; } } @@ -768,7 +781,7 @@ namespace Milimoe.FunGame.Core.Model LastRound.Targets = [.. targets]; decided = true; - await OnCharacterActingAsync(character, CharacterActionType.NormalAttack, targets); + await OnCharacterNormalAttackAsync(character, targets); character.NormalAttack.Attack(this, character, targets); baseTime = character.NormalAttack.HardnessTime; @@ -805,7 +818,7 @@ namespace Milimoe.FunGame.Core.Model character.CharacterState = CharacterState.Casting; SkillTarget skillTarget = new(skill, targets); - await OnCharacterActingAsync(character, CharacterActionType.PreCastSkill, skillTarget); + await OnCharacterPreCastSkillAsync(character, skillTarget); _castingSkills.Add(character, skillTarget); baseTime = skill.CastTime; @@ -829,7 +842,7 @@ namespace Milimoe.FunGame.Core.Model decided = true; SkillTarget skillTarget = new(skill, targets); - await OnCharacterActingAsync(character, CharacterActionType.PreCastSkill, skillTarget); + await OnCharacterPreCastSkillAsync(character, skillTarget); skill.OnSkillCasting(this, character, targets); skill.BeforeSkillCasted(); @@ -841,7 +854,7 @@ namespace Milimoe.FunGame.Core.Model LastRound.SkillCost = $"{-cost:0.##} EP"; WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量,释放了{(skill.IsSuperSkill ? "爆发技" : "战技")} [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); - await OnCharacterActingAsync(character, CharacterActionType.CastSkill, skillTarget, cost); + await OnCharacterCastSkillAsync(character, skillTarget, cost); skill.OnSkillCasted(this, character, targets); effects = [.. character.Effects.Where(e => e.Level > 0)]; @@ -879,7 +892,7 @@ namespace Milimoe.FunGame.Core.Model LastRound.SkillCost = $"{-cost:0.##} MP"; WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点魔法值,释放了魔法 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); - await OnCharacterActingAsync(character, CharacterActionType.CastSkill, skillTarget, cost); + await OnCharacterCastSkillAsync(character, skillTarget, cost); skill.OnSkillCasted(this, character, targets); } @@ -922,7 +935,7 @@ namespace Milimoe.FunGame.Core.Model WriteLine("[ " + character + $" ] 消耗了 {cost:0.##} 点能量值,释放了爆发技 [ {skill.Name} ]!{(skill.Slogan != "" ? skill.Slogan : "")}"); SkillTarget skillTarget = new(skill, targets); - await OnCharacterActingAsync(character, CharacterActionType.CastSkill, skillTarget, cost); + await OnCharacterCastSkillAsync(character, skillTarget, cost); skill.OnSkillCasted(this, character, targets); } @@ -968,14 +981,19 @@ namespace Milimoe.FunGame.Core.Model { decided = true; WriteLine("[ " + character + $" ] 结束了回合!"); - await OnCharacterActingAsync(character, CharacterActionType.EndTurn); + await OnCharacterDoNothingAsync(character); + } + else + { + decided = true; + WriteLine("[ " + character + $" ] 完全行动不能!"); } } - if (!decided || type == CharacterActionType.None) + if (type == CharacterActionType.None) { WriteLine("[ " + character + $" ] 放弃了行动!"); - await OnCharacterActingAsync(character, CharacterActionType.None); + await OnCharacterGiveUpAsync(character); } LastRound.ActionType = type; @@ -1002,7 +1020,7 @@ namespace Milimoe.FunGame.Core.Model LastRound.CastTime = newHardnessTime; } AddCharacter(character, newHardnessTime, isCheckProtected); - await OnQueueUpdatedAsync(_queue, character, QueueUpdatedReason.Action, "设置角色行动后的硬直时间。"); + await OnQueueUpdatedAsync(_queue, character, newHardnessTime, QueueUpdatedReason.Action, "设置角色行动后的硬直时间。"); LastRound.HardnessTime = newHardnessTime; effects = [.. character.Effects.Where(e => e.Level > 0)]; @@ -1107,19 +1125,19 @@ namespace Milimoe.FunGame.Core.Model { character.HP += reallyReHP; character.MP += reallyReMP; - WriteLine($"角色 {character.Name} 回血:{recoveryHP:0.##} [{character.HP:0.##} / {character.MaxHP:0.##}] / 回蓝:{recoveryMP:0.##} [{character.MP:0.##} / {character.MaxMP:0.##}]"); + WriteLine($"角色 {character.Name} 回血:{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; - WriteLine($"角色 {character.Name} 回血:{recoveryHP:0.##} [{character.HP:0.##} / {character.MaxHP:0.##}]"); + WriteLine($"角色 {character.Name} 回血:{recoveryHP:0.##} [{character.HP:0.##} / {character.MaxHP:0.##}] / 当前能量:{character.EP:0.##}"); } if (reallyReMP > 0) { character.MP += reallyReMP; - WriteLine($"角色 {character.Name} 回蓝:{recoveryMP:0.##} [{character.MP:0.##} / {character.MaxMP:0.##}]"); + WriteLine($"角色 {character.Name} 回蓝:{recoveryMP:0.##} [{character.MP:0.##} / {character.MaxMP:0.##}] / 当前能量:{character.EP:0.##}"); } } @@ -2013,7 +2031,7 @@ namespace Milimoe.FunGame.Core.Model } /// - /// 是否在回合外释放爆发技插队(仅自动化) + /// 是否在回合外释放爆发技插队(仅自动化,手动设置请调用:) /// /// 当前正在行动的角色 /// @@ -2218,7 +2236,7 @@ namespace Milimoe.FunGame.Core.Model { LastRound.Targets = [.. targets]; - await OnCharacterActingAsync(character, CharacterActionType.UseItem, item, targets); + await OnCharacterUseItemAsync(character, item, targets); string line = $"[ {character} ] 使用了物品 [ {item.Name} ]!\r\n[ {character} ] "; @@ -2247,7 +2265,7 @@ namespace Milimoe.FunGame.Core.Model WriteLine(line); SkillTarget skillTarget = new(skill, targets); - await OnCharacterActingAsync(character, CharacterActionType.CastSkill, skillTarget, costMP, costEP); + await OnCharacterCastItemSkillAsync(character, item, skillTarget, costMP, costEP); skill.OnSkillCasted(this, character, targets); return true; @@ -2267,7 +2285,7 @@ namespace Milimoe.FunGame.Core.Model character.Respawn(_original[character.Guid]); WriteLine($"[ {character} ] 已复活!获得 {hardnessTime} {GameplayEquilibriumConstant.InGameTime}的硬直时间。"); AddCharacter(character, hardnessTime, false); - await OnQueueUpdatedAsync(_queue, character, QueueUpdatedReason.Respawn, "设置角色复活后的硬直时间。"); + await OnQueueUpdatedAsync(_queue, character, hardnessTime, QueueUpdatedReason.Respawn, "设置角色复活后的硬直时间。"); LastRound.Respawns.Add(character); _respawnCountdown.Remove(character); if (!_respawnTimes.TryAdd(character, 1)) @@ -2290,7 +2308,7 @@ namespace Milimoe.FunGame.Core.Model _queue.Remove(character); _cutCount.Remove(character); AddCharacter(character, 0, false); - await OnQueueUpdatedAsync(_queue, character, QueueUpdatedReason.PreCastSuperSkill, "设置角色预释放爆发技的硬直时间。"); + await OnQueueUpdatedAsync(_queue, character, 0, QueueUpdatedReason.PreCastSuperSkill, "设置角色预释放爆发技的硬直时间。"); WriteLine("[ " + character + " ] 预释放了爆发技!!"); foreach (Character c in _hardnessTimes.Keys) { @@ -2566,21 +2584,119 @@ namespace Milimoe.FunGame.Core.Model await (DamageToEnemy?.Invoke(this, actor, enemy, damage, isNormalAttack, isMagicDamage, magicType, damageResult) ?? Task.CompletedTask); } - public delegate Task CharacterActingEventHandler(ActionQueue queue, Character actor, CharacterActionType type, params object[] args); + public delegate Task CharacterNormalAttackEventHandler(ActionQueue queue, Character actor, List targets); /// - /// 角色行动事件 + /// 角色普通攻击事件 /// - public event CharacterActingEventHandler? CharacterActing; + public event CharacterNormalAttackEventHandler? CharacterNormalAttack; /// - /// 角色行动事件 + /// 角色普通攻击事件 /// /// - /// - /// + /// /// - protected async Task OnCharacterActingAsync(Character actor, CharacterActionType type, params object[] args) + protected async Task OnCharacterNormalAttackAsync(Character actor, List targets) { - await (CharacterActing?.Invoke(this, actor, type, args) ?? Task.CompletedTask); + await (CharacterNormalAttack?.Invoke(this, actor, targets) ?? Task.CompletedTask); + } + + public delegate Task CharacterPreCastSkillEventHandler(ActionQueue queue, Character actor, SkillTarget skillTarget); + /// + /// 角色吟唱技能事件(包括直接释放战技) + /// + public event CharacterPreCastSkillEventHandler? CharacterPreCastSkill; + /// + /// 角色吟唱技能事件(包括直接释放战技) + /// + /// + /// + /// + protected async Task OnCharacterPreCastSkillAsync(Character actor, SkillTarget skillTarget) + { + await (CharacterPreCastSkill?.Invoke(this, actor, skillTarget) ?? Task.CompletedTask); + } + + public delegate Task CharacterCastSkillEventHandler(ActionQueue queue, Character actor, SkillTarget skillTarget, double cost); + /// + /// 角色释放技能事件 + /// + public event CharacterCastSkillEventHandler? CharacterCastSkill; + /// + /// 角色释放技能事件 + /// + /// + /// + /// + /// + protected async Task OnCharacterCastSkillAsync(Character actor, SkillTarget skillTarget, double cost) + { + await (CharacterCastSkill?.Invoke(this, actor, skillTarget, cost) ?? Task.CompletedTask); + } + + public delegate Task CharacterUseItemEventHandler(ActionQueue queue, Character actor, Item item, List targets); + /// + /// 角色使用物品事件 + /// + public event CharacterUseItemEventHandler? CharacterUseItem; + /// + /// 角色使用物品事件 + /// + /// + /// + /// + /// + protected async Task OnCharacterUseItemAsync(Character actor, Item item, List targets) + { + await (CharacterUseItem?.Invoke(this, actor, item, targets) ?? Task.CompletedTask); + } + + public delegate Task CharacterCastItemSkillEventHandler(ActionQueue queue, Character actor, Item item, SkillTarget skillTarget, double costMP, double costEP); + /// + /// 角色释放物品的技能事件 + /// + public event CharacterCastItemSkillEventHandler? CharacterCastItemSkill; + /// + /// 角色释放物品的技能事件 + /// + /// + /// + /// + /// + /// + /// + protected async Task OnCharacterCastItemSkillAsync(Character actor, Item item, SkillTarget skillTarget, double costMP, double costEP) + { + await (CharacterCastItemSkill?.Invoke(this, actor, item, skillTarget, costMP, costEP) ?? Task.CompletedTask); + } + + public delegate Task CharacterDoNothingEventHandler(ActionQueue queue, Character actor); + /// + /// 角色主动结束回合事件(区别于放弃行动,这个是主动的) + /// + public event CharacterDoNothingEventHandler? CharacterDoNothing; + /// + /// 角色主动结束回合事件(区别于放弃行动,这个是主动的) + /// + /// + /// + protected async Task OnCharacterDoNothingAsync(Character actor) + { + await (CharacterDoNothing?.Invoke(this, actor) ?? Task.CompletedTask); + } + + public delegate Task CharacterGiveUpEventHandler(ActionQueue queue, Character actor); + /// + /// 角色放弃行动事件 + /// + public event CharacterGiveUpEventHandler? CharacterGiveUp; + /// + /// 角色放弃行动事件 + /// + /// + /// + protected async Task OnCharacterGiveUpAsync(Character actor) + { + await (CharacterGiveUp?.Invoke(this, actor) ?? Task.CompletedTask); } public delegate Task GameEndEventHandler(ActionQueue queue, Character winner); @@ -2613,7 +2729,7 @@ namespace Milimoe.FunGame.Core.Model return await (GameEndTeam?.Invoke(this, winner) ?? Task.FromResult(true)); } - public delegate Task QueueUpdatedEventHandler(ActionQueue queue, List characters, Character character, QueueUpdatedReason reason, string msg); + public delegate Task QueueUpdatedEventHandler(ActionQueue queue, List characters, Character character, double hardnessTime, QueueUpdatedReason reason, string msg); /// /// 行动顺序表更新事件 /// @@ -2623,12 +2739,13 @@ namespace Milimoe.FunGame.Core.Model /// /// /// + /// /// /// /// - protected async Task OnQueueUpdatedAsync(List characters, Character character, QueueUpdatedReason reason, string msg = "") + protected async Task OnQueueUpdatedAsync(List characters, Character character, double hardnessTime, QueueUpdatedReason reason, string msg = "") { - await (QueueUpdated?.Invoke(this, characters, character, reason, msg) ?? Task.CompletedTask); + await (QueueUpdated?.Invoke(this, characters, character, hardnessTime, reason, msg) ?? Task.CompletedTask); } #endregion