Card爆改!

This commit is contained in:
SoulliesOfficial
2025-11-15 12:17:34 -05:00
parent 85bbe2431c
commit 5fe665d0ce
121 changed files with 838 additions and 783 deletions

View File

@@ -13,27 +13,9 @@ namespace Continentis.MainGame.Card
public abstract partial class CardLogicBase
{
[Header("Reference")]
public CardData cardData;
public CardInstance cardInstance;
public CompositeDisposable disposables = new CompositeDisposable();
public ICardOwner owner => cardInstance.owner;
public CharacterBase user => cardInstance.user;
public CombatTeam UsingTeam => cardInstance.usingTeam;
public HandCardView handCardView => cardInstance.handCardView;
public IntentionCardView intentionCardView => cardInstance.intentionCardView;
[Header("Card Base Info")]
public Guid cardID;
public int upgradeLevel;
[Header("Submodules")]
public AttributeSubmodule attributeSubmodule { get; private set; }
public WeightSubmodule weightSubmodule { get; private set; }
public CombatBuffSubmodule combatBuffSubmodule { get; private set; }
public EventSubmodule eventSubmodule { get; private set; }
public ContentSubmodule contentSubmodule { get; private set; }
public PlaySubmodule playSubmodule { get; private set; }
public CardInstance card;
public CardData cardData => card.cardData;
public CharacterBase user => card.user;
public HashSet<CardLogicComponentBase> logicComponents { get; private set; }
/// <summary>
@@ -52,8 +34,6 @@ namespace Continentis.MainGame.Card
if (Activator.CreateInstance(logicType) is CardLogicBase cardLogic)
{
cardLogic.cardData = data;
cardLogic.Setup();
return cardLogic;
}
@@ -61,45 +41,24 @@ namespace Continentis.MainGame.Card
return null;
}
public void Setup()
public virtual void Initialize(CardInstance cardInstance)
{
this.cardID = Guid.NewGuid();
this.attributeSubmodule = new AttributeSubmodule(this);
this.weightSubmodule = new WeightSubmodule(this);
this.eventSubmodule = new EventSubmodule(this);
this.combatBuffSubmodule = new CombatBuffSubmodule(this);
this.contentSubmodule = new ContentSubmodule(this);
this.playSubmodule = new PlaySubmodule(this);
this.logicComponents = new HashSet<CardLogicComponentBase>();
SetUpLogicComponents();
card = cardInstance;
logicComponents = new HashSet<CardLogicComponentBase>();
card.eventSubmodule.onTargeting += TargetingEffect;
card.eventSubmodule.onUntargeting += UntargetingEffect;
}
protected virtual void SetUpLogicComponents()
public virtual void SetUpLogicComponents()
{
}
public virtual void Initialize()
{
RefreshCardAttributes();
if (HasKeyword("Instant")) //如果是“瞬发”牌,添加抽牌后立刻打出的事件
{
eventSubmodule.onDraw.InsertByPriority("Instant", new PrioritizedAction(() =>
{
DetectTargetsValidity(out List<CharacterBase> valid, out _, out _);
Play(SetRandomTargets(valid), user);
}, 99));
}
}
public T AddLogicComponent<T>() where T : CardLogicComponentBase, new()
{
if (logicComponents.Any(component => component is T))
{
Debug.LogWarning($"Card {cardData.className} already has component of type {typeof(T)}, cannot add duplicate.");
Debug.LogWarning($"Card {card.cardData.className} already has component of type {typeof(T)}, cannot add duplicate.");
return null;
}
else
@@ -116,30 +75,319 @@ namespace Continentis.MainGame.Card
return logicComponents.OfType<T>().FirstOrDefault();
}
public void UpgradeCard()
public virtual void ApplyAttributeChangesByCard()
{
if (owner is not CombatTeam)
{
KeyValuePair<string, List<CardInstance>> currentPile = cardInstance.deck.GetCardLocation(cardInstance, out int index);
if (!cardData.upgradeNode.isTerminalNode)
{
cardInstance.DestroyHandCardView();
CardData newData = cardData.upgradeNode.upgradeCards[0]; //后续可改为选择升级方向
CardLogicBase newLogic = CardLogicBase.GenerateCardLogic(newData);
cardInstance.cardLogic = newLogic;
newLogic.cardInstance = cardInstance;
cardInstance.cardLogic.Initialize();
if (user is PlayerHero)
cardInstance.GenerateHandCardView(CombatUIManager.Instance.combatMainPage.Pile(currentPile.Key), index);
}
}
else
{
}
}
public virtual List<CommandBase> PlayEffect(List<CharacterBase> targetList)
{
return null;
}
public virtual void AfterPlayEffect(List<CharacterBase> targetList)
{
}
}
#region Attributes
public partial class CardLogicBase
{
/// <summary>
/// 设置可变属性值
/// </summary>
/// <param name="attributeName">属性名,通常为</param>
/// <param name="additive">是否为叠加true为叠加false为覆盖true时originalValue为外部传入值</param>
/// <param name="originalValue">原始伤害值仅在additive为true时有效否则此值被覆盖为BaseAttribute</param>
/// <param name="baseOffset">伤害增量</param>
public void SetVariableAttribute(string attributeName, int baseOffset, bool additive = false, int originalValue = 0)
{
card.SetVariableAttribute(attributeName, baseOffset, additive, originalValue);
}
/// <summary>
/// 检查卡牌是否具有某属性
/// </summary>
public bool HasAttribute(string attributeName)
{
return card.HasAttribute(attributeName);
}
/// <summary>
/// 获取卡牌的属性值
/// </summary>
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);
}
/// <summary>
/// 设置卡牌的属性值
/// </summary>
public void SetAttribute(string attributeName, int value)
{
card.SetAttribute(attributeName, value);
}
/// <summary>
/// 设置卡牌的属性值
/// </summary>
public void SetAttribute(string attributeName, float value)
{
card.SetAttribute(attributeName, value);
}
/// <summary>
/// 修改卡牌的属性值
/// </summary>
public void ModifyAttribute(string attributeName, int delta)
{
card.ModifyAttribute(attributeName, delta);
}
}
#endregion
#region Command
public partial class CardLogicBase
{
/// <summary>
/// 创建一个命令组,组内命令按顺序执行
/// </summary>
/// <param name="commands">命令模板</param>
protected CommandGroup SingleCommandGroup(params CommandBase[] commands)
{
return SingleCommandGroup(ExecutionMode.Parallel, commands);
}
/// <summary>
/// 创建一个命令组,组内命令按指定顺序执行
/// </summary>
/// <param name="executionMode">执行模式,顺序或并行</param>
/// <param name="commands">命令模板</param>
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;
}
/// <summary>
/// 对目标列表中的每个目标依次执行一组有参函数命令每组命令的参数为targetList中的个体主体Group顺序执行单体Group并行执行。
/// </summary>
/// <param name="targetList">目标列表</param>
/// <param name="singleCommands">单体命令模板</param>
protected CommandGroup TargetListCommandGroup(List<CharacterBase> targetList,
params CommandBase[] singleCommands)
{
return TargetListCommandGroup(targetList, ExecutionMode.Sequential, ExecutionMode.Parallel,
singleCommands);
}
/// <summary>
/// 对目标列表中的每个目标依次执行一组有参函数命令每组命令的参数为targetList中的个体。
/// </summary>
/// <param name="targetList">目标列表</param>
/// <param name="mainExecutionMode">主体Group各个目标的执行顺序</param>
/// <param name="singleExecutionMode">单体Group一个目标中指令的执行顺序</param>
/// <param name="singleCommands">单体命令模板</param>
protected virtual CommandGroup TargetListCommandGroup(
List<CharacterBase> 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<CommandBase> allCommands = new List<CommandBase>();
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
{
/// <summary>
/// 获取最终伤害
/// </summary>
/// <param name="target">目标</param>
/// <param name="elementalTags">元素标签若为null则使用卡牌的元素标签</param>
public virtual int GetTargetedFinalDamage(CharacterBase target, List<string> elementalTags = null)
{
return GetFinalDamage(target, elementalTags, out _, out _, out _, out _);
}
/// <summary>
/// 获取最终伤害
/// </summary>
/// <param name="elementalTags">元素标签若为null则使用卡牌的元素标签</param>
public virtual int GetNoTargetFinalDamage(List<string> elementalTags = null)
{
return GetFinalDamage(null, elementalTags, out _, out _, out _, out _);
}
protected virtual int GetFinalDamage(CharacterBase target, List<string> 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
{
/// <summary>
/// 创建一个角色战斗Buff实例
/// 注意此函数依赖ModManager的类型注册功能请确保在Mod加载时已注册对应Buff类型
/// 此函数中的T并不是原型参数而是获取Mod中注册的类型用的
/// </summary>
public T CreateCharacterBuff<T>(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<T>(buffTypeID, parameters);
}
public T CreateCharacterBuff<T>(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<T>(buffTypeID, parameters);
}
/// <summary>
/// 创建一个卡牌战斗Buff实例
/// 注意此函数依赖ModManager的类型注册功能请确保在Mod加载时已注册对应Buff类型
/// 此函数中的T并不是原型参数而是获取Mod中注册的类型用的
/// </summary>
public T CreateCardBuff<T>(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<T>(buffTypeID, parameters);
}
public T CreateCardBuff<T>(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<T>(buffTypeID, parameters);
}
}
#endregion
public abstract partial class CardLogicBase
{
/// <summary>
/// 获取衍生卡牌数据
/// </summary>
@@ -161,6 +409,24 @@ namespace Continentis.MainGame.Card
Debug.LogError($"Card {cardData.className} does not contain derivative card data '{dataName}'.");
return null;
}
/// <summary>
/// 选中目标时触发的效果效果在所有逻辑组件的Targeting之前执行在SetUp函数生成EventSubmodule的时候
/// 如果必须需要在逻辑组件之后执行请重写Initialize函数。
/// </summary>
public virtual void TargetingEffect(CharacterBase target)
{
}
/// <summary>
/// 取消选中目标时触发的效果效果在所有逻辑组件的Untargeting之前执行在SetUp函数生成EventSubmodule的时候
/// 如果必须需要在逻辑组件之后执行请重写Initialize函数。
/// </summary>
public virtual void UntargetingEffect()
{
}
}
/// <summary>
@@ -171,15 +437,16 @@ namespace Continentis.MainGame.Card
/// </summary>
public abstract class CardLogicComponentBase
{
protected CardLogicBase card;
protected CardLogicBase mainLogic;
protected CardInstance card => mainLogic.card;
protected CharacterBase user => card.user;
protected CombatTeam team => card.UsingTeam;
protected CombatTeam usingTeam => card.usingTeam;
public virtual void Initialize(CardLogicBase card)
{
this.card = card;
card.eventSubmodule.onTargeting += TargetingEffect;
card.eventSubmodule.onUntargeting += UntargetingEffect;
this.mainLogic = card;
this.card.eventSubmodule.onTargeting += TargetingEffect;
this.card.eventSubmodule.onUntargeting += UntargetingEffect;
}
protected virtual void TargetingEffect(CharacterBase target)