MOD!
This commit is contained in:
@@ -1,14 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Continentis.MainGame.Character;
|
||||
using Continentis.MainGame.Commands;
|
||||
using NUnit.Framework;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
#region Fundamental
|
||||
public partial class CardLogicBase
|
||||
{
|
||||
public bool HasTag(string tag)
|
||||
{
|
||||
return functionalTags.Contains(tag) || elementalTags.Contains(tag);
|
||||
return tags.Contains(tag);
|
||||
}
|
||||
|
||||
public List<string> GetElementTags(List<string> tags = null)
|
||||
{
|
||||
tags ??= this.tags;
|
||||
return tags.Filtered((tag) => MainGameManager.Instance.elementTags.Contains(tag));
|
||||
}
|
||||
|
||||
public bool HasKeyword(string keyword)
|
||||
@@ -16,7 +28,94 @@ namespace Continentis.MainGame.Card
|
||||
return contentSubmodule.keywords.Contains(keyword);
|
||||
}
|
||||
}
|
||||
#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();
|
||||
|
||||
if (clone is CommandGroup group)
|
||||
{
|
||||
foreach (CommandBase cmd in group.commands)
|
||||
{
|
||||
cmd.selfContext.context["Target"] = target;
|
||||
}
|
||||
}
|
||||
|
||||
clone.selfContext.context["Target"] = target;
|
||||
singleGroup.AddCommand(clone);
|
||||
}
|
||||
|
||||
mainGroup.AddCommand(singleGroup);
|
||||
}
|
||||
|
||||
return mainGroup;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Attack
|
||||
public partial class CardLogicBase
|
||||
{
|
||||
/// <summary>
|
||||
@@ -24,25 +123,60 @@ namespace Continentis.MainGame.Card
|
||||
/// </summary>
|
||||
/// <param name="target">目标</param>
|
||||
/// <param name="elementalTags">元素标签,若为null则使用卡牌的元素标签</param>
|
||||
protected virtual int GetFinalDamage(CharacterBase target, List<string> elementalTags = null)
|
||||
public virtual int GetFinalDamage(CharacterBase target, List<string> elementalTags = null)
|
||||
{
|
||||
elementalTags ??= this.elementalTags;
|
||||
|
||||
float elementalAmplifier = 1;
|
||||
foreach (var element in elementalTags) //计算元素伤害加成
|
||||
return GetFinalDamage(target, 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)
|
||||
{
|
||||
elementalTags ??= GetElementTags();
|
||||
|
||||
//----计算基础伤害增量----
|
||||
int physicsOffset = 0;
|
||||
if (HasTag("Physics") || HasKeyword("Slash") || HasKeyword("Prick") || HasKeyword("Strike"))
|
||||
{
|
||||
elementalAmplifier *= user.attributeSubmodule.GetCurrentGeneralAttribute(element + "DamageDealtMultiplier", 1);
|
||||
elementalAmplifier *= target.attributeSubmodule.GetCurrentGeneralAttribute(element + "DamageGainMultiplier", 1);
|
||||
physicsOffset = user.GetAttribute("PhysicsDamageDealtOffset"); //物理伤害基础增量
|
||||
}
|
||||
|
||||
int magicOffset = 0;
|
||||
if (HasTag("Magic") || HasKeyword("Arcane") || HasKeyword("Sorcery"))
|
||||
{
|
||||
magicOffset = user.GetAttribute("MagicDamageDealtOffset"); //魔法伤害基础增量
|
||||
}
|
||||
|
||||
//----计算伤害因数----
|
||||
|
||||
float finalDamage =
|
||||
attributeSubmodule.GetRoundCurrentAttribute("Damage") * elementalAmplifier *
|
||||
attributeSubmodule.GetCurrentAttribute("FinalDamageDealtMultiplier", 1) *
|
||||
target.attributeSubmodule.GetCurrentGeneralAttribute("FinalDamageGainMultiplier", 1);
|
||||
//计算元素伤害加成,注意,“物理Physics”也是一种元素,因此下方没有“通用物理伤害加成”的计算
|
||||
elementalMultiplier = 1;
|
||||
foreach (string element in elementalTags)
|
||||
{
|
||||
elementalMultiplier *= user.GetRawAttribute(element + "DamageDealtMultiplier", 1) *
|
||||
target.GetRawAttribute(element + "DamageGainMultiplier", 1);
|
||||
}
|
||||
|
||||
//计算通用的魔法伤害加成
|
||||
magicMultiplier = 1;
|
||||
if (HasTag("Magic") || HasKeyword("Arcane") || HasKeyword("Sorcery"))
|
||||
{
|
||||
magicMultiplier = user.GetRawAttribute("MagicDamageDealtMultiplier", 1) *
|
||||
target.GetRawAttribute("MagicDamageGainMultiplier", 1);
|
||||
}
|
||||
|
||||
//计算最终伤害加成
|
||||
finalMultiplier = user.GetRawAttribute("FinalDamageDealtMultiplier", 1) *
|
||||
target.GetRawAttribute("FinalDamageGainMultiplier", 1);
|
||||
|
||||
//----计算最终伤害----
|
||||
baseDamageAfterOffset = attributeSubmodule.GetCurrentAttribute("Damage") + physicsOffset + magicOffset;
|
||||
|
||||
float finalDamage = baseDamageAfterOffset * elementalMultiplier * magicMultiplier * finalMultiplier;
|
||||
|
||||
return Mathf.RoundToInt(finalDamage);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Attributes
|
||||
public partial class CardLogicBase
|
||||
@@ -54,7 +188,7 @@ namespace Continentis.MainGame.Card
|
||||
/// <param name="additive">是否为叠加,true为叠加,false为覆盖,true时,originalValue为外部传入值</param>
|
||||
/// <param name="originalValue">原始伤害值,仅在additive为true时有效,否则此值被覆盖为BaseAttribute</param>
|
||||
/// <param name="baseOffset">伤害增量</param>
|
||||
public void SetVariableAttribute(string attributeName, bool additive, int originalValue, int baseOffset)
|
||||
public void SetVariableAttribute(string attributeName, int baseOffset, bool additive = false, int originalValue = 0)
|
||||
{
|
||||
string baseName = "Base" + attributeName;
|
||||
string baseOffsetName = baseName + "Offset";
|
||||
@@ -66,12 +200,25 @@ namespace Continentis.MainGame.Card
|
||||
ModifyAttribute(attributeName, baseOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查卡牌是否具有某属性
|
||||
/// </summary>
|
||||
public bool HasAttribute(string attributeName)
|
||||
{
|
||||
return attributeSubmodule.attributeGroup.current.ContainsKey(attributeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取卡牌的属性值
|
||||
/// </summary>
|
||||
public int GetAttribute(string attributeName)
|
||||
public int GetAttribute(string attributeName, int defaultValue = 0)
|
||||
{
|
||||
return attributeSubmodule.GetRoundCurrentAttribute(attributeName);
|
||||
return attributeSubmodule.GetRoundCurrentAttribute(attributeName, defaultValue);
|
||||
}
|
||||
|
||||
public float GetRawAttribute(string attributeName, float defaultValue = 0)
|
||||
{
|
||||
return attributeSubmodule.GetCurrentAttribute(attributeName, defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,6 +229,14 @@ namespace Continentis.MainGame.Card
|
||||
attributeSubmodule.attributeGroup.current[attributeName] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置卡牌的属性值
|
||||
/// </summary>
|
||||
public void SetAttribute(string attributeName, float value)
|
||||
{
|
||||
attributeSubmodule.attributeGroup.current[attributeName] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改卡牌的属性值
|
||||
/// </summary>
|
||||
@@ -91,43 +246,11 @@ namespace Continentis.MainGame.Card
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region CombatResoures
|
||||
public partial class CardLogicBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 检查是否有足够的体力
|
||||
/// </summary>
|
||||
protected virtual bool CheckEnoughStamina()
|
||||
{
|
||||
int staminaCost = attributeSubmodule.GetRoundCurrentAttribute("StaminaCost");
|
||||
return user.attributeSubmodule.generalAttributeGroup.current["Stamina"] >= staminaCost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消耗体力
|
||||
/// </summary>
|
||||
protected virtual void ConsumeStamina()
|
||||
{
|
||||
int staminaCost = attributeSubmodule.GetRoundCurrentAttribute("StaminaCost");
|
||||
user.attributeSubmodule.generalAttributeGroup.current["Stamina"] -= staminaCost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否有足够的魔法
|
||||
/// </summary>
|
||||
protected virtual bool CheckEnoughMana()
|
||||
{
|
||||
int manaCost = attributeSubmodule.GetRoundCurrentAttribute("ManaCost");
|
||||
return user.attributeSubmodule.generalAttributeGroup.current["Mana"] >= manaCost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消耗魔法
|
||||
/// </summary>
|
||||
protected virtual void ConsumeMana()
|
||||
{
|
||||
int manaCost = attributeSubmodule.GetRoundCurrentAttribute("ManaCost");
|
||||
user.attributeSubmodule.generalAttributeGroup.current["Mana"] -= manaCost;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -65,7 +65,7 @@ namespace Continentis.MainGame.Card
|
||||
{
|
||||
protected bool FindExistingBuff<T>(out T existingBuff) where T : CardBuffBase
|
||||
{
|
||||
return base.FindExistingBuff(out existingBuff, attachedCard.combatBuffSubmodule.buffList);
|
||||
return base.FindExistingSameBuff(out existingBuff, attachedCard.combatBuffSubmodule.buffList);
|
||||
}
|
||||
|
||||
public override void Apply(CardLogicBase attachedCard, CharacterBase sourceCharacter = null)
|
||||
@@ -1,20 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Continentis/MainGame/Card/AttributesDefaultCollection", fileName = "CardAttributesDefaultCollection")]
|
||||
public class CardAttributesDefaultCollection : SerializedScriptableObject
|
||||
public class CardAttributesDefaultCollection : ScriptableObject
|
||||
{
|
||||
[Title("Attributes")]
|
||||
[Searchable]
|
||||
[DictionaryDrawerSettings(KeyColumnWidth = 400)]
|
||||
public Dictionary<string, float> originalAttributes;
|
||||
[Header("Attributes")]
|
||||
public SerializableDictionary<string, float> variableAttributes;
|
||||
|
||||
public SerializableDictionary<string, float> originalAttributes;
|
||||
|
||||
[Searchable]
|
||||
[DictionaryDrawerSettings(KeyColumnWidth = 400)]
|
||||
[Tooltip("初始化时赋予给CurrentAttributes的属性:第一栏是属性名,第二栏是初始化时使用对应名称的CurrentAttributes的数据(或一个常数),留空则默认为0")]
|
||||
public Dictionary<string, string> endowingCurrentAttributes;
|
||||
public SerializableDictionary<string, string> endowingCurrentAttributes;
|
||||
}
|
||||
}
|
||||
@@ -3,25 +3,18 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Continentis.MainGame.Character;
|
||||
using Sirenix.OdinInspector;
|
||||
using SoulliesFramework.General;
|
||||
using UnityEditor;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
using NaughtyAttributes;
|
||||
using SLSFramework.UModAssistance;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public enum CardRarity
|
||||
{
|
||||
None = 0,
|
||||
Common = 10,
|
||||
Uncommon = 20,
|
||||
Rare = 30,
|
||||
Epic = 40,
|
||||
Legendary = 50,
|
||||
Divine = 60
|
||||
}
|
||||
|
||||
public enum CardType
|
||||
{
|
||||
Attack = 0,
|
||||
@@ -33,45 +26,64 @@ namespace Continentis.MainGame.Card
|
||||
}
|
||||
|
||||
[CreateAssetMenu(menuName = "Continentis/MainGame/Card/CardData", fileName = "CardData")]
|
||||
public partial class CardData : SerializedScriptableObject
|
||||
public partial class CardData : ScriptableObject
|
||||
{
|
||||
[Title("Fundamental")]
|
||||
[ValueDropdown("GetLogicTypes", IsUniqueList = true)]
|
||||
[OnValueChanged("SetCardIdentifier")]
|
||||
public Type cardClass;
|
||||
public string cardName;
|
||||
public string cardIdentifier;
|
||||
public CardRarity cardRarity;
|
||||
[FormerlySerializedAs("cardLogicClassName")] [Header("Fundamental")]
|
||||
public string classFullName;
|
||||
public string displayName;
|
||||
public Rarity cardRarity;
|
||||
public CardType cardType;
|
||||
public List<string> tags;
|
||||
|
||||
public Sprite cardSprite;
|
||||
[TextArea(1, 10)]
|
||||
|
||||
public string functionText;
|
||||
public string cardDescription;
|
||||
[FormerlySerializedAs("tags")] public List<string> functionalTags;
|
||||
public List<string> elementalTags;
|
||||
|
||||
[Header("Intention")]
|
||||
public float baseWeight = 1f;
|
||||
|
||||
[Searchable]
|
||||
[Title("References")]
|
||||
public Dictionary<string, GameObject> prefabs = new Dictionary<string, GameObject>();
|
||||
[Searchable]
|
||||
public List<CardData> derivativeCardDataList = new List<CardData>();
|
||||
|
||||
[Title("Attributes")]
|
||||
[Searchable]
|
||||
[Header("Attributes")]
|
||||
[Tooltip("可变属性,这个属性会自动设置BaseAttr进入Original,设置Attr,BaseAttrOffset=0,以及DisplayAttr进入Current")]
|
||||
public Dictionary<string, float> variableAttributes = new Dictionary<string, float>();
|
||||
|
||||
[Searchable]
|
||||
[Tooltip("基础属性,不会改变,通常情况下不会直接使用")]
|
||||
public Dictionary<string, float> originalAttributes = new Dictionary<string, float>();
|
||||
public SerializableDictionary<string, float> variableAttributes = new SerializableDictionary<string, float>();
|
||||
|
||||
[Searchable]
|
||||
[Tooltip("初始化时赋予给CurrentAttributes的属性,第一栏是属性名,第二栏是初始化时使用对应名称的OriginalAttributes的,留空则默认为0,如果是float数字则直接使用该数字")]
|
||||
public Dictionary<string, string> endowingCurrentAttributes = new Dictionary<string, string>();
|
||||
[Tooltip("基础属性,不会改变,通常情况下不会直接使用")]
|
||||
public SerializableDictionary<string, float> originalAttributes = new SerializableDictionary<string, float>();
|
||||
|
||||
[FormerlySerializedAs("endowingCurrentAttributes")] [Tooltip("初始化时赋予给CurrentAttributes的属性,第一栏是属性名,第二栏是初始化时使用对应名称的OriginalAttributes的,留空则默认为0,如果是float数字则直接使用该数字")]
|
||||
public SerializableDictionary<string, string> runtimeCurrentAttributes = new SerializableDictionary<string, string>();
|
||||
|
||||
[Title("Upgrade")]
|
||||
[InlineButton("CreateUpgradeNode", "Create")]
|
||||
[Header("Upgrade")]
|
||||
public CardUpgradeNode upgradeNode;
|
||||
|
||||
[Header("References")]
|
||||
public List<string> prefabRefs = new List<string>();
|
||||
public List<string> derivativeCardDataRefs = new List<string>();
|
||||
public List<string> derivativeCharacterDataRefs = new List<string>();
|
||||
}
|
||||
|
||||
public partial class CardData
|
||||
{
|
||||
public string ModName => classFullName.Split('_').First();
|
||||
public string ClassName => classFullName.Split('_').Last();
|
||||
|
||||
public bool HasTag(string tag)
|
||||
{
|
||||
return tags.Contains(tag);
|
||||
}
|
||||
|
||||
public bool HasKeyword(string keyword)
|
||||
{
|
||||
return functionText.Contains($"$Keyword(\"{keyword}\")");
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CardData
|
||||
{
|
||||
public static CardData Get(string dataID)
|
||||
{
|
||||
return ModManager.GetData<CardData>(dataID);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CardData
|
||||
@@ -82,41 +94,69 @@ namespace Continentis.MainGame.Card
|
||||
/// <param name="owner">卡牌持有者</param>
|
||||
/// <param name="pileName">初始卡堆名称,默认为"Draw"</param>
|
||||
/// <param name="index">插入位置,默认为0</param>
|
||||
public CardInstance GenerateCardInstance(ICardOwner owner, string pileName = "Draw", int index = 0)
|
||||
public CardInstance GenerateCardInstance(ICardOwner owner, string pileName = "Draw", int index = -1)
|
||||
{
|
||||
CardInstance cardInstance = new CardInstance(GenerateCardLogic(), owner, pileName, index);
|
||||
cardInstance.cardLogic.InitialRefresh();
|
||||
cardInstance.cardLogic.Initialize();
|
||||
return cardInstance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成卡牌逻辑实例
|
||||
/// </summary>
|
||||
public CardLogicBase GenerateCardLogic()
|
||||
{
|
||||
if (Activator.CreateInstance(cardClass) is CardLogicBase cardLogic)
|
||||
Type cardLogicType = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(assembly => assembly.GetTypes())
|
||||
.FirstOrDefault(t => typeof(CardLogicBase).IsAssignableFrom(t) && t.Name == this.classFullName);//TODO: 后续优化为共用字典
|
||||
|
||||
if(cardLogicType == null)
|
||||
{
|
||||
Debug.LogError($"Card class '{classFullName}' not found in assemblies.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Activator.CreateInstance(cardLogicType) is CardLogicBase cardLogic)
|
||||
{
|
||||
cardLogic.cardData = this;
|
||||
cardLogic.Setup();
|
||||
return cardLogic;
|
||||
}
|
||||
|
||||
Debug.LogError($"Card class '{cardClass}' not found or could not be instantiated.");
|
||||
Debug.LogError($"Card class '{classFullName}' not found or could not be instantiated.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过名称获取衍生卡牌数据
|
||||
/// 通过索引获取衍生卡牌数据
|
||||
/// </summary>
|
||||
public CardData GetDerivativeCardData(string cardName)
|
||||
public CardData GetDerivativeCardData(int index)
|
||||
{
|
||||
return derivativeCardDataList.FirstOrDefault(card => card.cardName == cardName);
|
||||
return ModManager.GetData<CardData>(derivativeCardDataRefs[index]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过索引获取衍生角色数据
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public CharacterData GetDerivativeCharacterData(int index)
|
||||
{
|
||||
return ModManager.GetData<CharacterData>(derivativeCharacterDataRefs[index]);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public partial class CardData
|
||||
{
|
||||
{/*
|
||||
private void SetCardIdentifier()
|
||||
{
|
||||
cardIdentifier = cardClass.Name;
|
||||
|
||||
int underscoreIndex = cardClass.Name.IndexOf('_');
|
||||
string cardNamePart = cardClass.Name.Substring(underscoreIndex + 1);
|
||||
string result = Regex.Replace(cardNamePart, "([a-z])([A-Z])", "$1 $2");
|
||||
cardName = result;
|
||||
}
|
||||
|
||||
private void CreateUpgradeNode()
|
||||
@@ -155,7 +195,7 @@ namespace Continentis.MainGame.Card
|
||||
yield return new ValueDropdownItem<Type>(path, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[FoldoutGroup("Functions")]
|
||||
[Button("从所有的DefaultCollection中粘贴默认属性")]
|
||||
public void PasteDefaultAttributes()
|
||||
@@ -179,10 +219,10 @@ namespace Continentis.MainGame.Card
|
||||
Type assetType = collection.GetType();
|
||||
string assetNamespace = assetType.Namespace;
|
||||
|
||||
// 检查目录是否有效,是否在"Assets/CoreMods/"下,并且是否以"/Characters/DefaultCollections"结尾
|
||||
// 检查目录是否有效,是否在"Assets/Mods/"下,并且是否以"/Characters/DefaultCollections"结尾
|
||||
if (!string.IsNullOrEmpty(directory) &&
|
||||
assetNamespace == typeof(CardAttributesDefaultCollection).Namespace &&
|
||||
directory.StartsWith("Assets/CoreMods/") &&
|
||||
directory.StartsWith("Assets/Mods/") &&
|
||||
directory.EndsWith("/Cards/DefaultCollections"))
|
||||
{
|
||||
// 3. 如果路径和命名空间都符合要求,则将该资产添加到目标列表中
|
||||
@@ -191,45 +231,23 @@ namespace Continentis.MainGame.Card
|
||||
Debug.Log($"Loaded DefaultStringList from: {path}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Dictionary<string, float> variableAttributes = new Dictionary<string, float>();
|
||||
Dictionary<string, float> originalAttributes = new Dictionary<string, float>();
|
||||
Dictionary<string, string> endowingCurrentAttributes = new Dictionary<string, string>();
|
||||
|
||||
foreach (CardAttributesDefaultCollection collection in targetCollections)
|
||||
{
|
||||
collection.variableAttributes.PasteDictionary(variableAttributes);
|
||||
collection.originalAttributes.PasteDictionary(originalAttributes);
|
||||
collection.endowingCurrentAttributes.PasteDictionary(endowingCurrentAttributes);
|
||||
}
|
||||
|
||||
variableAttributes.PasteDictionary(this.variableAttributes);
|
||||
originalAttributes.PasteDictionary(this.originalAttributes);
|
||||
endowingCurrentAttributes.PasteDictionary(this.endowingCurrentAttributes);
|
||||
Debug.Log($"Pasted default attributes to file {this.name}");
|
||||
}
|
||||
|
||||
[FoldoutGroup("Functions")]
|
||||
[Button("自动设置需要加权(名称中带有Base)的属性")]
|
||||
private void AutoSetUpWeightedAttributes()
|
||||
{
|
||||
List<string> baseAttributeNames = originalAttributes
|
||||
.Where(attr => attr.Key.Contains("Base"))
|
||||
.Select(attr => attr.Key).ToList();
|
||||
|
||||
List<string> attributeNames = baseAttributeNames
|
||||
.Select(attr => attr.Replace("Base", "")).ToList();
|
||||
|
||||
List<string> baseOffsetAttributeName = baseAttributeNames
|
||||
.Select(attr => attr + "Offset").ToList();
|
||||
|
||||
List<string> displayAttributeNames = baseAttributeNames
|
||||
.Select(attr => attr.Replace("Base", "Display")).ToList();
|
||||
|
||||
for (int index = 0; index < baseAttributeNames.Count; index++)
|
||||
{
|
||||
endowingCurrentAttributes.TryAdd(attributeNames[index], baseAttributeNames[index]);
|
||||
endowingCurrentAttributes.TryAdd(baseOffsetAttributeName[index], "0");
|
||||
endowingCurrentAttributes.TryAdd(displayAttributeNames[index], baseAttributeNames[index]);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using SoulliesFramework.General;
|
||||
using NaughtyAttributes;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
[Serializable]
|
||||
public class CardUpgradeNode
|
||||
{
|
||||
[ReadOnly]
|
||||
@@ -76,7 +78,7 @@ namespace Continentis.MainGame.Card
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[CardUpgradeNode] Attempted to get upgrade attributes for a non-terminal node card {sourceCard.cardName}.");
|
||||
Debug.LogError($"[CardUpgradeNode] Attempted to get upgrade attributes for a non-terminal node card {sourceCard.classFullName}.");
|
||||
}
|
||||
|
||||
return upgradeAttributes;
|
||||
|
||||
@@ -2,36 +2,64 @@ using System.Collections.Generic;
|
||||
using Continentis.MainGame.Character;
|
||||
using Continentis.MainGame.UI;
|
||||
using Lean.Pool;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public partial class CardInstance
|
||||
{
|
||||
[Title("References")]
|
||||
[Header("References")]
|
||||
public DeckSubmodule deck;
|
||||
public string currentPileName;
|
||||
//public string currentPileName;
|
||||
public ICardOwner owner;
|
||||
public CombatTeam team;
|
||||
public CharacterBase user;
|
||||
public CombatTeam team;
|
||||
public CardLogicBase cardLogic;
|
||||
public CardLocation cardLocation;
|
||||
public HandCardView handCardView;
|
||||
public IntentionCardView intentionCardView;
|
||||
|
||||
public CardInstance (CardLogicBase cardLogic, ICardOwner owner, string initialPileName, int index)
|
||||
public CardInstance(CardLogicBase cardLogic, ICardOwner owner, string initialPileName, int index = -1)
|
||||
{
|
||||
cardLogic.cardInstance = this;
|
||||
this.cardLogic = cardLogic;
|
||||
|
||||
|
||||
this.owner = owner;
|
||||
this.team = owner as CombatTeam;
|
||||
this.user = owner as CharacterBase;
|
||||
if (this.owner is CombatTeam team)
|
||||
{
|
||||
this.team = team;
|
||||
}
|
||||
else if (this.owner is CharacterBase character)
|
||||
{
|
||||
this.team = character.team;
|
||||
}
|
||||
|
||||
this.deck = owner.deckSubmodule;
|
||||
this.currentPileName = initialPileName;
|
||||
this.deck.Pile(currentPileName).Insert(index, this);
|
||||
this.cardLocation = new CardLocation(initialPileName, index);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
this.deck.Pile(cardLocation.pileName).Add(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.deck.Pile(cardLocation.pileName).Insert(index, this);
|
||||
}
|
||||
}
|
||||
|
||||
public HandCardView GenerateHandCardView(string pileName, int index = -1)
|
||||
{
|
||||
PileBase pile = CombatUIManager.Instance.combatMainPage.Pile(pileName);
|
||||
return GenerateHandCardView(pile, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成手牌
|
||||
/// </summary>
|
||||
/// <param name="pile">手牌生成位置</param>
|
||||
/// <param name="index">插入位置,-1为末尾</param>
|
||||
/// <returns></returns>
|
||||
public HandCardView GenerateHandCardView(PileBase pile, int index = -1)
|
||||
{
|
||||
GameObject handCardObjectPrefab = MainGameManager.Instance.basePrefabs.handCardObject;
|
||||
@@ -90,4 +118,16 @@ namespace Continentis.MainGame.Card
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CardLocation
|
||||
{
|
||||
public string pileName;
|
||||
public int index;
|
||||
|
||||
public CardLocation(string pileName, int index)
|
||||
{
|
||||
this.pileName = pileName;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,99 +2,145 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Continentis.MainGame.Character;
|
||||
using Continentis.MainGame.UI;
|
||||
using Lean.Pool;
|
||||
using Sirenix.OdinInspector;
|
||||
using Unity.VisualScripting;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public abstract partial class CardLogicBase
|
||||
{
|
||||
[Title("Reference")]
|
||||
public CardData cardData;
|
||||
[Header("Reference")] public CardData cardData;
|
||||
public CardInstance cardInstance;
|
||||
|
||||
public ICardOwner owner => cardInstance.owner;
|
||||
public CombatTeam team => cardInstance.team;
|
||||
public CharacterBase user => cardInstance.user;
|
||||
public CombatTeam team => cardInstance.team;
|
||||
public HandCardView handCardView => cardInstance.handCardView;
|
||||
|
||||
|
||||
[Title("Card Base Info")]
|
||||
public IntentionCardView intentionCardView => cardInstance.intentionCardView;
|
||||
|
||||
[Header("Card Base Info")]
|
||||
public Guid cardID;
|
||||
public List<string> functionalTags;
|
||||
public List<string> elementalTags;
|
||||
public List<string> tags;
|
||||
public int upgradeLevel;
|
||||
|
||||
[Title("Submodules")]
|
||||
[ShowInInspector]
|
||||
|
||||
[Header("Submodules")]
|
||||
public AttributeSubmodule attributeSubmodule { get; private set; }
|
||||
|
||||
[ShowInInspector]
|
||||
public WeightSubmodule weightSubmodule { get; private set; }
|
||||
|
||||
[ShowInInspector]
|
||||
public CombatBuffSubmodule combatBuffSubmodule { get; private set; }
|
||||
|
||||
[ShowInInspector]
|
||||
public EventSubmodule eventSubmodule { get; private set; }
|
||||
|
||||
[ShowInInspector]
|
||||
public EventSubmodule eventSubmodule { get; private set; }
|
||||
public ContentSubmodule contentSubmodule { get; private set; }
|
||||
|
||||
[ShowInInspector]
|
||||
public PlaySubmodule playSubmodule { get; private set; }
|
||||
|
||||
public HashSet<CardLogicComponentBase> logicComponents { get; private set; }
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
this.cardID = Guid.NewGuid();
|
||||
|
||||
this.functionalTags = new List<string>(cardData.functionalTags);
|
||||
this.elementalTags = new List<string>(cardData.elementalTags);
|
||||
attributeSubmodule = new AttributeSubmodule(this);
|
||||
weightSubmodule = new WeightSubmodule(this);
|
||||
eventSubmodule = new EventSubmodule(this);
|
||||
combatBuffSubmodule = new CombatBuffSubmodule(this);
|
||||
contentSubmodule = new ContentSubmodule(this);
|
||||
playSubmodule = new PlaySubmodule(this);
|
||||
|
||||
this.tags = new List<string>(cardData.tags);
|
||||
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();
|
||||
}
|
||||
|
||||
public virtual void InitialRefresh()
|
||||
protected virtual void SetUpLogicComponents()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
RefreshCardAttributes();
|
||||
CardDescriptionInterpreter.InterpretDescription(this);
|
||||
CardTextInterpreter.InterpretText(this);
|
||||
|
||||
if (HasKeyword("Instant")) //如果是“瞬发”牌,添加抽牌后立刻打出的事件
|
||||
{
|
||||
eventSubmodule.onDraw.InsertByPriority("Instant", new EventUnit(() =>
|
||||
{
|
||||
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.classFullName} 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<T>() where T : CardLogicComponentBase
|
||||
{
|
||||
return logicComponents.OfType<T>().FirstOrDefault();
|
||||
}
|
||||
|
||||
public void UpgradeCard()
|
||||
{
|
||||
if (team == null)
|
||||
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 = newData.GenerateCardLogic();
|
||||
cardInstance.cardLogic = newLogic;
|
||||
newLogic.cardInstance = cardInstance;
|
||||
cardInstance.cardLogic.InitialRefresh();
|
||||
if(user is PlayerHero)
|
||||
cardInstance.GenerateHandCardView(CombatUIManager.Instance.deckPage.Pile(currentPile.Key), index);
|
||||
cardInstance.cardLogic.Initialize();
|
||||
if (user is PlayerHero)
|
||||
cardInstance.GenerateHandCardView(CombatUIManager.Instance.combatMainPage.Pile(currentPile.Key), index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CardLogicBase
|
||||
|
||||
/// <summary>
|
||||
/// 卡牌逻辑组件基类接口
|
||||
/// 注意,所有的子接口需要实现的函数:
|
||||
/// ComponentTargetingEffect:此牌瞄准目标时调用
|
||||
/// ComponentUntargetingEffect:此牌取消瞄准目标时调用
|
||||
/// </summary>
|
||||
public abstract class CardLogicComponentBase
|
||||
{
|
||||
protected CardLogicBase card;
|
||||
protected CharacterBase user => card.user;
|
||||
protected CombatTeam team => card.team;
|
||||
|
||||
public virtual void Initialize(CardLogicBase card)
|
||||
{
|
||||
this.card = card;
|
||||
card.eventSubmodule.onTargeting += TargetingEffect;
|
||||
card.eventSubmodule.onUntargeting += UntargetingEffect;
|
||||
}
|
||||
|
||||
protected virtual void TargetingEffect(CharacterBase target)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void UntargetingEffect()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,32 +2,32 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Continentis.MainGame.Character;
|
||||
using Continentis.MainGame.Combat;
|
||||
using Continentis.MainGame.Commands;
|
||||
using DamageNumbersPro;
|
||||
using SoulliesFramework.General;
|
||||
using UnityEngine;
|
||||
using SLSFramework.General;
|
||||
using SLSFramework.UModAssistance;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public partial class CardLogicBase
|
||||
{
|
||||
public void Targeting(CharacterBase target)
|
||||
{
|
||||
TargetingEffect(target);
|
||||
}
|
||||
|
||||
public void Untargeting()
|
||||
{
|
||||
UntargetingEffect();
|
||||
}
|
||||
|
||||
protected virtual void TargetingEffect(CharacterBase target)
|
||||
/// <summary>
|
||||
/// 选中目标时触发的效果,效果在所有逻辑组件的Targeting之前执行(在SetUp函数生成EventSubmodule的时候)。
|
||||
/// 如果必须需要在逻辑组件之后执行,请重写Initialize函数。
|
||||
/// </summary>
|
||||
public virtual void TargetingEffect(CharacterBase target)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void UntargetingEffect()
|
||||
/// <summary>
|
||||
/// 取消选中目标时触发的效果,效果在所有逻辑组件的Untargeting之前执行(在SetUp函数生成EventSubmodule的时候)。
|
||||
/// 如果必须需要在逻辑组件之后执行,请重写Initialize函数。
|
||||
/// </summary>
|
||||
public virtual void UntargetingEffect()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -35,14 +35,23 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
public partial class CardLogicBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 刷新卡牌属性
|
||||
/// </summary>
|
||||
public void RefreshCardAttributes()
|
||||
{
|
||||
if(user == null) return;
|
||||
|
||||
attributeSubmodule.RefreshAllAttributes();
|
||||
if(handCardView == null || !handCardView.isSelecting) Untargeting();
|
||||
if ((handCardView == null && intentionCardView == null) || !handCardView.isSelecting)
|
||||
{
|
||||
eventSubmodule.onUntargeting();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据卡牌内容应用属性变化
|
||||
/// </summary>
|
||||
public virtual void ApplyAttributeChangesByCard()
|
||||
{
|
||||
|
||||
@@ -51,28 +60,27 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
public partial class CardLogicBase
|
||||
{
|
||||
public virtual void SetTargets(out List<CharacterBase> valid,
|
||||
out List<CharacterBase> conditionNotMet, out List<CharacterBase> invalid)
|
||||
public virtual void DetectTargetsValidity(out List<CharacterBase> valid, out List<CharacterBase> notMet, out List<CharacterBase> invalid)
|
||||
{
|
||||
List<CharacterBase> characters = CombatMainManager.Instance.characters;
|
||||
List<CharacterBase> characters = CombatMainManager.Instance.characterController.characters;
|
||||
invalid = new List<CharacterBase>();
|
||||
conditionNotMet = new List<CharacterBase>();
|
||||
notMet = new List<CharacterBase>();
|
||||
valid = new List<CharacterBase>();
|
||||
|
||||
int targetCount = attributeSubmodule.targetCount;
|
||||
|
||||
if (targetCount <= -2)
|
||||
{
|
||||
throw new Exception("Target count is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetCount == 0 || cardData.functionalTags.Contains("TargetSelf")) // 卡牌目标为自身
|
||||
if (targetCount == 0 || cardData.tags.Contains("TargetSelf")) // 卡牌目标为自身
|
||||
{
|
||||
valid.Add(user);
|
||||
invalid.AddRange(characters);
|
||||
invalid.Remove(user);
|
||||
}
|
||||
else if (cardData.functionalTags.Contains("TargetAllies")) // 卡牌目标为友方单位
|
||||
else if (cardData.tags.Contains("TargetAllies")) // 卡牌目标为友方单位
|
||||
{
|
||||
if(user.fraction is Fraction.Ally or Fraction.Player)
|
||||
{
|
||||
@@ -103,7 +111,7 @@ namespace Continentis.MainGame.Card
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cardData.functionalTags.Contains("TargetEnemies")) // 卡牌目标为敌人
|
||||
else if (cardData.tags.Contains("TargetEnemies")) // 卡牌目标为敌人
|
||||
{
|
||||
if (user.fraction is Fraction.Ally or Fraction.Player)
|
||||
{
|
||||
@@ -142,44 +150,58 @@ namespace Continentis.MainGame.Card
|
||||
.Where(target => !target.statusSubmodule.HasStatus(StatusType.Taunt))
|
||||
.ToList();
|
||||
|
||||
conditionNotMet.AddRange(protectedTargets);
|
||||
notMet.AddRange(protectedTargets);
|
||||
valid.RemoveAll(target => !target.statusSubmodule.HasStatus(StatusType.Taunt));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(cardData.functionalTags.Contains("TargetAll")) // 卡牌目标为全体
|
||||
else if(cardData.tags.Contains("TargetAll")) // 卡牌目标为全体
|
||||
{
|
||||
valid.AddRange(characters);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("No valid target found, please check the card data.");
|
||||
Debug.Log("No valid target found");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual List<CharacterBase> SetRandomTargets(List<CharacterBase> valid)
|
||||
{
|
||||
List<CharacterBase> targets = new List<CharacterBase>();
|
||||
int maximumTargets = attributeSubmodule.targetCount;
|
||||
if (maximumTargets == -1 || maximumTargets >= valid.Count)
|
||||
{
|
||||
targets.AddRange(valid);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (targets.Count < maximumTargets && valid.Count > 0)
|
||||
{
|
||||
CharacterBase target = valid[Random.Range(0, valid.Count)];
|
||||
valid.Remove(target);
|
||||
targets.Add(target);
|
||||
}
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
public virtual bool CheckBeforePlay()
|
||||
{
|
||||
Vector3 userPosition = user.characterView.transform.position;
|
||||
|
||||
if (!CheckEnoughStamina())
|
||||
if (!user.CheckEnoughStamina(GetAttribute("StaminaCost")))
|
||||
{
|
||||
MainGameManager.Instance.basePrefabs.GenerateInfoText("Not Enough Stamina", userPosition);
|
||||
MainGameManager.Instance.basePrefabs.GenerateInfoText("Not Enough Stamina", user.characterView);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CheckEnoughMana())
|
||||
if (!user.CheckEnoughMana(GetAttribute("ManaCost")))
|
||||
{
|
||||
MainGameManager.Instance.basePrefabs.GenerateInfoText("Not Enough Mana", userPosition);
|
||||
MainGameManager.Instance.basePrefabs.GenerateInfoText("Not Enough Mana", user.characterView);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*public void Play(List<CharacterBase> targetList, CharacterBase user = null, bool willCheckBeforePlay = true)
|
||||
{
|
||||
CommandQueueManager.Instance.AddCommand(_Play(targetList, user, willCheckBeforePlay));
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// 打出卡牌
|
||||
@@ -199,19 +221,45 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
handCardView.isDuringPlaying = true;
|
||||
}
|
||||
|
||||
|
||||
cardInstance.user = user ?? CombatMainManager.Instance.currentCharacter;
|
||||
|
||||
if (!willCheckBeforePlay || CheckBeforePlay())
|
||||
{
|
||||
eventSubmodule.onBeforePlay.Invoke(targetList);
|
||||
CommandQueueManager.Instance.AddCommand(PlayEffect(targetList));
|
||||
eventSubmodule.onAfterPlay.Invoke(targetList);
|
||||
AfterPlayEffect(targetList);
|
||||
combatBuffSubmodule.buffList.For(buff => buff.usageSubmodule?.UpdateModule());
|
||||
if (cardInstance.user is PlayerHero)
|
||||
CombatUIManager.Instance.deckPage.combatResourcesDisplayer.UpdateIcons();
|
||||
cardInstance.user.ModifyStamina(-GetAttribute("StaminaCost"));
|
||||
cardInstance.user.ModifyMana(-GetAttribute("ManaCost"));
|
||||
|
||||
if (cardInstance.user is PlayerHero)
|
||||
{
|
||||
CombatUIManager.Instance.combatMainPage.combatResourcesDisplayer.UpdateIcons();
|
||||
}
|
||||
|
||||
Debug.Log($"Starting to play card: {contentSubmodule.cardName}");
|
||||
|
||||
CommandQueueManager.Instance.AddCommand(new Cmd_Function(() =>
|
||||
{
|
||||
playSubmodule.isDuringPlayEffect = true;
|
||||
eventSubmodule.onBeforePlay.Invoke(targetList);
|
||||
cardInstance.user.eventSubmodule?.onBeforePlayCard.Invoke(cardInstance, targetList);
|
||||
cardInstance.user.combatBuffSubmodule.buffList.For(buff =>
|
||||
{
|
||||
buff.eventSubmodule?.onBeforePlayCard.Invoke(cardInstance, targetList);
|
||||
});
|
||||
}));
|
||||
|
||||
CommandQueueManager.Instance.AddCommand(PlayEffect(targetList));
|
||||
CommandQueueManager.Instance.AddCommand(new Cmd_Function(() =>
|
||||
{
|
||||
eventSubmodule.onAfterPlay.Invoke(targetList);
|
||||
combatBuffSubmodule.buffList.For(buff => buff.usageSubmodule?.UpdateModule());
|
||||
cardInstance.user.eventSubmodule.onAfterPlayCard.Invoke(cardInstance, targetList);
|
||||
cardInstance.user.combatBuffSubmodule.buffList.For(buff =>
|
||||
{
|
||||
buff.eventSubmodule?.onAfterPlayCard.Invoke(cardInstance, targetList);
|
||||
});
|
||||
AfterPlayEffect(targetList);
|
||||
playSubmodule.isDuringPlayEffect = false;
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -227,9 +275,6 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
protected virtual CommandBase PlayEffect(List<CharacterBase> targetList)
|
||||
{
|
||||
ConsumeStamina();
|
||||
ConsumeMana();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -237,14 +282,14 @@ namespace Continentis.MainGame.Card
|
||||
{
|
||||
if (user is PlayerHero playerHero)
|
||||
{
|
||||
if (HasTag("Exhaust"))
|
||||
if (HasKeyword("Exhaust"))
|
||||
{
|
||||
playerHero.deckSubmodule.ExhaustCard(cardInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
playerHero.deckSubmodule.DiscardCard(cardInstance);
|
||||
CommandQueueManager.Instance.AddCommand(new Cmd_Function(0, () =>
|
||||
CommandQueueManager.Instance.AddCommand(new Cmd_Function(() =>
|
||||
{
|
||||
if (handCardView != null)
|
||||
{
|
||||
@@ -255,7 +300,7 @@ namespace Continentis.MainGame.Card
|
||||
}
|
||||
else if (user is CombatNPC npc)
|
||||
{
|
||||
if (HasTag("Exhaust"))
|
||||
if (HasKeyword("Exhaust"))
|
||||
{
|
||||
npc.deckSubmodule.ExhaustCard(cardInstance);
|
||||
}
|
||||
@@ -265,34 +310,6 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
public partial class CardLogicBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加格挡(格挡每回合结束后会清空)
|
||||
/// </summary>
|
||||
protected void AddBlock(int block, CharacterBase target = null)
|
||||
{
|
||||
target ??= user;
|
||||
target.attributeSubmodule.generalAttributeGroup.current["Block"] += block;
|
||||
target.characterView.hudContainer.UpdateAllHUD();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加闪避(闪避在回合结束后或被击中后清空)
|
||||
/// </summary>
|
||||
protected void AddDodge(int dodge, CharacterBase target = null)
|
||||
{
|
||||
target ??= user;
|
||||
target.attributeSubmodule.generalAttributeGroup.current["Dodge"] += dodge;
|
||||
target.characterView.hudContainer.UpdateAllHUD();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加护盾(护盾不会自动清空)
|
||||
/// </summary>
|
||||
protected void AddShield(int shield, CharacterBase target = null)
|
||||
{
|
||||
target ??= user;
|
||||
target.attributeSubmodule.generalAttributeGroup.current["Shield"] += shield;
|
||||
target.characterView.hudContainer.UpdateAllHUD();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SoulliesFramework.General;
|
||||
using SLSFramework.General;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -18,9 +18,9 @@ namespace Continentis.MainGame.Card
|
||||
private void Initialize(CardData cardData)
|
||||
{
|
||||
Dictionary<string, float> originalAttributes = new Dictionary<string, float>(cardData.originalAttributes);
|
||||
Dictionary<string, string> endowingAttributes = new Dictionary<string, string>(cardData.endowingCurrentAttributes);
|
||||
Dictionary<string, string> endowingAttributes = new Dictionary<string, string>(cardData.runtimeCurrentAttributes);
|
||||
|
||||
if (cardData.upgradeNode.isInfiniteUpgrade)
|
||||
if (cardData.upgradeNode is { isInfiniteUpgrade: true })
|
||||
{
|
||||
foreach (KeyValuePair<string, float> upgrade in cardData.upgradeNode.GetUpgradeAttributes(owner.upgradeLevel))
|
||||
{
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using DynamicExpresso;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public static partial class CardDescriptionInterpreter
|
||||
{
|
||||
public static Interpreter DescriptionInterpreter;
|
||||
|
||||
static CardDescriptionInterpreter()
|
||||
{
|
||||
DescriptionInterpreter = new Interpreter();
|
||||
DescriptionInterpreter.SetFunction("GetDynamicValue", new Func<float, float, bool, string>(GetDynamicValue));
|
||||
DescriptionInterpreter.SetFunction("GetDynamicValue", new Func<float, string>(GetDynamicValue));
|
||||
DescriptionInterpreter.SetFunction("AttributeCheck", new Func<string, float, float, string>(AttributeCheck));
|
||||
|
||||
foreach (KeyValuePair<string,string> keyword in CombatMainManager.Instance.keywordCollection.keywords)
|
||||
{
|
||||
DescriptionInterpreter.SetVariable(keyword.Key, keyword.Key);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, InterpretedKeyword> keyword in CombatMainManager.Instance.keywordCollection.interpretedKeywords)
|
||||
{
|
||||
DescriptionInterpreter.SetVariable(keyword.Key, keyword.Value.name);
|
||||
}
|
||||
}
|
||||
|
||||
public static string InterpretDescription(CardLogicBase card)
|
||||
{
|
||||
card.contentSubmodule.keywords.Clear();
|
||||
|
||||
foreach (KeyValuePair<string, float> attribute in card.attributeSubmodule.attributeGroup.current)
|
||||
{
|
||||
DescriptionInterpreter.SetVariable(attribute.Key, attribute.Value);
|
||||
}
|
||||
|
||||
DescriptionInterpreter.SetFunction("RemoveWhenSelecting", new Func<string, string>((toRemove) => RemoveWhenSelecting(card, toRemove)));
|
||||
|
||||
string result = ParseKeywords(card.cardData.cardDescription, card.contentSubmodule.keywords);
|
||||
|
||||
card.contentSubmodule.cardDescription = result;
|
||||
|
||||
Debug.Log($"Interpreted Description: {result}");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CardDescriptionInterpreter
|
||||
{
|
||||
public static List<string> GetKeywords(string template)
|
||||
{
|
||||
List<string> keywords = new List<string>();
|
||||
|
||||
DescriptionInterpreter.UnsetExpression("Keyword");
|
||||
DescriptionInterpreter.SetFunction("Keyword", new Action<string>((keywordName) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(keywordName) && !keywords.Contains(keywordName))
|
||||
{
|
||||
keywords.Add(keywordName);
|
||||
}
|
||||
}));
|
||||
|
||||
try
|
||||
{
|
||||
//提取所有$Keyword(...)的关键词
|
||||
//其它的表达式直接跳过
|
||||
while (template.Contains("$"))
|
||||
{
|
||||
int startIndex = template.LastIndexOf('$');
|
||||
int endIndex = FindMatchingClosingParenthesis(template, startIndex);
|
||||
|
||||
if (endIndex == -1)
|
||||
{
|
||||
Debug.LogError($"解析错误: 在 '{template}' 中找不到与 '{template.Substring(startIndex)}' 匹配的闭合括号。");
|
||||
break; // 中断以防止死循环
|
||||
}
|
||||
|
||||
string expressionToEvaluate = template.Substring(startIndex, endIndex - startIndex + 1);
|
||||
string cleanExpression = expressionToEvaluate.Substring(1);
|
||||
|
||||
// 仅处理 Keyword 函数,跳过其他表达式
|
||||
if (cleanExpression.StartsWith("Keyword"))
|
||||
{
|
||||
DescriptionInterpreter.Eval(cleanExpression);
|
||||
}
|
||||
|
||||
// 移除已处理的表达式,继续查找下一个
|
||||
template = template.Substring(0, startIndex) + template.Substring(endIndex + 1);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"解析模板时发生严重错误: {ex.Message}\nStackTrace: {ex.StackTrace}");
|
||||
}
|
||||
|
||||
return keywords;
|
||||
}
|
||||
|
||||
public static string ParseKeywords(string template, List<string> keywords)
|
||||
{
|
||||
DescriptionInterpreter.UnsetExpression("Keyword");
|
||||
DescriptionInterpreter.SetFunction("Keyword", new Func<string, string>((keywordName) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(keywordName) && !keywords.Contains(keywordName))
|
||||
{
|
||||
keywords.Add(keywordName);
|
||||
}
|
||||
|
||||
return Keyword(keywordName);
|
||||
}));
|
||||
|
||||
try
|
||||
{
|
||||
while (template.Contains("$"))
|
||||
{
|
||||
int startIndex = template.LastIndexOf('$');
|
||||
int endIndex = FindMatchingClosingParenthesis(template, startIndex);
|
||||
|
||||
if (endIndex == -1)
|
||||
{
|
||||
Debug.LogError($"解析错误: 在 '{template}' 中找不到与 '{template.Substring(startIndex)}' 匹配的闭合括号。");
|
||||
return template; // 中断以防止死循环
|
||||
}
|
||||
|
||||
string expressionToEvaluate = template.Substring(startIndex, endIndex - startIndex + 1);
|
||||
string cleanExpression = expressionToEvaluate.Substring(1);
|
||||
|
||||
object result = DescriptionInterpreter.Eval(cleanExpression);
|
||||
|
||||
string resultAsLiteral = result.ToString();
|
||||
template = template.Substring(0, startIndex) + resultAsLiteral + template.Substring(endIndex + 1);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"解析模板时发生严重错误: {ex.Message}\nStackTrace: {ex.StackTrace}");
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
private static int FindMatchingClosingParenthesis(string text, int startIndex)
|
||||
{
|
||||
int openParenIndex = text.IndexOf('(', startIndex);
|
||||
if (openParenIndex == -1) return -1;
|
||||
|
||||
int parenthesisCounter = 1;
|
||||
bool isInString = false;
|
||||
|
||||
for (int i = openParenIndex + 1; i < text.Length; i++)
|
||||
{
|
||||
char c = text[i];
|
||||
char prevC = i > 0 ? text[i - 1] : '\0';
|
||||
|
||||
// 检查是否进入或退出字符串(忽略转义的引号 \")
|
||||
if (c == '"' && prevC != '\\')
|
||||
{
|
||||
isInString = !isInString;
|
||||
}
|
||||
|
||||
// 如果在字符串中,则跳过对括号的计数
|
||||
if (isInString)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '(')
|
||||
{
|
||||
parenthesisCounter++;
|
||||
}
|
||||
else if (c == ')')
|
||||
{
|
||||
parenthesisCounter--;
|
||||
}
|
||||
|
||||
if (parenthesisCounter == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // 没有找到匹配的闭合括号
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CardDescriptionInterpreter
|
||||
{
|
||||
private static string GetDynamicValue(float current, float baseValue, bool normalMode)
|
||||
{
|
||||
string color;
|
||||
if (current > baseValue)
|
||||
color = normalMode ? "green" : "red";
|
||||
else if (current < baseValue)
|
||||
color = normalMode ? "red" : "green";
|
||||
else
|
||||
color = "white";
|
||||
|
||||
//Debug.Log($"GetDynamicValue: current={current}, baseValue={baseValue}, normalMode={normalMode}, color={color}");
|
||||
|
||||
return $"<color={color}>{current}</color>";
|
||||
}
|
||||
|
||||
private static string GetDynamicValue(float current)
|
||||
{
|
||||
return current.ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
private static string Keyword(string keyword)
|
||||
{
|
||||
string color = "orange";
|
||||
return $"<color={color}>{keyword}</color>";
|
||||
}
|
||||
|
||||
private static string RemoveWhenSelecting(CardLogicBase card, string toRemove)
|
||||
{
|
||||
return card.handCardView != null && card.handCardView.isSelecting ? "" : toRemove;
|
||||
}
|
||||
|
||||
private static string AttributeCheck(string attributeName, float requirement, float probability)
|
||||
{
|
||||
if (probability < 0)
|
||||
{
|
||||
return "{" + attributeName + " check: " + requirement + "}";
|
||||
}
|
||||
|
||||
int percent = Mathf.RoundToInt(probability * 100);
|
||||
|
||||
string result = percent + "%";
|
||||
|
||||
if (percent == 0)
|
||||
{
|
||||
result = "<color=red>" + result + "</color>";
|
||||
}
|
||||
else if (percent > 0 && percent < 100)
|
||||
{
|
||||
result = "<color=yellow>" + result + "</color>";
|
||||
}
|
||||
else if (percent == 100)
|
||||
{
|
||||
result = "<color=green>" + result + "</color>";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbc566d961d78f142a5699dc40f43462
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SoulliesFramework.General;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
@@ -38,7 +38,7 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
public void RoundStart()
|
||||
{
|
||||
buffList.For(buff => buff.combatRoundTimeSubmodule?.UpdateModule());
|
||||
buffList.For(buff => buff.combatRoundTimeSubmodule?.Update());
|
||||
buffList.For(buff => buff.OnRoundStart());
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
public void ActionStart()
|
||||
{
|
||||
buffList.For(buff => buff.combatActionTimeSubmodule?.UpdateModule());
|
||||
buffList.For(buff => buff.combatActionTimeSubmodule?.Update());
|
||||
buffList.For(buff => buff.OnActionStart());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
@@ -6,30 +7,27 @@ namespace Continentis.MainGame.Card
|
||||
public class ContentSubmodule : SubmoduleBase<CardLogicBase>
|
||||
{
|
||||
public List<string> keywords;
|
||||
public List<string> hintKeywords;
|
||||
|
||||
public string cardName;
|
||||
public Sprite cardSprite;
|
||||
public CardRarity cardRarity;
|
||||
public Rarity cardRarity;
|
||||
public CardType cardType;
|
||||
public string cardDescription;
|
||||
public string originalFunctionText;
|
||||
public string interpretedFunctionText;
|
||||
|
||||
public ContentSubmodule(CardLogicBase card) : base(card)
|
||||
{
|
||||
keywords = new List<string>();
|
||||
cardName = card.cardData.cardName;
|
||||
hintKeywords = new List<string>();
|
||||
cardName = card.cardData.displayName.Localize();
|
||||
cardSprite = card.cardData.cardSprite;
|
||||
cardDescription = card.cardData.cardDescription;
|
||||
originalFunctionText = card.cardData.functionText.Localize();
|
||||
cardRarity = card.cardData.cardRarity;
|
||||
cardType = card.cardData.cardType;
|
||||
//CardDescriptionInterpreter.InterpretDescription(card);
|
||||
//keywords = CardDescriptionInterpreter.GetKeywords(card.cardData.cardDescription);
|
||||
//Debug.Log($"Extracted Keywords: {string.Join(", ", keywords)}");
|
||||
}
|
||||
|
||||
public string GetFinalDescription()
|
||||
{
|
||||
//finalDescription = CardDescriptionInterpreter.InterpretDescription(card);
|
||||
return cardDescription;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Continentis.MainGame.Character;
|
||||
using SoftCircuits.Collections;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
@@ -8,6 +9,9 @@ namespace Continentis.MainGame.Card
|
||||
{
|
||||
public partial class EventSubmodule : SubmoduleBase<CardLogicBase>
|
||||
{
|
||||
public UnityAction<CharacterBase> onTargeting; //选中目标时
|
||||
public UnityAction onUntargeting; //取消选中目标时
|
||||
|
||||
public OrderedDictionary<string, EventUnit> onCombatStart; //战斗开始时
|
||||
public OrderedDictionary<string, EventUnit> onCombatEnd; //战斗结束时
|
||||
|
||||
@@ -25,6 +29,9 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
public EventSubmodule(CardLogicBase card) : base(card)
|
||||
{
|
||||
onTargeting += card.TargetingEffect;
|
||||
onUntargeting = card.UntargetingEffect;
|
||||
|
||||
onCombatStart = new OrderedDictionary<string, EventUnit>();
|
||||
onCombatEnd = new OrderedDictionary<string, EventUnit>();
|
||||
|
||||
@@ -44,9 +51,6 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
public partial class EventSubmodule
|
||||
{
|
||||
protected void SetUpDefaultEvents()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,11 @@ namespace Continentis.MainGame.Card
|
||||
{
|
||||
public partial class PlaySubmodule : SubmoduleBase<CardLogicBase>
|
||||
{
|
||||
public bool isDuringPlayEffect;
|
||||
|
||||
public PlaySubmodule(CardLogicBase card) : base(card)
|
||||
{
|
||||
|
||||
isDuringPlayEffect = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
@@ -21,16 +20,27 @@ namespace Continentis.MainGame.Card
|
||||
public TMP_Text staminaCostText;
|
||||
public TMP_Text manaCostText;
|
||||
public TMP_Text cardTypeText;
|
||||
public Image cardBackground;
|
||||
public Image cardImage;
|
||||
public Image cardRarityMark;
|
||||
public CardViewKeywordList keywordList;
|
||||
|
||||
public Image normalShadow;
|
||||
public Image hintShadow;
|
||||
public Image selectShadow;
|
||||
|
||||
public string collectionName;
|
||||
}
|
||||
|
||||
public partial class CardViewBase
|
||||
{
|
||||
|
||||
public bool isOccupied;
|
||||
|
||||
public bool isHovering;
|
||||
|
||||
public bool isSelecting;
|
||||
|
||||
public bool isDuringPlaying;
|
||||
|
||||
private void Update()
|
||||
@@ -46,7 +56,11 @@ namespace Continentis.MainGame.Card
|
||||
if(isDuringPlaying) return;
|
||||
|
||||
isHovering = true;
|
||||
keywordList.Enable(cardInstance.cardLogic.contentSubmodule.keywords);
|
||||
|
||||
List<string> allKeywords = new List<string>();
|
||||
allKeywords.AddRange(cardLogic.contentSubmodule.keywords);
|
||||
allKeywords.AddRange(cardLogic.contentSubmodule.hintKeywords);
|
||||
keywordList.Enable(allKeywords);
|
||||
}
|
||||
|
||||
public virtual void OnPointerExit(PointerEventData eventData)
|
||||
@@ -65,37 +79,41 @@ namespace Continentis.MainGame.Card
|
||||
cardInstance = card;
|
||||
}
|
||||
|
||||
isOccupied = false;
|
||||
isHovering = false;
|
||||
isSelecting = false;
|
||||
isDuringPlaying = false;
|
||||
|
||||
cardNameText.text = cardLogic.contentSubmodule.cardName;
|
||||
cardDescriptionText.text = cardLogic.contentSubmodule.cardDescription;
|
||||
cardDescriptionText.text = cardLogic.contentSubmodule.interpretedFunctionText;
|
||||
cardImage.sprite = cardLogic.contentSubmodule.cardSprite;
|
||||
|
||||
if (cardLogic.contentSubmodule.cardRarity != CardRarity.None)
|
||||
if (cardLogic.contentSubmodule.cardRarity != Rarity.None)
|
||||
{
|
||||
cardRarityMark.color = cardLogic.contentSubmodule.cardRarity switch
|
||||
{
|
||||
CardRarity.Common => Color.white,
|
||||
CardRarity.Uncommon => new Color(0.2f, 0.7f, 0.2f), // 绿色
|
||||
CardRarity.Rare => new Color(0f, 0.5f, 1f), // 蓝色
|
||||
CardRarity.Epic => new Color(0.6f, 0f, 0.8f), // 紫色
|
||||
CardRarity.Legendary => new Color(1f, 0.5f, 0f), // 橙色
|
||||
CardRarity.Divine => new Color(1f, 0.84f, 0f), // 金色
|
||||
_ => Color.grey
|
||||
};
|
||||
cardRarityMark.color = MainGameManager.Instance.basePrefabs.GetRarityColor(cardLogic.contentSubmodule.cardRarity);
|
||||
}
|
||||
else
|
||||
{
|
||||
cardRarityMark.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(collectionName)) collectionName = "Basic";
|
||||
CardViewCollection collection = MainGameManager.Instance.basePrefabs.cardViewCollections[collectionName];
|
||||
List<string> elementTags = cardLogic.GetElementTags();
|
||||
string firstElementTag = elementTags.Count > 0 ? elementTags[0] : "Default";
|
||||
if (!collection.cardViews.ContainsKey(firstElementTag)) firstElementTag = "Default";
|
||||
cardBackground.sprite = collection.cardViews[firstElementTag].background;
|
||||
|
||||
cardTypeText.text = cardLogic.contentSubmodule.cardType.ToString();
|
||||
|
||||
|
||||
staminaCostText.rectTransform.parent.gameObject.SetActive(true);
|
||||
staminaCostText.text = cardLogic.attributeSubmodule.GetRoundCurrentAttribute("StaminaCost").ToString();
|
||||
|
||||
int manaCost = cardLogic.attributeSubmodule.GetRoundCurrentAttribute("ManaCost");
|
||||
manaCostText.rectTransform.parent.gameObject.SetActive(manaCost > 0);
|
||||
manaCostText.text = manaCost.ToString();
|
||||
|
||||
if (cardLogic.HasTag("Unplayable")) // 如果卡牌不能被打出,则隐藏费用文本
|
||||
if (cardLogic.HasKeyword("Unplayable")) // 如果卡牌不能被打出,则隐藏费用文本
|
||||
{
|
||||
staminaCostText.rectTransform.parent.gameObject.SetActive(false);
|
||||
manaCostText.rectTransform.parent.gameObject.SetActive(false);
|
||||
|
||||
23
Assets/Scripts/MainGame/Card/CardView/CardViewCollection.cs
Normal file
23
Assets/Scripts/MainGame/Card/CardView/CardViewCollection.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
[CreateAssetMenu(fileName = "CardViewCollection", menuName = "Continentis/MainGame/Card/CardViewCollection")]
|
||||
public class CardViewCollection : ScriptableObject
|
||||
{
|
||||
[KeyWidth(0.25f)]
|
||||
public SerializableDictionary<string, CardViewParts> cardViews;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CardViewParts
|
||||
{
|
||||
public Sprite background;
|
||||
|
||||
[KeyWidth(0.25f)]
|
||||
public SerializableDictionary<string, GameObject> customParts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9af8ea1600887d498ff2b813a9aaef8
|
||||
65
Assets/Scripts/MainGame/Card/CardView/CardViewFunctions.cs
Normal file
65
Assets/Scripts/MainGame/Card/CardView/CardViewFunctions.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public abstract partial class CardViewBase
|
||||
{
|
||||
Tweener hintShadowTweener;
|
||||
Tweener selectShadowTweener;
|
||||
|
||||
public void ClearShadows()
|
||||
{
|
||||
hintShadowTweener?.Kill(true);
|
||||
selectShadowTweener?.Kill(true);
|
||||
hintShadow.gameObject.SetActive(false);
|
||||
selectShadow.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
public void TryDisableAllShadows(bool immediately = false)
|
||||
{
|
||||
DisableHintShadow(immediately);
|
||||
DisableSelectShadow(immediately);
|
||||
}
|
||||
|
||||
public void EnableHintShadow(Color shadowColor)
|
||||
{
|
||||
hintShadow.gameObject.SetActive(true);
|
||||
hintShadow.color = Color.clear;
|
||||
hintShadowTweener = hintShadow.DOColor(shadowColor, 0.2f).Play();
|
||||
}
|
||||
|
||||
public void DisableHintShadow(bool immediately = false)
|
||||
{
|
||||
if (immediately)
|
||||
{
|
||||
hintShadowTweener?.Kill(true);
|
||||
hintShadow.gameObject.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
hintShadowTweener = hintShadow.DOColor(Color.clear, 0.2f).OnComplete(() => { hintShadow.gameObject.SetActive(false); }).Play();
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableSelectShadow()
|
||||
{
|
||||
selectShadow.gameObject.SetActive(true);
|
||||
selectShadow.color = Color.clear;
|
||||
selectShadowTweener = selectShadow.DOColor(new Color(0.33f, 0.66f, 1f), 0.2f).Play();
|
||||
}
|
||||
|
||||
public void DisableSelectShadow(bool immediately = false)
|
||||
{
|
||||
if (immediately)
|
||||
{
|
||||
selectShadowTweener?.Kill(true);
|
||||
selectShadow.gameObject.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectShadowTweener = selectShadow.DOColor(Color.clear, 0.2f).OnComplete(() => { selectShadow.gameObject.SetActive(false); }).Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5352b6ab2496aa34e82a2182052d556c
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Continentis.MainGame.Combat;
|
||||
using Continentis.MainGame.UI;
|
||||
using DG.Tweening;
|
||||
using Lean.Pool;
|
||||
using SoulliesFramework.General;
|
||||
using SLSFramework.General;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
@@ -27,15 +28,19 @@ namespace Continentis.MainGame.Card
|
||||
disableTweener = canvasGroup.DOFade(0f, 0.2f).SetEase(Ease.OutQuad).SetAutoKill(false);
|
||||
}
|
||||
|
||||
public void SetUp(List<string> keywords)
|
||||
public void SetUp(List<string> keys)
|
||||
{
|
||||
keywordsContainer.DespawnAllChildren();
|
||||
|
||||
foreach (string keyword in keywords)
|
||||
foreach (string key in keys)
|
||||
{
|
||||
string desc = CombatMainManager.Instance.keywordCollection.GetKeywordDescription(keyword);
|
||||
InformationBox keywordBox = LeanPool.Spawn(MainGameManager.Instance.basePrefabs.informationBox, keywordsContainer).GetComponent<InformationBox>();
|
||||
keywordBox.Initialize(keyword, desc);
|
||||
if (MainGameManager.Instance.keywordData.TryGetLocalizedKeyword(key, out string locName, out string locDesc))
|
||||
{
|
||||
InformationBox keywordBox = LeanPool.Spawn(MainGameManager.Instance.basePrefabs.informationBox, keywordsContainer)
|
||||
.GetComponent<InformationBox>();
|
||||
|
||||
keywordBox.Initialize(locName, locDesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Continentis.MainGame.Character;
|
||||
using Continentis.MainGame.Combat;
|
||||
using Continentis.MainGame.UI;
|
||||
using SoulliesFramework.General;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
@@ -9,6 +10,8 @@ namespace Continentis.MainGame.Card
|
||||
{
|
||||
public partial class HandCardView : IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerClickHandler
|
||||
{
|
||||
public CombatCharacterViewBase currentTargetingCharacterView;
|
||||
|
||||
public List<CharacterBase> validTargets = new List<CharacterBase>();
|
||||
public List<CharacterBase> conditionNotMetTargets = new List<CharacterBase>();
|
||||
public List<CharacterBase> invalidTargets = new List<CharacterBase>();
|
||||
@@ -37,17 +40,30 @@ namespace Continentis.MainGame.Card
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (CombatUIManager.Instance.deckPage.handCardSelector.gameObject.activeInHierarchy)
|
||||
if (CombatUIManager.Instance.combatMainPage.handCardSelector.gameObject.activeInHierarchy)
|
||||
{
|
||||
if(isOccupied) return;
|
||||
|
||||
if (CombatUIManager.Instance.deckPage.handCardSelector.selectedCards.ContainsKey(cardInstance))
|
||||
if (CombatUIManager.Instance.combatMainPage.handCardSelector.selectedCards.Contains(cardInstance))
|
||||
{
|
||||
CombatUIManager.Instance.deckPage.handCardSelector.Deselect(this);
|
||||
CombatUIManager.Instance.combatMainPage.handCardSelector.Deselect(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
CombatUIManager.Instance.deckPage.handCardSelector.Select(this);
|
||||
CombatUIManager.Instance.combatMainPage.handCardSelector.Select(this);
|
||||
}
|
||||
}
|
||||
else if (CombatUIManager.Instance.combatMainPage.customCardSelector.gameObject.activeInHierarchy)
|
||||
{
|
||||
if(isOccupied) return;
|
||||
|
||||
if (CombatUIManager.Instance.combatMainPage.customCardSelector.selectedCards.Contains(cardInstance))
|
||||
{
|
||||
CombatUIManager.Instance.combatMainPage.customCardSelector.Deselect(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
CombatUIManager.Instance.combatMainPage.customCardSelector.Select(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +74,7 @@ namespace Continentis.MainGame.Card
|
||||
CombatUIManager.Instance.selectingCardView = this;
|
||||
|
||||
cardInstance.user = CombatMainManager.Instance.currentCharacter;
|
||||
cardLogic.SetTargets(out validTargets, out conditionNotMetTargets, out invalidTargets);
|
||||
cardLogic.DetectTargetsValidity(out validTargets, out conditionNotMetTargets, out invalidTargets);
|
||||
|
||||
if (cardLogic.attributeSubmodule.targetCount == 1)
|
||||
{
|
||||
@@ -81,19 +97,26 @@ namespace Continentis.MainGame.Card
|
||||
Camera uiCamera = CombatUIManager.Instance.uiCamera;
|
||||
Camera worldCamera = CombatUIManager.Instance.combatCamera;
|
||||
|
||||
if (CombatUIManager.Instance.hoveringCharacterView != null)
|
||||
// 先检查悬停的视图是否和当前记录的目标视图发生了变化
|
||||
if (CombatUIManager.Instance.hoveringCharacterView != currentTargetingCharacterView)
|
||||
{
|
||||
cardLogic.Targeting(CombatUIManager.Instance.hoveringCharacterView.character);
|
||||
Debug.Log("Hovering over " + CombatUIManager.Instance.hoveringCharacterView.character);
|
||||
}
|
||||
else
|
||||
{
|
||||
cardLogic.Untargeting();
|
||||
// 如果悬停视图不是空的,说明鼠标移动到了一个新的目标上
|
||||
if (CombatUIManager.Instance.hoveringCharacterView != null)
|
||||
{
|
||||
cardLogic.eventSubmodule.onTargeting(CombatUIManager.Instance.hoveringCharacterView.character);
|
||||
currentTargetingCharacterView = CombatUIManager.Instance.hoveringCharacterView;
|
||||
}
|
||||
else // 悬停视图是空的,说明鼠标离开了之前的目标
|
||||
{
|
||||
cardLogic.eventSubmodule.onUntargeting();
|
||||
currentTargetingCharacterView = null;
|
||||
}
|
||||
|
||||
// 因为目标发生了变化(无论是选中了新的还是取消了),所以统一在这里更新描述
|
||||
CardTextInterpreter.InterpretText(cardLogic);
|
||||
cardDescriptionText.text = cardLogic.contentSubmodule.interpretedFunctionText;
|
||||
}
|
||||
|
||||
CardDescriptionInterpreter.InterpretDescription(cardLogic);
|
||||
cardDescriptionText.text = cardLogic.contentSubmodule.cardDescription;
|
||||
|
||||
Vector3 startPosition = cardTransform.position + new Vector3(0, cardTransform.rect.height * cardTransform.lossyScale.y / 2, 0);
|
||||
Vector3 endPosition = SpaceConverter.ScreenPointToUIPoint(arrowCanvasRect, eventData.position, uiCamera);
|
||||
PointerArrow mainPointerArrow = CombatUIManager.Instance.arrowsPage.mainPointerArrow;
|
||||
@@ -203,7 +226,7 @@ namespace Continentis.MainGame.Card
|
||||
CharacterBase hoveringCharacter = hoveringCharacterView != null ? hoveringCharacterView.character : null;
|
||||
Camera uiCamera = CombatUIManager.Instance.uiCamera;
|
||||
CombatUIManager.Instance.arrowsPage.ClearPointerArrows();
|
||||
|
||||
|
||||
if (isSelecting)
|
||||
{
|
||||
isSelecting = false;
|
||||
@@ -211,16 +234,21 @@ namespace Continentis.MainGame.Card
|
||||
CombatUIManager.Instance.selectingCardView = null;
|
||||
canvas.overrideSorting = false;
|
||||
canvas.sortingOrder = 0;
|
||||
cardLogic.Untargeting();
|
||||
CardDescriptionInterpreter.InterpretDescription(cardLogic);
|
||||
cardDescriptionText.text = cardLogic.contentSubmodule.cardDescription;
|
||||
cardLogic.eventSubmodule.onUntargeting();
|
||||
CardTextInterpreter.InterpretText(cardLogic);
|
||||
cardDescriptionText.text = cardLogic.contentSubmodule.interpretedFunctionText;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (cardLogic.HasKeyword("Unplayable")) // 如果有“不能打出”关键词,直接返回
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cardLogic.cardData.functionalTags.Contains("TargetSelf"))
|
||||
if (!cardLogic.HasTag("TargetSelf"))
|
||||
{
|
||||
if (!validTargets.Contains(hoveringCharacter))
|
||||
{
|
||||
@@ -236,15 +264,15 @@ namespace Continentis.MainGame.Card
|
||||
{
|
||||
if (!cardLogic.Play(new List<CharacterBase>() { CombatUIManager.Instance.hoveringCharacterView.character }))
|
||||
{
|
||||
cardLogic.Untargeting();
|
||||
CardDescriptionInterpreter.InterpretDescription(cardLogic);
|
||||
cardDescriptionText.text = cardLogic.contentSubmodule.cardDescription;
|
||||
cardLogic.eventSubmodule.onUntargeting();
|
||||
CardTextInterpreter.InterpretText(cardLogic);
|
||||
cardDescriptionText.text = cardLogic.contentSubmodule.interpretedFunctionText;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RectTransform dropZone = CombatUIManager.Instance.deckPage.dropZone;
|
||||
RectTransform dropZone = CombatUIManager.Instance.combatMainPage.dropZone;
|
||||
bool isInDropZone = RectTransformUtility.RectangleContainsScreenPoint(dropZone, eventData.position, uiCamera);
|
||||
List<CharacterBase> targetList = new List<CharacterBase>();
|
||||
|
||||
|
||||
8
Assets/Scripts/MainGame/Card/Editor.meta
Normal file
8
Assets/Scripts/MainGame/Card/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d9724259ccea534b91e4aff8161b6a1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
74
Assets/Scripts/MainGame/Card/Editor/CardDataEditor.cs
Normal file
74
Assets/Scripts/MainGame/Card/Editor/CardDataEditor.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using SLSFramework.UModAssistance;
|
||||
using Continentis.MainGame.Character;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
[CustomEditor(typeof(CardData))]
|
||||
public class CardDataEditor : DataEditor
|
||||
{
|
||||
// 存储我们需要自定义绘制的属性的引用
|
||||
private SerializedProperty classFullNameProp;
|
||||
private SerializedProperty prefabsProp;
|
||||
private SerializedProperty derivativeCardsProp;
|
||||
private SerializedProperty derivativeCharactersProp;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
// 在启用时,根据我们修改后的字段名找到对应的SerializedProperty
|
||||
classFullNameProp = serializedObject.FindProperty("classFullName");
|
||||
prefabsProp = serializedObject.FindProperty("prefabRefs");
|
||||
derivativeCardsProp = serializedObject.FindProperty("derivativeCardDataRefs");
|
||||
derivativeCharactersProp = serializedObject.FindProperty("derivativeCharacterDataRefs");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// --- 绘制自定义的Type选择器 ---
|
||||
// 我们把它从所有自动绘制的属性中分离出来,放在最前面或最后面,让布局更清晰
|
||||
EditorGUILayout.Space(); // 增加一点间距
|
||||
EditorGUILayout.LabelField("Logic", EditorStyles.boldLabel);
|
||||
if (DrawTypeSelectorGUI(classFullNameProp, "Card Logic Class", typeof(CardLogicBase), "Continentis.Mods", "Cards"))
|
||||
{
|
||||
string classFullName = classFullNameProp.stringValue;
|
||||
|
||||
string displayName = "Card_" + classFullName + "_DisplayName";
|
||||
SerializedProperty displayNameProp = serializedObject.FindProperty("displayName");
|
||||
displayNameProp.stringValue = displayName;
|
||||
|
||||
string functionTextName = "Card_" + classFullName + "_FunctionText";
|
||||
SerializedProperty functionTextProp = serializedObject.FindProperty("functionText");
|
||||
functionTextProp.stringValue = functionTextName;
|
||||
}
|
||||
|
||||
// --- 核心修复 2:将 _cardLogicClassNameProp 也加入排除列表 ---
|
||||
// 因为这也是我们手动绘制的
|
||||
DrawPropertiesExcluding(serializedObject, new string[]
|
||||
{
|
||||
"m_Script",
|
||||
classFullNameProp.name, // <-- 新增
|
||||
prefabsProp.name,
|
||||
derivativeCardsProp.name,
|
||||
derivativeCharactersProp.name
|
||||
});
|
||||
|
||||
// --- 绘制自定义的引用列表 ---
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("References", EditorStyles.boldLabel);
|
||||
DrawCharacterListGUI<GameObject>(prefabsProp);
|
||||
DrawCharacterListGUI<CardData>(derivativeCardsProp);
|
||||
DrawCharacterListGUI<CharacterData>(derivativeCharactersProp);
|
||||
|
||||
HandleObjectPicker();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8385aa085e0e6154da49b54bdc5d26e7
|
||||
@@ -0,0 +1,76 @@
|
||||
using Continentis.MainGame.Character;
|
||||
using Continentis.Mods.Basic.Cards;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public class CardLogicComponent_Attack : CardLogicComponentBase
|
||||
{
|
||||
protected override void TargetingEffect(CharacterBase target)
|
||||
{
|
||||
card.SetAttribute("DisplayDamage", card.GetFinalDamage(target));
|
||||
}
|
||||
|
||||
protected override void UntargetingEffect()
|
||||
{
|
||||
card.SetAttribute("DisplayDamage", card.GetAttribute("Damage"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置伤害值
|
||||
/// </summary>
|
||||
/// <param name="additive">是否为叠加,true为叠加,false为覆盖</param>
|
||||
/// <param name="originalDamage">原始伤害值,仅在additive为true时有效,否则被覆盖为BaseDamage</param>
|
||||
/// <param name="damageOffset">伤害增量</param>
|
||||
public void SetDamage(int damageOffset, bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
card.SetVariableAttribute("Damage", damageOffset, additive, originalDamage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 斩击伤害计算,伤害=基础伤害+(力量加成+敏捷加成)/2
|
||||
/// </summary>
|
||||
public void SetDamage_Slash(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
float rawDamageOffsetFromStrength = user.GetRawAttribute("OffsetFromStrength");
|
||||
float rawDamageOffsetFromAgility = user.GetRawAttribute("OffsetFromAgility");
|
||||
SetDamage(Mathf.RoundToInt((rawDamageOffsetFromStrength + rawDamageOffsetFromAgility) / 2f), additive, originalDamage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打击伤害计算,伤害=基础伤害+力量加成
|
||||
/// </summary>
|
||||
public void SetDamage_Strike(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
int damageOffset = user.GetAttribute("OffsetFromStrength");
|
||||
SetDamage(damageOffset, additive, originalDamage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 突刺伤害计算,伤害=基础伤害+敏捷加成
|
||||
/// </summary>
|
||||
public void SetDamage_Prick(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
int damageOffset = user.GetAttribute("OffsetFromAgility");
|
||||
SetDamage(damageOffset, additive, originalDamage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 奥术伤害计算,伤害=基础伤害+智力加成
|
||||
/// </summary>
|
||||
public void SetDamage_Arcane(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
int damageOffset = user.GetAttribute("OffsetFromIntelligence");
|
||||
SetDamage(damageOffset, additive, originalDamage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 契术伤害计算,伤害=基础伤害+魅力加成
|
||||
/// </summary>
|
||||
public void SetDamage_Sorcery(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
int damageOffset = user.GetAttribute("OffsetFromCharisma");
|
||||
SetDamage(damageOffset, additive, originalDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using Continentis.MainGame.Card;
|
||||
using Continentis.MainGame.Character;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public class CardLogicComponent_Defense : CardLogicComponentBase
|
||||
{
|
||||
protected override void TargetingEffect(CharacterBase target)
|
||||
{
|
||||
if (card.HasAttribute("Block"))
|
||||
{
|
||||
card.SetAttribute("DisplayBlock", card.GetAttribute("Block"));
|
||||
}
|
||||
else if(card.HasAttribute("Dodge"))
|
||||
{
|
||||
card.SetAttribute("DisplayDodge", card.GetAttribute("Dodge"));
|
||||
}
|
||||
else if(card.HasAttribute("Shield"))
|
||||
{
|
||||
card.SetAttribute("DisplayShield", card.GetAttribute("Shield"));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UntargetingEffect()
|
||||
{
|
||||
if (card.HasAttribute("Block"))
|
||||
{
|
||||
card.SetAttribute("DisplayBlock", card.GetAttribute("Block"));
|
||||
}
|
||||
else if(card.HasAttribute("Dodge"))
|
||||
{
|
||||
card.SetAttribute("DisplayDodge", card.GetAttribute("Dodge"));
|
||||
}
|
||||
else if(card.HasAttribute("Shield"))
|
||||
{
|
||||
card.SetAttribute("DisplayShield", card.GetAttribute("Shield"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置格挡值,默认由体质加成
|
||||
/// </summary>
|
||||
public void SetBlock_Fortitude(bool additive = false, int originalBlock = 0)
|
||||
{
|
||||
int blockOffsetFromPhysique = user.GetAttribute("OffsetFromPhysique");
|
||||
card.SetVariableAttribute("Block", blockOffsetFromPhysique, additive, originalBlock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置格挡值,由智力加成
|
||||
/// </summary>
|
||||
public void SetBlock_Arcane(bool additive = false, int originalBlock = 0)
|
||||
{
|
||||
int blockOffsetFromIntelligence = user.GetAttribute("OffsetFromIntelligence");
|
||||
card.SetVariableAttribute("Block", blockOffsetFromIntelligence, additive, originalBlock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置格挡值,由魅力加成
|
||||
/// </summary>
|
||||
public void SetBlock_Sorcery(bool additive = false, int originalBlock = 0)
|
||||
{
|
||||
int blockOffsetFromCharisma = user.GetAttribute("OffsetFromCharisma");
|
||||
card.SetVariableAttribute("Block", blockOffsetFromCharisma, additive, originalBlock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置闪避值,由敏捷加成
|
||||
/// </summary>
|
||||
public void SetDodge_Swiftness(bool additive = false, int originalDodge = 0)
|
||||
{
|
||||
int dodgeOffsetFromAgility = user.GetAttribute("OffsetFromAgility");
|
||||
card.SetVariableAttribute("Dodge", dodgeOffsetFromAgility, additive, originalDodge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置闪避值,由智力加成
|
||||
/// </summary>
|
||||
public void SetDodge_Arcane(bool additive = false, int originalDodge = 0)
|
||||
{
|
||||
int dodgeOffsetFromIntelligence = user.GetAttribute("OffsetFromIntelligence");
|
||||
card.SetVariableAttribute("Dodge", dodgeOffsetFromIntelligence, additive, originalDodge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置闪避值,由魅力加成
|
||||
/// </summary>
|
||||
public void SetDodge_Sorcery(bool additive = false, int originalDodge = 0)
|
||||
{
|
||||
int dodgeOffsetFromCharisma = user.GetAttribute("OffsetFromCharisma");
|
||||
card.SetVariableAttribute("Dodge", dodgeOffsetFromCharisma, additive, originalDodge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置闪避值,由感知加成
|
||||
/// </summary>
|
||||
public void SetDodge_Prediction(bool additive = false, int originalDodge = 0)
|
||||
{
|
||||
int dodgeOffsetFromPrediction = user.GetAttribute("OffsetFromPerception");
|
||||
card.SetVariableAttribute("Dodge", dodgeOffsetFromPrediction, additive, originalDodge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置护盾值,默认无加成
|
||||
/// </summary>
|
||||
public void SetShield(bool additive = false, int originalShield = 0)
|
||||
{
|
||||
card.SetVariableAttribute("Shield", 0, additive, originalShield);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e81c09b6cb1ff44091ccc365049bbb0
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Continentis.MainGame;
|
||||
using Continentis.MainGame.Card;
|
||||
using SLSFramework.UModAssistance;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.Mods.Basic.Cards
|
||||
{
|
||||
public class CardLogicComponent_GenerateCards : CardLogicComponentBase
|
||||
{
|
||||
private Func<CardData, bool> cardFilter;
|
||||
|
||||
public override void Initialize(CardLogicBase card)
|
||||
{
|
||||
base.Initialize(card);
|
||||
cardFilter = cardData => !cardData.HasTag("Unobtainable");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置卡牌过滤器
|
||||
/// </summary>
|
||||
public void SetFilter(Func<CardData, bool> filter)
|
||||
{
|
||||
List<Func<CardData, bool>> originalFilters = new List<Func<CardData, bool>>();
|
||||
originalFilters.Add(cardData => !cardData.HasTag("Unobtainable"));
|
||||
if (filter != null) originalFilters.Add(filter);
|
||||
this.cardFilter = cardData => originalFilters.All(f => f(cardData));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取衍生卡牌数据
|
||||
/// </summary>
|
||||
public CardData GetDerivativeCardData(int index)
|
||||
{
|
||||
return card.cardData.GetDerivativeCardData(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的cardDataID中,根据needFilter决定是否进行过滤,获取符合条件的卡牌数据列表
|
||||
/// </summary>
|
||||
/// <param name="needFilter">是否需要过滤</param>
|
||||
/// <param name="cardDataIDs">指定的卡牌数据ID列表</param>
|
||||
/// <returns>符合条件的卡牌数据列表</returns>
|
||||
public List<CardData> GetDesignatedGlobalCardData(bool needFilter, params string[] cardDataIDs)
|
||||
{
|
||||
List<CardData> result = new List<CardData>();
|
||||
|
||||
foreach (string dataID in cardDataIDs)
|
||||
{
|
||||
if (ModManager.TryGetData(dataID, out CardData cardData))
|
||||
{
|
||||
if (needFilter && cardFilter(cardData))
|
||||
{
|
||||
result.Add(cardData);
|
||||
}
|
||||
else if (!needFilter)
|
||||
{
|
||||
result.Add(cardData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在游戏全部的卡牌中,获取经过全局过滤器过滤后的所有卡牌数据列表
|
||||
/// </summary>
|
||||
public List<CardData> GetFilteredGlobalCardData()
|
||||
{
|
||||
return ModManager.Database[typeof(CardData)].Values.Cast<CardData>().Where(cardFilter).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在游戏全部的卡牌中,获取经过指定过滤器过滤后的所有卡牌数据列表
|
||||
/// </summary>
|
||||
public List<CardData> GetFilteredGlobalCardData(Func<CardData, bool> filter)
|
||||
{
|
||||
return ModManager.Database[typeof(CardData)].Values.Cast<CardData>().Where(filter).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0c9acec889d2da44a18ad1d15a372ae
|
||||
@@ -0,0 +1,29 @@
|
||||
using Continentis.MainGame.Character;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public class CardLogicComponent_LifeSteal : CardLogicComponentBase
|
||||
{
|
||||
protected override void TargetingEffect(CharacterBase target)
|
||||
{
|
||||
card.SetAttribute("DisplayLifeStealPercent", GetLifeStealPercent());
|
||||
}
|
||||
|
||||
protected override void UntargetingEffect()
|
||||
{
|
||||
card.SetAttribute("DisplayLifeStealPercent", card.GetRawAttribute("LifeStealPercent"));
|
||||
}
|
||||
|
||||
public float GetLifeStealPercent()
|
||||
{
|
||||
return card.GetRawAttribute("LifeStealPercent") * (user.GetRawAttribute("LifeStealMultiplier", 1));
|
||||
}
|
||||
|
||||
public void LifeSteal(float damageDealtOnHealth)
|
||||
{
|
||||
int heal = Mathf.RoundToInt(damageDealtOnHealth * GetLifeStealPercent());
|
||||
user.Heal(heal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69c578f9c9d557749b4f9001da42b816
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Continentis.MainGame.Commands;
|
||||
using Continentis.MainGame.UI;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public class CardLogicComponent_SelectCustomCards : CardLogicComponentBase
|
||||
{
|
||||
public List<CardInstance> selectedCards;
|
||||
|
||||
/// <summary>
|
||||
/// 添加选择手牌的指令
|
||||
/// </summary>
|
||||
/// <param name="commandGroup">目标指令组</param>
|
||||
/// <param name="cardsToSelect">可供选择的卡牌列表</param>
|
||||
/// <param name="title">选择卡牌的描述性标题</param>
|
||||
/// <param name="maxSelection">最大选择数量</param>
|
||||
/// <param name="forceMax">是否强制选择最大数量</param>
|
||||
public void AddSelectionCommands(ref CommandGroup commandGroup, List<CardInstance> cardsToSelect, string title, int maxSelection, bool forceMax = false)
|
||||
{
|
||||
selectedCards = new List<CardInstance>();
|
||||
CustomCardSelectionInterface customCardSelector = CombatUIManager.Instance.combatMainPage.customCardSelector;
|
||||
|
||||
commandGroup.AddCommand(new Cmd_Function(() =>
|
||||
{
|
||||
customCardSelector.Setup(title, card.cardInstance, cardsToSelect, maxSelection, forceMax);
|
||||
}));
|
||||
commandGroup.AddCommand(new Cmd_WaitForUI(customCardSelector));
|
||||
commandGroup.AddCommand(new Cmd_Function(() =>
|
||||
{
|
||||
selectedCards = customCardSelector.selectedCards.ToList();
|
||||
selectedCards.ForEach(SelectEffect);
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卡牌被选择后的效果
|
||||
/// </summary>
|
||||
public void SelectEffect(CardInstance card)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 454f2361104caef46a6aa0ebbc74a7dd
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Continentis.MainGame.Commands;
|
||||
using Continentis.MainGame.UI;
|
||||
using SLSFramework.General;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public class CardLogicComponent_SelectHandCards : CardLogicComponentBase
|
||||
{
|
||||
private Func<CardInstance, bool> selectCondition;
|
||||
private Action<CardInstance> selectEffect;
|
||||
|
||||
public List<CardInstance> selectedCards;
|
||||
|
||||
public CardLogicComponent_SelectHandCards SetCondition(Func<CardInstance, bool> condition)
|
||||
{
|
||||
selectCondition = condition;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public CardLogicComponent_SelectHandCards SetEffect(Action<CardInstance> effect)
|
||||
{
|
||||
selectEffect = effect;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加选择手牌的指令
|
||||
/// </summary>
|
||||
/// <param name="commandGroup">目标指令组</param>
|
||||
/// <param name="title">选择卡牌的描述性标题</param>
|
||||
/// <param name="maxSelection">最大选择数量</param>
|
||||
/// <param name="forceMax">是否强制选择最大数量</param>
|
||||
public void AddSelectionCommands(ref CommandGroup commandGroup, string title, int maxSelection, bool forceMax = false)
|
||||
{
|
||||
selectedCards = new List<CardInstance>();
|
||||
HandCardSelectionInterface handCardSelector = CombatUIManager.Instance.combatMainPage.handCardSelector;
|
||||
|
||||
commandGroup.AddCommand(new Cmd_Function(() =>
|
||||
{
|
||||
handCardSelector.Setup(title, card.cardInstance, maxSelection, selectCondition, forceMax);
|
||||
}));
|
||||
commandGroup.AddCommand(new Cmd_WaitForUI(handCardSelector));
|
||||
commandGroup.AddCommand(new Cmd_Function(() =>
|
||||
{
|
||||
selectedCards = handCardSelector.selectedCards.ToList();
|
||||
selectedCards.ForEach(selectEffect);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20613f1c30ff5434c968adba59d99bfc
|
||||
@@ -1,78 +0,0 @@
|
||||
using Continentis.MainGame.Character;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public abstract partial class CardLogicBase_Attack : CardLogicBase
|
||||
{
|
||||
protected override void TargetingEffect(CharacterBase target)
|
||||
{
|
||||
SetAttribute("DisplayDamage", GetFinalDamage(target));
|
||||
}
|
||||
|
||||
protected override void UntargetingEffect()
|
||||
{
|
||||
SetAttribute("DisplayDamage", GetAttribute("Damage"));
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class CardLogicBase_Attack
|
||||
{
|
||||
/// <summary>
|
||||
/// 设置伤害值
|
||||
/// </summary>
|
||||
/// <param name="additive">是否为叠加,true为叠加,false为覆盖</param>
|
||||
/// <param name="originalDamage">原始伤害值,仅在additive为true时有效,否则被覆盖为BaseDamage</param>
|
||||
/// <param name="damageOffset">伤害增量</param>
|
||||
protected void SetDamage(bool additive, int originalDamage, int damageOffset)
|
||||
{
|
||||
SetVariableAttribute("Damage", additive, originalDamage, damageOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 斩击伤害计算,伤害=基础伤害+(力量加成+敏捷加成)/2
|
||||
/// </summary>
|
||||
protected void SetDamage_Slash(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
float rawDamageOffsetFromStrength = user.GetRawAttribute("PhysicsDamageDealtOffsetFromStrength");
|
||||
float rawDamageOffsetFromAgility = user.GetRawAttribute("PhysicsDamageDealtOffsetFromAgility");
|
||||
SetDamage(additive, originalDamage, Mathf.RoundToInt((rawDamageOffsetFromStrength + rawDamageOffsetFromAgility) / 2f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打击伤害计算,伤害=基础伤害+力量加成
|
||||
/// </summary>
|
||||
protected void SetDamage_Strike(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
int damageOffset = user.GetAttribute("PhysicsDamageDealtOffsetFromStrength");
|
||||
SetDamage(additive, originalDamage, damageOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 突刺伤害计算,伤害=基础伤害+敏捷加成
|
||||
/// </summary>
|
||||
protected void SetDamage_Prick(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
int damageOffset = user.GetAttribute("PhysicsDamageDealtOffsetFromAgility");
|
||||
SetDamage(additive, originalDamage, damageOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 奥术伤害计算,伤害=基础伤害+智力加成
|
||||
/// </summary>
|
||||
protected void SetDamage_Arcane(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
int damageOffset = user.GetAttribute("MagicDamageDealtOffsetFromIntelligence");
|
||||
SetDamage(additive, originalDamage, damageOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 契术伤害计算,伤害=基础伤害+魅力加成
|
||||
/// </summary>
|
||||
protected void SetDamage_Sorcery(bool additive = false, int originalDamage = 0)
|
||||
{
|
||||
int damageOffset = user.GetAttribute("MagicDamageDealtOffsetFromCharisma");
|
||||
SetDamage(additive, originalDamage, damageOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Continentis.MainGame.Character;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public abstract class CardLogicBase_MagicalAttack : CardLogicBase_Attack
|
||||
{
|
||||
protected override int GetFinalDamage(CharacterBase target, List<string> elementalTags = null)
|
||||
{
|
||||
elementalTags ??= this.elementalTags;
|
||||
|
||||
float elementalAmplifier = 1;
|
||||
foreach (var element in elementalTags) //计算元素伤害加成
|
||||
{
|
||||
elementalAmplifier *= user.attributeSubmodule.GetCurrentGeneralAttribute(element + "DamageDealtMultiplier", 1);
|
||||
elementalAmplifier *= target.attributeSubmodule.GetCurrentGeneralAttribute(element + "DamageGainMultiplier", 1);
|
||||
}
|
||||
|
||||
float finalDamage =
|
||||
attributeSubmodule.GetRoundCurrentAttribute("Damage") * elementalAmplifier *
|
||||
user.attributeSubmodule.GetCurrentGeneralAttribute("MagicDamageDealtMultiplier", 1) *
|
||||
target.attributeSubmodule.GetCurrentGeneralAttribute("MagicDamageGainMultiplier", 1) *
|
||||
attributeSubmodule.GetCurrentAttribute("FinalDamageDealtMultiplier", 1) *
|
||||
target.attributeSubmodule.GetCurrentGeneralAttribute("FinalDamageGainMultiplier", 1);
|
||||
|
||||
return Mathf.RoundToInt(finalDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23176f7c6d22f4d448e10c22d0242ca3
|
||||
@@ -1,10 +0,0 @@
|
||||
using Continentis.MainGame.Character;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame.Card
|
||||
{
|
||||
public abstract partial class CardLogicBase_PhysicalAttack : CardLogicBase_Attack
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a27ca03d2c3e84f408af33246a9d6c85
|
||||
Reference in New Issue
Block a user