using System.Text; using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Interface.Entity; using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Entity { /// /// 特殊效果类,需要继承 /// public class Effect : BaseEntity { /// /// 所属的技能 /// public Skill Skill { get; } /// /// 特殊效果类型 /// 注意:如果技能特效没有原生施加控制效果,请始终保持此属性为 。 /// public virtual EffectType EffectType { get; set; } = EffectType.None; /// /// 持续性的 /// 配合 使用,而不是 。 /// public virtual bool Durative { get; set; } = false; /// /// 持续时间 /// 配合 使用。 /// public virtual double Duration { get; set; } = 0; /// /// 持续时间(回合) /// 使用此属性需要将 设置为 false。 /// public virtual int DurationTurn { get; set; } = 0; /// /// 剩余持续时间 /// public double RemainDuration { get; set; } = 0; /// /// 剩余持续时间(回合) /// public int RemainDurationTurn { get; set; } = 0; /// /// 是否是没有具体持续时间的持续性特效 /// public virtual bool DurativeWithoutDuration { get; set; } = false; /// /// 附属于某个特效 /// public Effect? ParentEffect { get; set; } = null; /// /// 是否是某个特效的附属 /// public bool IsSubsidiary => ParentEffect != null; /// /// 是否强制在状态栏中隐藏 /// public virtual bool ForceHideInStatusBar { get; set; } = false; /// /// 是否显示在状态栏 /// public bool ShowInStatusBar => !ForceHideInStatusBar && (Skill.Item is null || (Durative && Duration > 0) || DurationTurn > 0 || DurativeWithoutDuration); /// /// 特效是否生效 /// public bool IsInEffect => Level > 0 && !IsBeingTemporaryDispelled; /// /// 魔法类型 /// public virtual MagicType MagicType { get; set; } = MagicType.None; /// /// 驱散性 [ 能驱散什么特效,默认无驱散 ] /// public virtual DispelType DispelType { get; set; } = DispelType.None; /// /// 被驱散性 [ 能被什么驱散类型驱散,默认弱驱散 ] /// public virtual DispelledType DispelledType { get; set; } = DispelledType.Weak; /// /// 是否是负面效果 /// public virtual bool IsDebuff { get; set; } = false; /// /// 驱散性和被驱散性的具体说明 /// public virtual string DispelDescription { get => GetDispelDescription("\r\n"); set => _dispelDescription = value; } /// /// 是否具备弱驱散功能(强驱散包含在内) /// public bool CanWeakDispel => DispelType == DispelType.Weak || DispelType == DispelType.DurativeWeak || DispelType == DispelType.TemporaryWeak || CanStrongDispel; /// /// 是否具备强驱散功能 /// public bool CanStrongDispel => DispelType == DispelType.Strong || DispelType == DispelType.DurativeStrong || DispelType == DispelType.TemporaryStrong; /// /// 是否是临时驱散 [ 需注意持续性驱散是在持续时间内将特效无效化而不是移除,适用临时驱散机制 ] /// public bool IsTemporaryDispel => DispelType == DispelType.DurativeWeak || DispelType == DispelType.TemporaryWeak || DispelType == DispelType.DurativeStrong || DispelType == DispelType.TemporaryStrong; /// /// 是否处于临时被驱散状态 [ 如果使用后不手动恢复为 false,那么行动顺序表会在时间流逝时恢复它 ] /// 注意看标准实现,需要配合 使用 /// public bool IsBeingTemporaryDispelled { get; set; } = false; /// /// 无视免疫类型 /// public virtual ImmuneType IgnoreImmune { get; set; } = ImmuneType.None; /// /// 效果描述 /// public virtual string Description { get; set; } = ""; /// /// 等级,跟随技能的等级 /// public int Level => Skill.Level; /// /// 此特效的施加者,用于溯源 /// public virtual Character? Source { get; set; } = null; /// /// 游戏中的行动顺序表实例,在技能效果被触发时,此实例会获得赋值,使用时需要判断其是否存在 /// public IGamingQueue? GamingQueue { get; set; } = null; /// /// 用于动态扩展特效的参数 /// public Dictionary Values { get; } = []; /// /// 输出文本或日志 /// public Action WriteLine { get { if (GamingQueue is null) return Console.WriteLine; else return GamingQueue.WriteLine; } } /// /// Values 构造动态特效参考这个构造函数 /// /// /// protected Effect(Skill skill, Dictionary? args = null) { Skill = skill; if (args != null) { foreach (string key in args.Keys) { Values[key] = args[key]; } } } internal Effect() { Skill = Factory.GetSkill(); } /// /// 获得此特效时 /// /// public virtual void OnEffectGained(Character character) { } /// /// 失去此特效时 /// /// public virtual void OnEffectLost(Character character) { } /// /// 在伤害计算前修改伤害类型 /// /// /// /// /// /// public virtual void AlterDamageTypeBeforeCalculation(Character character, Character enemy, ref bool isNormalAttack, ref DamageType damageType, ref MagicType magicType) { } /// /// 在伤害计算前修改预期伤害 /// /// /// /// /// /// /// /// /// 返回伤害增减值 public virtual double AlterExpectedDamageBeforeCalculation(Character character, Character enemy, double damage, bool isNormalAttack, DamageType damageType, MagicType magicType, Dictionary totalDamageBonus) { return 0; } /// /// 在伤害计算完成后修改实际伤害 [ 允许取消伤害 ] /// /// /// /// /// /// /// /// /// /// /// 返回伤害增减值 public virtual double AlterActualDamageAfterCalculation(Character character, Character enemy, double damage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult, ref bool isEvaded, Dictionary totalDamageBonus) { return 0; } /// /// 在应用真实伤害前修改伤害 [ 允许取消伤害 ] /// /// /// /// /// /// /// 返回 true 取消伤害 public virtual bool BeforeApplyTrueDamage(Character character, Character enemy, double damage, bool isNormalAttack, DamageResult damageResult) { return false; } /// /// 伤害应用时触发 /// /// /// /// /// /// /// /// /// /// /// public virtual void OnApplyDamage(Character character, Character enemy, double damage, double actualDamage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult, string shieldMessage, ref string originalMessage) { } /// /// 在完成普通攻击动作之后修改硬直时间 /// /// /// /// public virtual void AlterHardnessTimeAfterNormalAttack(Character character, ref double baseHardnessTime, ref bool isCheckProtected) { } /// /// 在完成释放技能动作之后修改硬直时间 /// /// /// /// /// public virtual void AlterHardnessTimeAfterCastSkill(Character character, Skill skill, ref double baseHardnessTime, ref bool isCheckProtected) { } /// /// 在造成伤害时,修改获得的能量 /// /// /// public virtual void AlterEPAfterDamage(Character character, ref double baseEP) { } /// /// 在受到伤害时,修改获得的能量 /// /// /// public virtual void AlterEPAfterGetDamage(Character character, ref double baseEP) { } /// /// 技能开始吟唱时 [ 爆发技插队可触发此项 ] /// /// /// public virtual void OnSkillCasting(Character caster, List targets) { } /// /// 技能吟唱被打断时 /// /// /// /// public virtual void OnSkillCastInterrupted(Character caster, Skill skill, Character interrupter) { } /// /// 吟唱结束后释放技能(魔法)/ 直接释放技能(战技/爆发技) /// /// /// /// public virtual void OnSkillCasted(Character caster, List targets, Dictionary others) { } /// /// 对目标触发技能效果(局外) /// /// /// /// public virtual void OnSkillCasted(User user, List targets, Dictionary others) { } /// /// 时间流逝时 /// /// /// public virtual void OnTimeElapsed(Character character, double elapsed) { } /// /// 在完成伤害结算后 /// /// /// /// /// /// /// /// /// public virtual void AfterDamageCalculation(Character character, Character enemy, double damage, double actualDamage, bool isNormalAttack, DamageType damageType, MagicType magicType, DamageResult damageResult) { } /// /// 在治疗结算前修改治疗值 /// /// /// /// /// /// /// 返回治疗增减值 public virtual double AlterHealValueBeforeHealToTarget(Character actor, Character target, double heal, ref bool canRespawn, Dictionary totalHealBonus) { return 0; } /// /// 在特效持有者的回合开始前 /// /// /// /// /// /// public virtual void OnTurnStart(Character character, List enemys, List teammates, List skills, List items) { } /// /// 在特效持有者的回合结束后 /// /// public virtual void OnTurnEnd(Character character) { } /// /// 技能被升级时 /// /// /// public virtual void OnSkillLevelUp(Character character, double level) { } /// /// 特效持有者升级时 /// /// /// public virtual void OnOwnerLevelUp(Character owner, double level) { } /// /// 在完成死亡结算后 [ 全体广播 ] /// /// /// /// /// public virtual void AfterDeathCalculation(Character death, Character? killer, Dictionary continuousKilling, Dictionary earnedMoney) { } /// /// 闪避检定前触发 /// /// /// /// /// 返回 false 表示不进行闪避检定 public virtual bool BeforeEvadeCheck(Character actor, Character enemy, ref double throwingBonus) { return true; } /// /// 在触发闪避时 /// /// /// /// /// 返回 true 表示无视闪避 public virtual bool OnEvadedTriggered(Character actor, Character enemy, double dice) { return false; } /// /// 暴击检定前触发 /// /// /// /// /// 返回 false 表示不进行暴击检定 public virtual bool BeforeCriticalCheck(Character actor, Character enemy, ref double throwingBonus) { return true; } /// /// 在触发暴击时 /// /// /// /// public virtual void OnCriticalDamageTriggered(Character actor, Character enemy, double dice) { } /// /// 角色属性发生变化 /// /// public virtual void OnAttributeChanged(Character character) { } /// /// 行动开始前,修改可选择的 , , 列表 /// 注意 是副本,修改无效 /// /// /// /// /// /// /// public virtual void AlterSelectListBeforeAction(Character character, List enemys, List teammates, List skills, Dictionary continuousKilling, Dictionary earnedMoney) { } /// /// 开始选择目标前,修改可选择的 , 列表 /// 有两种,使用时注意判断是 还是 /// /// /// /// /// public virtual void AlterSelectListBeforeSelection(Character character, ISkill skill, List enemys, List teammates) { } /// /// 行动开始前,指定角色的行动,而不是使用顺序表自带的逻辑;或者修改对应的操作触发概率 /// /// /// /// /// /// /// /// /// public virtual CharacterActionType AlterActionTypeBeforeAction(Character character, CharacterState state, ref bool canUseItem, ref bool canCastSkill, ref double pUseItem, ref double pCastSkill, ref double pNormalAttack) { return CharacterActionType.None; } /// /// 可重写对某个特效的驱散实现,适用于特殊驱散类型 /// /// /// /// /// public virtual void OnDispellingEffect(Character dispeller, Character target, Effect effect, bool isEnemy) { bool isDispel = false; // 先看特效整体是不是能被驱散的 switch (effect.DispelledType) { case DispelledType.Weak: if (CanWeakDispel) { isDispel = true; } break; case DispelledType.Strong: if (CanStrongDispel) { isDispel = true; } break; default: break; } if (isDispel) { bool removeEffectTypes = false; bool removeEffectStates = false; // 接下来再看看特效给角色施加的特效类型和改变状态是不是能被驱散的 // 检查特效持续性 if (effect.DurativeWithoutDuration || (effect.Durative && effect.Duration > 0) || effect.DurationTurn > 0) { // 先从角色身上移除特效类型 if (isEnemy != effect.IsDebuff) { if (target.CharacterEffectTypes.TryGetValue(effect, out List? types) && types != null) { RemoveEffectTypesByDispel(types, isEnemy); if (types.Count == 0) { target.CharacterEffectTypes.Remove(effect); removeEffectTypes = true; } } else { removeEffectTypes = true; } } // 友方移除控制状态 if (!isEnemy && effect.IsDebuff) { if (target.CharacterEffectStates.TryGetValue(effect, out List? states) && states != null) { RemoveEffectStatesByDispel(states); if (states.Count == 0) { target.CharacterEffectStates.Remove(effect); removeEffectStates = true; } } else { removeEffectStates = true; } } target.UpdateCharacterState(); } // 移除整个特效 if (removeEffectTypes && removeEffectStates) { if (IsTemporaryDispel) { effect.IsBeingTemporaryDispelled = true; } else { effect.RemainDuration = 0; effect.RemainDurationTurn = 0; target.Effects.Remove(effect); } effect.OnEffectLost(target); } } } /// /// 当特效被驱散时的 /// /// /// /// /// /// 返回 false 可以阻止驱散 public virtual bool OnEffectIsBeingDispelled(Character dispeller, Character target, Effect dispellerEffect, bool isEnemy) { return true; } /// /// 当角色触发生命偷取后 /// /// /// /// /// public virtual void AfterLifesteal(Character character, Character enemy, double damage, double steal) { } /// /// 在角色护盾结算前触发 /// /// /// /// /// /// /// /// /// 返回 false 可以跳过护盾结算 public virtual bool BeforeShieldCalculation(Character character, Character attacker, DamageType damageType, MagicType magicType, double damage, ref double damageReduce, ref string message) { return true; } /// /// 在角色护盾有效防御时 [ 破碎本身不会触发此钩子,但破碎后化解可触发 ] /// /// /// /// /// /// /// public virtual void OnShieldNeutralizeDamage(Character character, Character attacker, DamageType damageType, MagicType magicType, double damage, ShieldType shieldType) { } /// /// 当角色护盾破碎时 [ 非绑定特效,只有同种类型的总护盾值小于等于 0 时触发 ] /// /// /// /// /// /// 返回 false 可以阻止后续扣除角色生命值 public virtual bool OnShieldBroken(Character character, Character attacker, ShieldType type, double overFlowing) { return true; } /// /// 当角色护盾破碎时 [ 绑定特效的护盾值小于等于 0 时便会触发 ] /// /// /// /// /// /// 返回 false 可以阻止后续扣除角色生命值 public virtual bool OnShieldBroken(Character character, Character attacker, Effect effect, double overFlowing) { return true; } /// /// 在免疫检定时 /// /// /// /// /// /// false:免疫检定不通过 public virtual bool OnImmuneCheck(Character actor, Character enemy, ISkill skill, Item? item = null) { return true; } /// /// 在伤害免疫检定时 /// /// /// /// /// /// /// /// false:免疫检定不通过 public virtual bool OnDamageImmuneCheck(Character actor, Character enemy, bool isNormalAttack, DamageType damageType, MagicType magicType, double damage) { return true; } /// /// 对敌人造成技能伤害 [ 强烈建议使用此方法造成伤害而不是自行调用 ] /// /// /// /// /// /// /// public DamageResult DamageToEnemy(Character actor, Character enemy, DamageType damageType, MagicType magicType, double expectedDamage) { if (GamingQueue is null) return DamageResult.Evaded; int changeCount = 0; DamageResult result = DamageResult.Normal; double damage = expectedDamage; if (damageType != DamageType.True) { result = damageType == DamageType.Physical ? GamingQueue.CalculatePhysicalDamage(actor, enemy, false, expectedDamage, out damage, ref changeCount) : GamingQueue.CalculateMagicalDamage(actor, enemy, false, MagicType, expectedDamage, out damage, ref changeCount); } GamingQueue.DamageToEnemyAsync(actor, enemy, damage, false, damageType, magicType, result); return result; } /// /// 治疗一个目标 [ 强烈建议使用此方法而不是自行调用 ] /// /// /// /// /// public void HealToTarget(Character actor, Character target, double heal, bool canRespawn = false) { GamingQueue?.HealToTargetAsync(actor, target, heal, canRespawn); } /// /// 打断施法 [ 尽可能的调用此方法而不是直接调用 ,以防止中断性变更 ] /// /// /// public void InterruptCasting(Character caster, Character interrupter) { GamingQueue?.InterruptCastingAsync(caster, interrupter); } /// /// 打断施法 [ 用于使敌人目标丢失 ] [ 尽可能的调用此方法而不是直接调用 ,以防止中断性变更 ] /// /// public void InterruptCasting(Character interrupter) { GamingQueue?.InterruptCastingAsync(interrupter); } /// /// 将特效状态设置到角色上 [ 尽可能的调用此方法而不是自己实现 ] /// /// /// public void AddEffectStatesToCharacter(Character character, List states) { if (character.CharacterEffectStates.TryGetValue(this, out List? value) && value != null) { states.AddRange(value); } states = [.. states.Distinct()]; character.CharacterEffectStates[this] = states; character.UpdateCharacterState(); } /// /// 将特效控制效果设置到角色上 [ 尽可能的调用此方法而不是自己实现 ] /// 施加 EffectType 的同时也会施加 CharacterState,参见 /// /// /// public void AddEffectTypeToCharacter(Character character, List types) { if (character.CharacterEffectTypes.TryGetValue(this, out List? value) && value != null) { types.AddRange(value); } types = [.. types.Distinct()]; character.CharacterEffectTypes[this] = types; List states = []; foreach (EffectType type in types) { states.Add(SkillSet.GetCharacterStateByEffectType(type)); } AddEffectStatesToCharacter(character, states); } /// /// 将免疫状态设置到角色上 [ 尽可能的调用此方法而不是自己实现 ] /// /// /// public void AddImmuneTypesToCharacter(Character character, List types) { if (character.CharacterImmuneTypes.TryGetValue(this, out List? value) && value != null) { types.AddRange(value); } types = [.. types.Distinct()]; character.CharacterImmuneTypes[this] = types; character.UpdateCharacterState(); } /// /// 将特效状态从角色身上移除 [ 尽可能的调用此方法而不是自己实现 ] /// /// public void RemoveEffectStatesFromCharacter(Character character) { character.CharacterEffectStates.Remove(this); character.UpdateCharacterState(); } /// /// 将特效控制效果从角色身上移除 [ 尽可能的调用此方法而不是自己实现 ] /// /// public void RemoveEffectTypesFromCharacter(Character character) { character.CharacterEffectTypes.Remove(this); character.UpdateCharacterState(); } /// /// 将免疫状态从角色身上移除 [ 尽可能的调用此方法而不是自己实现 ] /// /// public void RemoveImmuneTypesFromCharacter(Character character) { character.CharacterImmuneTypes.Remove(this); character.UpdateCharacterState(); } /// /// 从角色身上消除特效类型 [ 如果重写了 ,则尽可能的调用此方法而不是自己实现 ] /// /// /// public void RemoveEffectTypesByDispel(List types, bool isEnemy) { EffectType[] loop = [.. types]; foreach (EffectType type in loop) { bool isDebuff = SkillSet.GetIsDebuffByEffectType(type); if (isEnemy == isDebuff) { // 简单判断,敌方不考虑 debuff,友方只考虑 debuff continue; } DispelledType dispelledType = SkillSet.GetDispelledTypeByEffectType(type); bool canDispel = false; switch (dispelledType) { case DispelledType.Weak: if (CanWeakDispel) canDispel = true; break; case DispelledType.Strong: if (CanStrongDispel) canDispel = true; break; default: break; } if (canDispel) { types.Remove(type); } } } /// /// 从角色身上消除状态类型 [ 如果重写了 ,则尽可能的调用此方法而不是自己实现 ] /// /// public void RemoveEffectStatesByDispel(List states) { CharacterState[] loop = [.. states]; foreach (CharacterState state in loop) { DispelledType dispelledType = DispelledType.Weak; switch (state) { case CharacterState.NotActionable: case CharacterState.ActionRestricted: case CharacterState.BattleRestricted: dispelledType = DispelledType.Strong; break; case CharacterState.SkillRestricted: case CharacterState.AttackRestricted: break; default: break; } bool canDispel = false; switch (dispelledType) { case DispelledType.Weak: if (CanWeakDispel) canDispel = true; break; case DispelledType.Strong: if (CanStrongDispel) canDispel = true; break; default: break; } if (canDispel) { states.Remove(state); } } } /// /// 驱散目标 [ 尽可能的调用此方法而不是自己实现 ] /// 此方法会触发 /// /// /// /// public void Dispel(Character dispeller, Character target, bool isEnemy) { if (DispelType == DispelType.None) { return; } Effect[] effects = [.. target.Effects.Where(e => e.IsInEffect && e.ShowInStatusBar)]; foreach (Effect effect in effects) { if (effect.OnEffectIsBeingDispelled(dispeller, target, this, isEnemy)) { OnDispellingEffect(dispeller, target, effect, isEnemy); } } } /// /// 修改角色的硬直时间 [ 尽可能的调用此方法而不是自己实现 ] /// /// 角色 /// 加值 /// 是否是百分比 /// 是否使用插队保护机制 public void ChangeCharacterHardnessTime(Character character, double addValue, bool isPercentage, bool isCheckProtected) { GamingQueue?.ChangeCharacterHardnessTime(character, addValue, isPercentage, isCheckProtected); } /// /// 设置角色为 AI 控制 [ 系统控制 ] /// /// /// /// public void SetCharactersToAIControl(bool cancel = false, params IEnumerable characters) { GamingQueue?.SetCharactersToAIControl(true, cancel, characters); } /// /// 检查角色是否在 AI 控制状态 /// /// /// public bool IsCharacterInAIControlling(Character character) { return GamingQueue?.IsCharacterInAIControlling(character) ?? false; } /// /// 返回特效详情 /// /// public override string ToString() { StringBuilder builder = new(); string isDurative = ""; if (Durative) { isDurative = $"(剩余:{RemainDuration:0.##} {GameplayEquilibriumConstant.InGameTime})"; } else if (DurationTurn > 0) { isDurative = $"(剩余:{RemainDurationTurn} 回合)"; } builder.Append($"【{Name} - 等级 {Level}】{Description}{isDurative}"); string dispels = GetDispelDescription(","); if (dispels != "") { builder.Append($"({dispels})"); } if (IsBeingTemporaryDispelled) { builder.Append("(已被临时驱散)"); } return builder.ToString(); } /// /// 复制一个特效 /// /// public Effect Copy(Skill skill, bool copyByCode = false) { Dictionary args = new() { { "skill", skill }, { "values", Values } }; Effect copy = Factory.OpenFactory.GetInstance(Id, Name, args); if (!copyByCode) { copy.Id = Id; copy.Name = Name; copy.Description = Description; copy.DispelDescription = DispelDescription; copy.EffectType = EffectType; copy.DispelType = DispelType; copy.DispelledType = DispelledType; copy.IsDebuff = IsDebuff; copy.IgnoreImmune = IgnoreImmune; copy.DurativeWithoutDuration = DurativeWithoutDuration; copy.MagicType = MagicType; copy.GamingQueue = GamingQueue; } copy.Durative = Durative; copy.Duration = Duration; copy.DurationTurn = DurationTurn; return copy; } /// /// 比较两个特效 /// /// /// public override bool Equals(IBaseEntity? other) { return other is Effect c && c.Id + "." + Name == Id + "." + Name; } /// /// 获取驱散描述 /// /// /// private string GetDispelDescription(string separator) { if (_dispelDescription.Trim() != "") { return _dispelDescription; } else if (DispelType != DispelType.None || DispelledType != DispelledType.Weak) { List dispels = []; if (DispelType != DispelType.None) { dispels.Add($"驱散性:{SkillSet.GetDispelType(DispelType)}"); } if (DispelledType != DispelledType.Weak) { dispels.Add($"被驱散性:{SkillSet.GetDispelledType(DispelledType)}"); } return string.Join(separator, dispels); } return ""; } /// /// 驱散描述 /// private string _dispelDescription = ""; } }