Files
Continentis/Assets/Scripts/MainGame/Card/CardLogicBase.cs
SoulliesOfficial d09b58fd80 架构大更
2026-03-20 11:56:50 -04:00

383 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Character;
using Continentis.MainGame.Equipment;
using SLSFramework.General;
using SLSFramework.UModAssistance;
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<CardLogicComponentBase> logicComponents { get; private set; }
/// <summary>生成卡牌逻辑实例。</summary>
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<CardLogicComponentBase>();
card.eventSubmodule.onTargeting += TargetingEffect;
card.eventSubmodule.onUntargeting += UntargetingEffect;
}
public virtual void SetUpLogicComponents() { }
public T AddLogicComponent<T>() 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;
}
T component = new T();
component.Initialize(this);
logicComponents.Add(component);
return component;
}
public T LogicComponent<T>() where T : CardLogicComponentBase
{
return logicComponents.OfType<T>().FirstOrDefault();
}
public virtual void ApplyAttributeChangesByCard() { }
/// <summary>卡牌出牌效果,返回一个 CommandGroup 供队列执行。</summary>
public virtual CommandGroup PlayEffect(List<CharacterBase> targetList)
{
return new CommandGroup();
}
public virtual void AfterPlayEffect(List<CharacterBase> targetList) { }
}
#region Attributes
public partial class CardLogicBase
{
/// <summary>设置可变属性值。</summary>
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) => card.HasAttribute(attributeName);
/// <summary>获取卡牌的属性值。</summary>
public int GetAttribute(string attributeName, int defaultValue = 0) => card.GetAttribute(attributeName, defaultValue);
public float GetRawAttribute(string attributeName, float defaultValue = 0) => card.GetRawAttribute(attributeName, defaultValue);
/// <summary>设置卡牌的属性值int。</summary>
public void SetAttribute(string attributeName, int value) => card.SetAttribute(attributeName, value);
/// <summary>设置卡牌的属性值float。</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
{
// ── 新 API闭包工厂推荐Mod 制作者优先使用) ─────────────────────
/// <summary>
/// 对 targetList 中的每个目标调用工厂 lambda生成的命令按 mainExecutionMode 组合。
/// </summary>
protected CommandGroup ForEachTarget(
List<CharacterBase> targetList,
Func<CharacterBase, CommandBase> factory,
ExecutionMode mainExecutionMode = ExecutionMode.Sequential)
{
var group = new CommandGroup(mainExecutionMode);
foreach (CharacterBase target in targetList)
{
CharacterBase captured = target;
group.AddCommand(factory(captured));
}
return group;
}
/// <summary>
/// 对 targetList 中的每个目标调用工厂 lambda生成的 CommandGroup 按 mainExecutionMode 组合。
/// </summary>
protected CommandGroup ForEachTarget(
List<CharacterBase> targetList,
Func<CharacterBase, CommandGroup> factory,
ExecutionMode mainExecutionMode = ExecutionMode.Sequential)
{
var group = new CommandGroup(mainExecutionMode);
foreach (CharacterBase target in targetList)
{
CharacterBase captured = target;
group.AddCommand(factory(captured));
}
return group;
}
// ── 旧 API模板 Clone 模式(保留供向后兼容,后续迁移完成后移除) ──
/// <summary>
/// 克隆命令模板列表,组合为单个并行 CommandGroup。
/// </summary>
protected CommandGroup SingleCommandGroup(params CommandBase[] commands)
{
return SingleCommandGroup(ExecutionMode.Parallel, commands);
}
/// <summary>
/// 克隆命令模板列表,按 executionMode 组合为单个 CommandGroup。
/// </summary>
protected virtual CommandGroup SingleCommandGroup(
ExecutionMode executionMode = ExecutionMode.Parallel, params CommandBase[] commands)
{
var group = new CommandGroup(executionMode);
foreach (CommandBase template in commands)
group.AddCommand(template.Clone());
return group;
}
/// <summary>
/// 对目标列表中的每个目标克隆命令模板并注入 Target生成嵌套 CommandGroup。
/// 新代码请改用 <see cref="ForEachTarget"/> 闭包工厂模式。
/// </summary>
[Obsolete("请改用 ForEachTarget(targetList, target => ...) 闭包工厂模式。")]
protected CommandGroup TargetListCommandGroup(
List<CharacterBase> targetList,
params CommandBase[] singleCommands)
{
return TemplateTargetGroup(targetList, ExecutionMode.Sequential, ExecutionMode.Parallel, singleCommands);
}
/// <summary>
/// 对目标列表中的每个目标克隆命令模板并注入 Target生成嵌套 CommandGroup。
/// 新代码请改用 <see cref="ForEachTarget"/> 闭包工厂模式。
/// </summary>
[Obsolete("请改用 ForEachTarget(targetList, target => ...) 闭包工厂模式。")]
protected virtual CommandGroup TargetListCommandGroup(
List<CharacterBase> targetList,
ExecutionMode mainExecutionMode = ExecutionMode.Sequential,
ExecutionMode singleExecutionMode = ExecutionMode.Parallel,
params CommandBase[] singleCommands)
{
return TemplateTargetGroup(targetList, mainExecutionMode, singleExecutionMode, singleCommands);
}
/// <summary>
/// 模板 Clone 模式的底层实现,供旧代码向后兼容。
/// 克隆每条模板命令并向所有子命令的 selfContext 注入 Target。
/// </summary>
protected virtual CommandGroup TemplateTargetGroup(
List<CharacterBase> targetList,
ExecutionMode mainExecutionMode = ExecutionMode.Sequential,
ExecutionMode singleExecutionMode = ExecutionMode.Parallel,
params CommandBase[] singleCommands)
{
var mainGroup = new CommandGroup(mainExecutionMode);
foreach (CharacterBase target in targetList)
{
var singleGroup = new CommandGroup(singleExecutionMode);
foreach (CommandBase template in singleCommands)
{
CommandBase clone = template.Clone();
// 收集所有子命令(含嵌套组内的命令)并注入 Target
var allCommands = clone is CommandGroup group
? group.GetAllCommands(true)
: new List<CommandBase> { clone };
foreach (CommandBase cmd in allCommands)
cmd.selfContext.Set(CommandContextKeys.Target, target);
singleGroup.AddCommand(clone);
}
mainGroup.AddCommand(singleGroup);
}
return mainGroup;
}
}
#endregion
#region Attack
public partial class CardLogicBase
{
/// <summary>获取对指定目标的最终伤害值。</summary>
public virtual int GetTargetedFinalDamage(CharacterBase target, List<string> elementalTags = null)
{
return GetFinalDamage(target, elementalTags, out _, out _, out _, out _);
}
/// <summary>获取无目标时的最终伤害值。</summary>
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");
elementalMultiplier = 1f;
foreach (string element in elementalTags)
{
float targetGain = haveTarget ? target.GetRawAttribute(element + "DamageGainMultiplier", 1f) : 1f;
elementalMultiplier *= user.GetRawAttribute(element + "DamageDealtMultiplier", 1f) * targetGain;
}
magicMultiplier = 1f;
if (card.HasKeyword("Magic") || card.HasKeyword("Arcane") || card.HasKeyword("Sorcery"))
{
float targetGain = haveTarget ? target.GetRawAttribute("MagicDamageGainMultiplier", 1f) : 1f;
magicMultiplier = user.GetRawAttribute("MagicDamageDealtMultiplier", 1f) * targetGain;
}
float targetFinalGain = haveTarget ? target.GetRawAttribute("FinalDamageGainMultiplier", 1f) : 1f;
finalMultiplier = user.GetRawAttribute("FinalDamageDealtMultiplier", 1f) * 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 类型注册)。</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 类型注册)。</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>
public CardData GetDerivativeCardData(int index)
{
return ModManager.GetData<CardData>(cardData.derivativeCardDataRefs[index]);
}
/// <summary>获取衍生卡牌数据(按名称)。</summary>
public CardData GetDerivativeCardData(string dataName)
{
if (cardData.derivativeCardDataRefs.Contains(dataName))
return ModManager.GetData<CardData>(dataName);
Debug.LogError($"Card {cardData.className} does not contain derivative card data '{dataName}'.");
return null;
}
/// <summary>选中目标时触发的效果(在逻辑组件的 Targeting 之前执行)。</summary>
public virtual void TargetingEffect(CharacterBase target) { }
/// <summary>取消选中目标时触发的效果(在逻辑组件的 Untargeting 之前执行)。</summary>
public virtual void UntargetingEffect() { }
}
/// <summary>卡牌逻辑组件基类。</summary>
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() { }
}
}