using System; using System.Collections.Generic; using System.Linq; using Continentis.MainGame.Character; using Continentis.MainGame.Equipment; using SLSFramework.General; using SLSFramework.UModAssistance; using UniRx; using UnityEngine; namespace Continentis.MainGame.Card { public abstract partial class CardLogicBase { [Header("Reference")] public CardInstance card; public CardData cardData => card.cardData; public CharacterBase user => card.user; public HashSet logicComponents { get; private set; } /// /// 生成卡牌逻辑实例 /// public static CardLogicBase GenerateCardLogic(CardData data) { string typeID = ModManager.GetTypeID(data.modName, "Cards", data.categoryName, data.className); Type logicType = ModManager.GetType(typeID); if(logicType == null) { Debug.LogError($"Card class '{typeID}' not found in assemblies."); return null; } if (Activator.CreateInstance(logicType) is CardLogicBase cardLogic) { return cardLogic; } Debug.LogError($"Card class '{typeID}' not found or could not be instantiated."); return null; } public virtual void Initialize(CardInstance cardInstance) { card = cardInstance; logicComponents = new HashSet(); card.eventSubmodule.onTargeting += TargetingEffect; card.eventSubmodule.onUntargeting += UntargetingEffect; } public virtual void SetUpLogicComponents() { } public T AddLogicComponent() where T : CardLogicComponentBase, new() { if (logicComponents.Any(component => component is T)) { Debug.LogWarning($"Card {card.cardData.className} already has component of type {typeof(T)}, cannot add duplicate."); return null; } else { T component = new T(); component.Initialize(this); logicComponents.Add(component); return component; } } public T LogicComponent() where T : CardLogicComponentBase { return logicComponents.OfType().FirstOrDefault(); } public virtual void ApplyAttributeChangesByCard() { } public virtual List PlayEffect(List targetList) { return null; } public virtual void AfterPlayEffect(List targetList) { } } #region Attributes public partial class CardLogicBase { /// /// 设置可变属性值 /// /// 属性名,通常为 /// 是否为叠加,true为叠加,false为覆盖,true时,originalValue为外部传入值 /// 原始伤害值,仅在additive为true时有效,否则此值被覆盖为BaseAttribute /// 伤害增量 public void SetVariableAttribute(string attributeName, int baseOffset, bool additive = false, int originalValue = 0) { card.SetVariableAttribute(attributeName, baseOffset, additive, originalValue); } /// /// 检查卡牌是否具有某属性 /// public bool HasAttribute(string attributeName) { return card.HasAttribute(attributeName); } /// /// 获取卡牌的属性值 /// public int GetAttribute(string attributeName, int defaultValue = 0) { return card.GetAttribute(attributeName, defaultValue); } public float GetRawAttribute(string attributeName, float defaultValue = 0) { return card.GetRawAttribute(attributeName, defaultValue); } /// /// 设置卡牌的属性值 /// public void SetAttribute(string attributeName, int value) { card.SetAttribute(attributeName, value); } /// /// 设置卡牌的属性值 /// public void SetAttribute(string attributeName, float value) { card.SetAttribute(attributeName, value); } /// /// 修改卡牌的属性值 /// public void ModifyAttribute(string attributeName, int delta) { card.ModifyAttribute(attributeName, delta); } } #endregion #region Command public partial class CardLogicBase { /// /// 创建一个命令组,组内命令按顺序执行 /// /// 命令模板 protected CommandGroup SingleCommandGroup(params CommandBase[] commands) { return SingleCommandGroup(ExecutionMode.Parallel, commands); } /// /// 创建一个命令组,组内命令按指定顺序执行 /// /// 执行模式,顺序或并行 /// 命令模板 protected virtual CommandGroup SingleCommandGroup( ExecutionMode executionMode = ExecutionMode.Parallel, params CommandBase[] commands) { CommandGroup singleGroup = new CommandGroup(executionMode); foreach (CommandBase template in commands) { singleGroup.AddCommand(template.Clone()); } return singleGroup; } /// /// 对目标列表中的每个目标,依次执行一组有参函数命令,每组命令的参数为targetList中的个体,主体Group顺序执行,单体Group并行执行。 /// /// 目标列表 /// 单体命令模板 protected CommandGroup TargetListCommandGroup(List targetList, params CommandBase[] singleCommands) { return TargetListCommandGroup(targetList, ExecutionMode.Sequential, ExecutionMode.Parallel, singleCommands); } /// /// 对目标列表中的每个目标,依次执行一组有参函数命令,每组命令的参数为targetList中的个体。 /// /// 目标列表 /// 主体Group(各个目标)的执行顺序 /// 单体Group(一个目标中指令)的执行顺序 /// 单体命令模板 protected virtual CommandGroup TargetListCommandGroup( List targetList, ExecutionMode mainExecutionMode = ExecutionMode.Sequential, ExecutionMode singleExecutionMode = ExecutionMode.Parallel, params CommandBase[] singleCommands) { CommandGroup mainGroup = new CommandGroup(mainExecutionMode); foreach (CharacterBase target in targetList) { CommandGroup singleGroup = new CommandGroup(singleExecutionMode); foreach (CommandBase template in singleCommands) { CommandBase clone = template.Clone(); List allCommands = new List(); if (clone is CommandGroup group) { allCommands.AddRange(group.GetAllCommands(true)); } else { allCommands.Add(clone); } foreach (CommandBase cmd in allCommands) { cmd.selfContext.context["Target"] = target; } singleGroup.AddCommand(clone); } mainGroup.AddCommand(singleGroup); } return mainGroup; } } #endregion #region Attack public partial class CardLogicBase { /// /// 获取最终伤害 /// /// 目标 /// 元素标签,若为null则使用卡牌的元素标签 public virtual int GetTargetedFinalDamage(CharacterBase target, List elementalTags = null) { return GetFinalDamage(target, elementalTags, out _, out _, out _, out _); } /// /// 获取最终伤害 /// /// 元素标签,若为null则使用卡牌的元素标签 public virtual int GetNoTargetFinalDamage(List elementalTags = null) { return GetFinalDamage(null, elementalTags, out _, out _, out _, out _); } protected virtual int GetFinalDamage(CharacterBase target, List elementalTags, out float baseDamageAfterOffset, out float elementalMultiplier, out float magicMultiplier, out float finalMultiplier) { bool haveTarget = target != null; elementalTags ??= card.GetElementalKeywords(); //----计算基础伤害增量---- int physicsOffset = 0; if (card.HasKeyword("Physics") || card.HasKeyword("Slash") || card.HasKeyword("Prick") || card.HasKeyword("Strike")) { physicsOffset = user.GetAttribute("PhysicsDamageDealtOffset"); //物理伤害基础增量 } int magicOffset = 0; if (card.HasKeyword("Magic") || card.HasKeyword("Arcane") || card.HasKeyword("Sorcery")) { magicOffset = user.GetAttribute("MagicDamageDealtOffset"); //魔法伤害基础增量 } //----计算伤害因数---- //计算元素伤害加成,注意,“物理Physics”也是一种元素,因此下方没有“通用物理伤害加成”的计算 elementalMultiplier = 1; foreach (string element in elementalTags) { float targetGain = haveTarget ? target.GetRawAttribute(element + "DamageGainMultiplier", 1) : 1; elementalMultiplier *= user.GetRawAttribute(element + "DamageDealtMultiplier", 1) * targetGain; } //计算通用的魔法伤害加成 magicMultiplier = 1; if (card.HasKeyword("Magic") || card.HasKeyword("Arcane") || card.HasKeyword("Sorcery")) { float targetGain = haveTarget ? target.GetRawAttribute("MagicDamageGainMultiplier", 1) : 1; magicMultiplier = user.GetRawAttribute("MagicDamageDealtMultiplier", 1) * targetGain; } //计算最终伤害加成 float targetFinalGain = haveTarget ? target.GetRawAttribute("FinalDamageGainMultiplier", 1) : 1; finalMultiplier = user.GetRawAttribute("FinalDamageDealtMultiplier", 1) * targetFinalGain; //----计算最终伤害---- baseDamageAfterOffset = card.attributeSubmodule.GetCurrentAttribute("Damage") + physicsOffset + magicOffset; float finalDamage = baseDamageAfterOffset * elementalMultiplier * magicMultiplier * finalMultiplier; return Mathf.RoundToInt(finalDamage); } } #endregion #region Buffs public partial class CardLogicBase { /// /// 创建一个角色战斗Buff实例 /// 注意,此函数依赖ModManager的类型注册功能,请确保在Mod加载时已注册对应Buff类型 /// 此函数中的T并不是原型参数,而是获取Mod中注册的类型用的 /// public T CreateCharacterBuff(params object[] parameters) where T :CharacterCombatBuffBase { string buffTypeID = ModManager.GetTypeID(typeof(T)); if (string.IsNullOrEmpty(buffTypeID)) { Debug.LogError($"Failed to get buff name for type {typeof(T).FullName}"); return null; } return ModManager.CreateInstance(buffTypeID, parameters); } public T CreateCharacterBuff(string buffTypeID, params object[] parameters) where T :CharacterCombatBuffBase { if (string.IsNullOrEmpty(buffTypeID)) { Debug.LogError($"Failed to get buff name for type {typeof(T).FullName}"); return null; } return ModManager.CreateInstance(buffTypeID, parameters); } /// /// 创建一个卡牌战斗Buff实例 /// 注意,此函数依赖ModManager的类型注册功能,请确保在Mod加载时已注册对应Buff类型 /// 此函数中的T并不是原型参数,而是获取Mod中注册的类型用的 /// public T CreateCardBuff(params object[] parameters) where T :CardBuffBase { string buffTypeID = ModManager.GetTypeID(typeof(T)); if (string.IsNullOrEmpty(buffTypeID)) { Debug.LogError($"Failed to get buff name for type {typeof(T).FullName}"); return null; } return ModManager.CreateInstance(buffTypeID, parameters); } public T CreateCardBuff(string buffTypeID, params object[] parameters) where T :CardBuffBase { if (string.IsNullOrEmpty(buffTypeID)) { Debug.LogError($"Failed to get buff name for type {typeof(T).FullName}"); return null; } return ModManager.CreateInstance(buffTypeID, parameters); } } #endregion public abstract partial class CardLogicBase { /// /// 获取衍生卡牌数据 /// public CardData GetDerivativeCardData(int index) { return ModManager.GetData(cardData.derivativeCardDataRefs[index]); } /// /// 获取衍生卡牌数据 /// public CardData GetDerivativeCardData(string dataName) { if (cardData.derivativeCardDataRefs.Contains(dataName)) { return ModManager.GetData(dataName); } Debug.LogError($"Card {cardData.className} does not contain derivative card data '{dataName}'."); return null; } /// /// 选中目标时触发的效果,效果在所有逻辑组件的Targeting之前执行(在SetUp函数生成EventSubmodule的时候)。 /// 如果必须需要在逻辑组件之后执行,请重写Initialize函数。 /// public virtual void TargetingEffect(CharacterBase target) { } /// /// 取消选中目标时触发的效果,效果在所有逻辑组件的Untargeting之前执行(在SetUp函数生成EventSubmodule的时候)。 /// 如果必须需要在逻辑组件之后执行,请重写Initialize函数。 /// public virtual void UntargetingEffect() { } } /// /// 卡牌逻辑组件基类接口 /// 注意,所有的子接口需要实现的函数: /// ComponentTargetingEffect:此牌瞄准目标时调用 /// ComponentUntargetingEffect:此牌取消瞄准目标时调用 /// public abstract class CardLogicComponentBase { protected CardLogicBase mainLogic; protected CardInstance card => mainLogic.card; protected CharacterBase user => card.user; protected CombatTeam usingTeam => card.usingTeam; public virtual void Initialize(CardLogicBase card) { this.mainLogic = card; this.card.eventSubmodule.onTargeting += TargetingEffect; this.card.eventSubmodule.onUntargeting += UntargetingEffect; } protected virtual void TargetingEffect(CharacterBase target) { } protected virtual void UntargetingEffect() { } } }