This commit is contained in:
SoulliesOfficial
2025-10-24 09:11:22 -04:00
parent 61a397dd4c
commit 76157e3cb1
329 changed files with 8609 additions and 4549 deletions

View File

@@ -13,11 +13,15 @@ namespace Continentis.MainGame
public static string Parse(Interpreter interpreter, string template, List<string> keywords, List<string> hintKeywords)
{
interpreter.UnsetFunction("Keyword");
interpreter.SetFunction("Keyword", new Func<string, string>(kw => SetKeyword(ref keywords, kw)));
interpreter.SetFunction("Keyword", new Func<string, string>(kw => SetKeyword(ref keywords, kw, "#FFA500")));
interpreter.UnsetFunction("HintKeyword");
interpreter.SetFunction("HintKeyword", new Func<string, string>(kw => SetKeyword(ref hintKeywords, kw)));
interpreter.SetFunction("HintKeyword", new Func<string, string>(kw => SetKeyword(ref hintKeywords, kw, "#FFA500")));
interpreter.SetFunction("HintKeyword", new Func<string, string, string>((kw, colorHex) => SetKeyword(ref hintKeywords, kw, colorHex)));
interpreter.UnsetFunction("DescKeyword");
interpreter.SetFunction("DescKeyword", new Func<string, string>(DescKeyword));
interpreter.SetFunction("DescKeyword", new Func<string, string>(kw =>DescKeyword(kw, "#FFA500")));
interpreter.SetFunction("DescKeyword", new Func<string, string, string>(DescKeyword));
interpreter.UnsetFunction("ColorText");
interpreter.SetFunction("ColorText", new Func<string, string, string>(ColorText));
try
{
@@ -34,7 +38,7 @@ namespace Continentis.MainGame
string expressionToEvaluate = template.Substring(startIndex, endIndex - startIndex + 1);
string cleanExpression = expressionToEvaluate.Substring(1);
Debug.Log($"Evaluating expression: {cleanExpression}");
object result = interpreter.Eval(cleanExpression);
string resultAsLiteral = result.ToString();
template = template.Substring(0, startIndex) + resultAsLiteral + template.Substring(endIndex + 1);
@@ -48,14 +52,14 @@ namespace Continentis.MainGame
return template;
//本地函数,用于添加关键词到集合中并返回格式化后的关键词字符串
string SetKeyword(ref List<string> collection, string keyword)
string SetKeyword(ref List<string> collection, string keyword, string colorHex)
{
if (!string.IsNullOrEmpty(keyword) && !collection.Contains(keyword))
{
collection.Add(keyword);
}
return Keyword(keyword);
return Keyword(keyword, colorHex);
}
}
@@ -139,10 +143,10 @@ namespace Continentis.MainGame
return valueStr;
}
public static string Keyword(string key)
public static string Keyword(string key, string colorHex)
{
string color = "orange";
string color = colorHex;
string result = key;
if (MainGameManager.Instance.keywordData.TryGetKeyword(key, out InterpretedKeyword keyword))
{
@@ -152,9 +156,9 @@ namespace Continentis.MainGame
return $"<color={color}>{result}</color>";
}
public static string DescKeyword(string key)
public static string DescKeyword(string key, string colorHex)
{
string color = "yellow";
string color = colorHex;
string result = key;
if (MainGameManager.Instance.keywordData.TryGetKeyword(key, out InterpretedKeyword keyword))
{
@@ -163,5 +167,11 @@ namespace Continentis.MainGame
return $"<color={color}>{result}</color>";
}
public static string ColorText(string text, string colorHex)
{
string color = colorHex;
return $"<color={color}>{text}</color>";
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SLSFramework.General;
using UnityEngine;
namespace Continentis.MainGame
@@ -17,20 +18,22 @@ namespace Continentis.MainGame
{
Positive,
Negative,
Neutral
Neutral,
Focusing
}
public abstract partial class BuffBase<T>
public abstract partial class BuffBase<T> : IPrioritized
{
public BuffType buffType;
public BuffDispelLevel dispelThreshold;
public ContentSubmodule contentSubmodule;
protected void Initialize(BuffType buffType, BuffDispelLevel dispelThreshold)
public int Priority { get; private set; }
protected void Initialize(BuffType buffType, BuffDispelLevel dispelThreshold, int priority = 0)
{
this.buffType = buffType;
this.dispelThreshold = dispelThreshold;
this.Priority = priority + (buffType == BuffType.Focusing ? 10000 : 0);
}
}
@@ -105,8 +108,8 @@ namespace Continentis.MainGame
}
/// <summary>
/// Buff被正常移除时调用。在Buff生命周期结束时调用。
/// UntriggerRemove不会调用此函数。但是此函数通常情况下绝不会被调用。
/// Buff被正常移除(包括驱散)时调用。在Buff生命周期结束时调用。
/// UntriggerRemove不会调用此函数。但是UntriggerRemove通常情况下绝不会被调用。
/// </summary>
public virtual void OnBuffRemove()
{

View File

@@ -38,12 +38,12 @@ namespace Continentis.MainGame
/// <param name="willLocalizeFuncText">是否本地化功能描述文本默认为true设为false说明此Buff具有不止一条本地化文本需要切换。</param>
public ContentSubmodule(BuffBase<T> buff, bool willLocalizeFuncText = true) : base(buff)
{
string className = buff.GetType().Name;
this.displayName = ("Buff_" + className + "_DisplayName").Localize();
string modClassName = ModManager.GetModClassName(buff.GetType());
this.displayName = ("Buff_" + modClassName + "_DisplayName").Localize();
if (willLocalizeFuncText)
{
this.originalFunctionText = ("Buff_" + className + "_FunctionText").Localize();
this.originalFunctionText = ("Buff_" + modClassName + "_FunctionText").Localize();
this.interpretedFunctionText = this.originalFunctionText;
}
@@ -83,7 +83,7 @@ namespace Continentis.MainGame
{
if (string.IsNullOrEmpty(iconID))
{
this.iconID = "BuffIcon_" + buff.GetType().Name;
this.iconID = "BuffIcon_" + ModManager.GetModClassName(buff.GetType());
}
else
{
@@ -294,7 +294,6 @@ namespace Continentis.MainGame
stackAmount = Mathf.Min(stackAmount, stackUpperLimit);
}
}
public void AddStack(int amount) => ModifyStack(amount);
public void ReduceStack(int amount) => ModifyStack(-amount);
public void ModifyStack(int amount)

View File

@@ -5,6 +5,7 @@ using Continentis.MainGame.Character;
using Continentis.MainGame.Commands;
using NUnit.Framework;
using SLSFramework.General;
using SLSFramework.UModAssistance;
using UnityEngine;
namespace Continentis.MainGame.Card
@@ -247,10 +248,26 @@ namespace Continentis.MainGame.Card
}
#endregion
#region CombatResoures
#region Buffs
public partial class CardLogicBase
{
/// <summary>
/// 创建一个角色战斗Buff实例
/// 注意此函数依赖ModManager的类型注册功能请确保在Mod加载时已注册对应Buff类型
/// 此函数中的T并不是原型参数而是获取Mod中注册的类型用的
/// </summary>
protected CharacterCombatBuffBase CreateCharacterBuff<T>(params object[] parameters) where T :CharacterCombatBuffBase
{
string buffTypeID = ModManager.GetTypeID(typeof(T));
if (string.IsNullOrEmpty(buffTypeID))
{
Debug.LogError($"Failed to get buff name for type {typeof(T).FullName}");
return null;
}
return ModManager.CreateInstance<CharacterCombatBuffBase>(buffTypeID, parameters);
}
}
#endregion
}

View File

@@ -5,7 +5,7 @@ using UnityEngine;
namespace Continentis.MainGame.Card
{
public partial class CombatBuffBase : CardBuffBase
public partial class CardCombatBuffBase : CardBuffBase
{
public CardLogicBase sourceCard;
@@ -13,14 +13,14 @@ namespace Continentis.MainGame.Card
public CountSubmodule combatRoundTimeSubmodule;
}
public partial class CombatBuffBase
public partial class CardCombatBuffBase
{
public sealed override bool OnBuffApply(out BuffBase<CardLogicBase> existingBuff)
{
throw new System.NotImplementedException("请使用类型约束更强的OnBuffApply方法");
}
public virtual bool OnBuffApply(out CombatBuffBase existingBuff)
public virtual bool OnBuffApply(out CardCombatBuffBase existingBuff)
{
throw new System.NotImplementedException(); //需要在子类中实现
}
@@ -61,7 +61,7 @@ namespace Continentis.MainGame.Card
}
}
public partial class CombatBuffBase
public partial class CardCombatBuffBase
{
protected bool FindExistingBuff<T>(out T existingBuff) where T : CardBuffBase
{
@@ -79,7 +79,7 @@ namespace Continentis.MainGame.Card
this.sourceCharacter = sourceCharacter;
this.sourceCard = sourceCard;
if (OnBuffApply(out CombatBuffBase existingBuff))
if (OnBuffApply(out CardCombatBuffBase existingBuff))
{
this.attachedCard.combatBuffSubmodule.buffList.Add(this);
OnAfterFirstApply();

View File

@@ -28,8 +28,9 @@ namespace Continentis.MainGame.Card
[CreateAssetMenu(menuName = "Continentis/MainGame/Card/CardData", fileName = "CardData")]
public partial class CardData : ScriptableObject
{
[FormerlySerializedAs("cardLogicClassName")] [Header("Fundamental")]
public string classFullName;
[Header("Fundamental")]
public string modName;
public string className;
public string displayName;
public Rarity cardRarity;
public CardType cardType;
@@ -64,9 +65,6 @@ namespace Continentis.MainGame.Card
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);
@@ -88,44 +86,6 @@ namespace Continentis.MainGame.Card
public partial class CardData
{
/// <summary>
/// 生成卡牌实例
/// </summary>
/// <param name="owner">卡牌持有者</param>
/// <param name="pileName">初始卡堆名称,默认为"Draw"</param>
/// <param name="index">插入位置默认为0</param>
public CardInstance GenerateCardInstance(ICardOwner owner, string pileName = "Draw", int index = -1)
{
CardInstance cardInstance = new CardInstance(GenerateCardLogic(), owner, pileName, index);
cardInstance.cardLogic.Initialize();
return cardInstance;
}
/// <summary>
/// 生成卡牌逻辑实例
/// </summary>
public CardLogicBase GenerateCardLogic()
{
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 '{classFullName}' not found or could not be instantiated.");
return null;
}
/// <summary>
/// 通过索引获取衍生卡牌数据

View File

@@ -78,7 +78,7 @@ namespace Continentis.MainGame.Card
}
else
{
Debug.LogError($"[CardUpgradeNode] Attempted to get upgrade attributes for a non-terminal node card {sourceCard.classFullName}.");
Debug.LogError($"[CardUpgradeNode] Attempted to get upgrade attributes for a non-terminal node card {sourceCard.className}.");
}
return upgradeAttributes;

View File

@@ -47,6 +47,36 @@ namespace Continentis.MainGame.Card
this.deck.Pile(cardLocation.pileName).Insert(index, this);
}
}
/// <summary>
/// 根据CardLogic生成卡牌实例
/// </summary>
/// <param name="logic">卡牌逻辑实例</param>
/// <param name="owner">卡牌持有者</param>
/// <param name="pileName">初始卡堆名称"</param>
/// <param name="index">插入位置默认为0</param>
public static CardInstance GenerateCardInstance(CardLogicBase logic, ICardOwner owner, string pileName, int index = -1)
{
CardInstance cardInstance = new CardInstance(logic, owner, pileName, index);
cardInstance.cardLogic.Initialize();
return cardInstance;
}
/// <summary>
/// 根据CardData生成卡牌实例
/// </summary>
/// <param name="data">卡牌数据</param>
/// <param name="owner">卡牌持有者</param>
/// <param name="pileName">初始卡堆名称"</param>
/// <param name="index">插入位置默认为0</param>
/// <returns></returns>
public static CardInstance GenerateCardInstance(CardData data, ICardOwner owner, string pileName, int index = -1)
{
CardInstance cardInstance = new CardInstance(CardLogicBase.GenerateCardLogic(data), owner, pileName, index);
cardInstance.cardLogic.Initialize();
return cardInstance;
}
public HandCardView GenerateHandCardView(string pileName, int index = -1)
{

View File

@@ -2,14 +2,17 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Character;
using Continentis.MainGame.Equipment;
using SLSFramework.General;
using SLSFramework.UModAssistance;
using UnityEngine;
namespace Continentis.MainGame.Card
{
public abstract partial class CardLogicBase
{
[Header("Reference")] public CardData cardData;
[Header("Reference")]
public CardData cardData;
public CardInstance cardInstance;
public ICardOwner owner => cardInstance.owner;
@@ -32,6 +35,31 @@ namespace Continentis.MainGame.Card
public PlaySubmodule playSubmodule { get; private set; }
public HashSet<CardLogicComponentBase> logicComponents { get; private set; }
/// <summary>
/// 生成卡牌逻辑实例
/// </summary>
public static CardLogicBase GenerateCardLogic(CardData data)
{
string typeID = ModManager.GetTypeID(data.modName, "Cards", data.className);
Type logicType = ModManager.GetType(typeID);
if(logicType == null)
{
Debug.LogError($"Card class '{typeID}' not found in assemblies.");
return null;
}
if (Activator.CreateInstance(logicType) is CardLogicBase cardLogic)
{
cardLogic.cardData = data;
cardLogic.Setup();
return cardLogic;
}
Debug.LogError($"Card class '{typeID}' not found or could not be instantiated.");
return null;
}
public void Setup()
{
this.cardID = Guid.NewGuid();
@@ -72,7 +100,7 @@ namespace Continentis.MainGame.Card
{
if (logicComponents.Any(component => component is T))
{
Debug.LogWarning($"Card {cardData.classFullName} already has component of type {typeof(T)}, cannot add duplicate.");
Debug.LogWarning($"Card {cardData.className} already has component of type {typeof(T)}, cannot add duplicate.");
return null;
}
else
@@ -99,7 +127,7 @@ namespace Continentis.MainGame.Card
cardInstance.DestroyHandCardView();
CardData newData = cardData.upgradeNode.upgradeCards[0]; //后续可改为选择升级方向
CardLogicBase newLogic = newData.GenerateCardLogic();
CardLogicBase newLogic = CardLogicBase.GenerateCardLogic(newData);
cardInstance.cardLogic = newLogic;
newLogic.cardInstance = cardInstance;
cardInstance.cardLogic.Initialize();

View File

@@ -7,22 +7,22 @@ namespace Continentis.MainGame.Card
{
public partial class CombatBuffSubmodule : SubmoduleBase<CardLogicBase>
{
public List<CombatBuffBase> buffList;
public List<CardCombatBuffBase> buffList;
public CombatBuffSubmodule(CardLogicBase owner) : base(owner)
{
buffList = new List<CombatBuffBase>();
buffList = new List<CardCombatBuffBase>();
}
}
public partial class CombatBuffSubmodule
{
public T GetBuff<T>() where T : CombatBuffBase
public T GetBuff<T>() where T : CardCombatBuffBase
{
return (T)buffList.Find(x => x.GetType() == typeof(T));
}
public bool HasBuff<T>() where T : CombatBuffBase
public bool HasBuff<T>() where T : CardCombatBuffBase
{
return buffList.Exists(x => x.GetType() == typeof(T));
}

View File

@@ -1,4 +1,5 @@
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
using SLSFramework.UModAssistance;
@@ -10,7 +11,25 @@ namespace Continentis.MainGame.Card
public class CardDataEditor : DataEditor
{
// 存储我们需要自定义绘制的属性的引用
private SerializedProperty classFullNameProp;
private SerializedProperty modNameProp;
private SerializedProperty classNameProp;
private SerializedProperty displayNameProp;
private SerializedProperty cardRarityProp;
private SerializedProperty cardTypeProp;
private SerializedProperty tagsProp;
private SerializedProperty cardSpriteProp;
private SerializedProperty functionTextProp;
private SerializedProperty cardDescriptionProp;
private SerializedProperty baseWeightProp;
private SerializedProperty variableAttributesProp;
private SerializedProperty originalAttributesProp;
private SerializedProperty runtimeCurrentAttributesProp;
private SerializedProperty upgradeNodeProp;
private SerializedProperty prefabsProp;
private SerializedProperty derivativeCardsProp;
private SerializedProperty derivativeCharactersProp;
@@ -20,7 +39,20 @@ namespace Continentis.MainGame.Card
base.OnEnable();
// 在启用时根据我们修改后的字段名找到对应的SerializedProperty
classFullNameProp = serializedObject.FindProperty("classFullName");
modNameProp = serializedObject.FindProperty("modName");
classNameProp = serializedObject.FindProperty("className");
displayNameProp = serializedObject.FindProperty("displayName");
cardRarityProp = serializedObject.FindProperty("cardRarity");
cardTypeProp = serializedObject.FindProperty("cardType");
tagsProp = serializedObject.FindProperty("tags");
cardSpriteProp = serializedObject.FindProperty("cardSprite");
functionTextProp = serializedObject.FindProperty("functionText");
cardDescriptionProp = serializedObject.FindProperty("cardDescription");
baseWeightProp = serializedObject.FindProperty("baseWeight");
variableAttributesProp = serializedObject.FindProperty("variableAttributes");
originalAttributesProp = serializedObject.FindProperty("originalAttributes");
runtimeCurrentAttributesProp = serializedObject.FindProperty("runtimeCurrentAttributes");
upgradeNodeProp = serializedObject.FindProperty("upgradeNode");
prefabsProp = serializedObject.FindProperty("prefabRefs");
derivativeCardsProp = serializedObject.FindProperty("derivativeCardDataRefs");
derivativeCharactersProp = serializedObject.FindProperty("derivativeCharacterDataRefs");
@@ -33,30 +65,42 @@ namespace Continentis.MainGame.Card
// --- 绘制自定义的Type选择器 ---
// 我们把它从所有自动绘制的属性中分离出来,放在最前面或最后面,让布局更清晰
EditorGUILayout.Space(); // 增加一点间距
EditorGUILayout.LabelField("Logic", EditorStyles.boldLabel);
if (DrawTypeSelectorGUI(classFullNameProp, "Card Logic Class", typeof(CardLogicBase), "Continentis.Mods", "Cards"))
EditorGUILayout.LabelField("Fundamental", EditorStyles.boldLabel);
if (DrawTypeSelectorGUI(classNameProp, "Card Logic Class", typeof(CardLogicBase), out Type outType, "Continentis.Mods", "Cards"))
{
string classFullName = classFullNameProp.stringValue;
string className = classNameProp.stringValue;
string modName = outType.Namespace!.Replace("Continentis.Mods.", "").Split('.')[0];
string displayName = "Card_" + modName + "_" + className + "_DisplayName";
string functionTextName = "Card_" + modName + "_" + className + "_FunctionText";
string displayName = "Card_" + classFullName + "_DisplayName";
SerializedProperty displayNameProp = serializedObject.FindProperty("displayName");
modNameProp.stringValue = modName;
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
});
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.PropertyField(modNameProp);
EditorGUILayout.PropertyField(classNameProp);
EditorGUILayout.PropertyField(displayNameProp);
EditorGUI.EndDisabledGroup();
EditorGUILayout.PropertyField(cardRarityProp);
EditorGUILayout.PropertyField(cardTypeProp);
EditorGUILayout.PropertyField(tagsProp, true);
EditorGUILayout.PropertyField(cardSpriteProp);
EditorGUILayout.PropertyField(functionTextProp);
EditorGUILayout.PropertyField(cardDescriptionProp);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Attributes", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(baseWeightProp);
EditorGUILayout.PropertyField(variableAttributesProp, true);
EditorGUILayout.PropertyField(originalAttributesProp, true);
EditorGUILayout.PropertyField(runtimeCurrentAttributesProp, true);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Upgrade", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(upgradeNodeProp);
// --- 绘制自定义的引用列表 ---
EditorGUILayout.Space();

View File

@@ -66,7 +66,7 @@ namespace Continentis.MainGame.Character
this.team = null;
break;
}
attributeSubmodule = new AttributeSubmodule(this);
equipmentSubmodule = new EquipmentSubmodule(this);
eventSubmodule = new EventSubmodule(this);

View File

@@ -1,11 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Card;
using Continentis.MainGame.UI;
using SLSFramework.General;
using UnityEngine;
namespace Continentis.MainGame.Character
{
public abstract partial class CombatBuffBase : CharacterBuffBase
public abstract partial class CharacterCombatBuffBase : CharacterBuffBase
{
public CardLogicBase sourceCard;
@@ -19,31 +21,32 @@ namespace Continentis.MainGame.Character
public StatusSubmodule statusSubmodule;
}
public partial class CombatBuffBase
public partial class CharacterCombatBuffBase
{
public sealed override bool OnBuffApply(out BuffBase<CharacterBase> existingBuff)
{
throw new System.NotImplementedException("请使用类型约束更强的OnBuffApply方法");
}
public virtual bool OnBuffApply(out CombatBuffBase existingBuff)
public virtual bool OnBuffApply(out CharacterCombatBuffBase existingBuff)
{
throw new System.NotImplementedException(); //需要在子类中实现
}
public override void OnAfterFirstApply()
{
statusSubmodule?.AddStatus();
}
public override void OnBuffRemove()
{
RefreshAttributes();
statusSubmodule?.RemoveStatus();
iconSubmodule?.Remove();
}
}
public partial class CombatBuffBase
public partial class CharacterCombatBuffBase
{
protected bool FindExistingSameBuff<T>(out T existingBuff) where T : CharacterBuffBase
{
@@ -61,9 +64,9 @@ namespace Continentis.MainGame.Character
this.sourceCharacter = sourceCharacter;
this.sourceCard = sourceCard;
if (OnBuffApply(out CombatBuffBase existingBuff))
if (OnBuffApply(out CharacterCombatBuffBase existingBuff))
{
this.attachedCharacter.combatBuffSubmodule.buffList.Add(this);
this.attachedCharacter.combatBuffSubmodule.buffList.AddByPriority(this);
OnAfterFirstApply();
@@ -92,9 +95,44 @@ namespace Continentis.MainGame.Character
{
this.attachedCharacter.combatBuffSubmodule.buffList.Remove(this);
}
/// <summary>
/// 检查并处理专注类Buff的添加逻辑。
/// 如果已有相同Buff存在则返回true并通过out参数返回该Buff。
/// 如果没有相同Buff存在则返回false并在必要时移除优先级最低的专注类Buff以腾出空间。
/// </summary>
/// <param name="existingBuff"></param>
/// <returns></returns>
public bool FocusingCheck(out CharacterCombatBuffBase existingBuff)
{
// 移除超出上限的专注类Buff
List<CharacterCombatBuffBase> focusingBuffs =
attachedCharacter.combatBuffSubmodule.buffList.Where(buff => buff.buffType == BuffType.Focusing).ToList();
focusingBuffs.Sort();
int maximumFocusingBuffAmount = attachedCharacter.GetAttribute("MaximumFocusingBuffAmount", 1);
for (int i = maximumFocusingBuffAmount; i < focusingBuffs.Count; i++)
{
focusingBuffs[i].Remove();
}
// 检查是否已有相同Buff存在
if (FindExistingSameBuff(out existingBuff))
{
return true;
}
// 如果没有相同Buff存在但已达上限则移除优先级最低的专注类Buff将由新Buff替代
if(focusingBuffs.Count >= maximumFocusingBuffAmount)
{
CharacterCombatBuffBase lowestPriorityBuff = focusingBuffs[focusingBuffs.Count - 1];
lowestPriorityBuff.Remove();
}
return false;
}
}
public partial class CombatBuffBase
public partial class CharacterCombatBuffBase
{
private void RefreshAttributes()
{

View File

@@ -15,14 +15,14 @@ namespace Continentis.MainGame.Character
foreach (string cardDataID in data.initialDeckRef)
{
ModManager.GetData<CardData>(cardDataID).GenerateCardInstance(this, initialPile);
CardInstance.GenerateCardInstance(ModManager.GetData<CardData>(cardDataID), this, initialPile);
}
foreach (EquipmentBase equipment in equipmentSubmodule.currentEquipments)
{
foreach (string cardDataID in equipment.equipmentData.belongingCardDataRefs)
{
ModManager.GetData<CardData>(cardDataID).GenerateCardInstance(this, initialPile);
CardInstance.GenerateCardInstance(ModManager.GetData<CardData>(cardDataID), this, initialPile);
}
}
}
@@ -224,16 +224,24 @@ namespace Continentis.MainGame.Character
/// <summary>
/// 添加格挡(格挡每回合结束后会清空)
/// </summary>
public void AddBlock(int baseBlock, CharacterBase target = null)
public void AddBlock(int baseBlock, bool applyOffsetAndModifier = true, CharacterBase target = null)
{
int baseBlockAfterOffset = baseBlock + GetAttribute("BlockGainOffset");
int finalBlock = Mathf.RoundToInt(baseBlockAfterOffset * GetRawAttribute("BlockGainMultiplier", 1));
target ??= this;
target.ModifyAttribute("Block", finalBlock);
if (!applyOffsetAndModifier)
{
target.ModifyAttribute("Block", baseBlock);
}
else
{
int baseBlockAfterOffset = baseBlock + GetAttribute("BlockGainOffset");
int finalBlock = Mathf.RoundToInt(baseBlockAfterOffset * GetRawAttribute("BlockGainMultiplier", 1));
target.ModifyAttribute("Block", finalBlock);
}
target.characterView.hudContainer.UpdateAllHUD();
}
/// <summary>
/// 添加闪避(闪避在回合结束后或被击中后清空)
/// </summary>

View File

@@ -8,22 +8,22 @@ namespace Continentis.MainGame.Character
{
public partial class CombatBuffSubmodule : SubmoduleBase<CharacterBase>
{
public List<CombatBuffBase> buffList;
public List<CharacterCombatBuffBase> buffList;
public CombatBuffSubmodule(CharacterBase character) : base(character)
{
buffList = new List<CombatBuffBase>();
buffList = new List<CharacterCombatBuffBase>();
}
}
public partial class CombatBuffSubmodule
{
public T GetBuff<T>() where T : CombatBuffBase
public T GetBuff<T>() where T : CharacterCombatBuffBase
{
return (T)buffList.Find(x => x.GetType() == typeof(T));
}
public bool HasBuff<T>() where T : CombatBuffBase
public bool HasBuff<T>() where T : CharacterCombatBuffBase
{
return buffList.Exists(x => x.GetType() == typeof(T));
}

View File

@@ -41,8 +41,17 @@ namespace Continentis.MainGame.Character
ExhaustPile.ForEach(c=>c.GenerateHandCardView(CombatUIManager.Instance.combatMainPage.exhaustPile));
}
public void DrawCards(int cardCount, float interval)
/// <summary>
/// 抽取指定数量的卡牌,返回一个包含抽牌指令的指令组。
/// </summary>
public CommandGroup DrawCards(int cardCount, float interval = 0.1f)
{
if (owner.statusSubmodule.HasStatus(StatusType.Heavy)) //沉重状态无法抽牌
{
MainGameManager.GenerateInfoText("Heavy: Can not draw cards", owner.characterView);
return new CommandGroup(ExecutionMode.Sequential);
}
if (cardCount > DrawPile.Count && DiscardPile.Count > 0)
{
Debug.Log("抽牌堆牌数不足,且弃牌堆有牌,正在洗牌...");
@@ -52,12 +61,23 @@ namespace Continentis.MainGame.Character
Debug.Log($"准备抽取 {cardCount} 张卡牌。");
CommandContext context = new CommandContext();
CommandQueueManager.Instance.AddCommand(new Cmd_DrawCards(this, cardCount, interval), context);
CommandQueueManager.Instance.AddCommand(new Cmd_Function(0, () =>
{
//Debug.Log((context.sharedInfo["DrawnCards"] as List<CardInstance>).Count); //TODO: 抽牌后的处理
}));
//return context.sharedInfo["DrawnCards"] as List<CardBase>;
CommandGroup drawCardsGroup = new CommandGroup(ExecutionMode.Sequential, context,
new Cmd_DrawCards(this, cardCount, interval),
new Cmd_Function(0, () =>
{
//Debug.Log((context.sharedInfo["DrawnCards"] as List<CardInstance>).Count); //TODO: 抽牌后的处理
}));
return drawCardsGroup;
}
/// <summary>
/// 从指令组的上下文中获取抽到的卡牌列表。
/// </summary>
public List<CardInstance> GetDrawnCards(CommandGroup drawCardsGroup)
{
CommandContext context = drawCardsGroup.groupContext;
return context.GetInfo<List<CardInstance>>("DrawnCards");
}
public void PlayCard(CardInstance card, List<CharacterBase> targetList)

View File

@@ -10,6 +10,7 @@ namespace Continentis.MainGame.Character
Silence = 1, //沉默,无法使用魔法牌 (Magic)
Disarm = 2, //缴械,无法使用攻击牌 (Attack)
Inhibition = 3, //抑制,无法使用能力牌 (Ability)
Heavy = 4, //沉重,无法再抽牌
//正面状态
Invincible = 1000, //无敌

View File

@@ -77,7 +77,7 @@ namespace Continentis.MainGame.Character
if (_haveCustomClassProp.boolValue)
{
// 如果勾选则显示class选择器
DrawTypeSelectorGUI(_classFullNameProp, "Character Class", typeof(CharacterBase), "Continentis.Mods", ".Characters");
DrawTypeSelectorGUI(_classFullNameProp, "Character Class", typeof(CharacterBase), out _, "Continentis.Mods", ".Characters");
}
else
{

View File

@@ -66,7 +66,7 @@ namespace Continentis.MainGame.Combat
SetViewPositions();
SetViewHUDs();
ModManager.CreateInstance<CombatBuffBase>("Basic_Weak", 2).Apply(enemies[0]);
ModManager.CreateInstance<CharacterCombatBuffBase>("Basic.Buffs.Weak", 2).Apply(enemies[0]);
}
public void AddCombatNPCs(params (CharacterData, Fraction)[] dataList)

View File

@@ -140,10 +140,15 @@ namespace Continentis.MainGame.Combat
CombatMainPage combatMainPage = CombatUIManager.Instance.combatMainPage;
playerHero.deckSubmodule.SetUpHandCardViews();
playerHero.combatBuffSubmodule.ActionStart();
combatMainPage.handPile.isUpdatingLayout = false;
playerHero.deckSubmodule.DrawCards(5, 0.1f);
CommandQueueManager.Instance.AddCommand(new Cmd_Function(0f, () => combatMainPage.handPile.isUpdatingLayout = true));
CommandQueueManager.Instance.AddCommand(playerHero.deckSubmodule.DrawCards(5));
CommandQueueManager.Instance.AddCommand(new Cmd_Function(0f, () =>
{
//Debug.Log((drawCard.groupContext.context["DrawnCards"] as List<CardInstance>).Count);
combatMainPage.handPile.isUpdatingLayout = true;
}));
combatMainPage.combatResourcesDisplayer.SetCharacter(playerHero);
@@ -195,6 +200,8 @@ namespace Continentis.MainGame.Combat
{
if (currentCharacter is PlayerHero playerHero)
{
playerHero.combatBuffSubmodule.ActionEnd();
Debug.Log(currentCharacter.data.className + " 结束行动,整理手牌。");
List<CardInstance> handPile = new List<CardInstance>(playerHero.deckSubmodule.HandPile);

View File

@@ -22,7 +22,7 @@ namespace Continentis.MainGame.Commands
private readonly List<CardInstance> customDrawCards;
public Cmd_DrawCards(DeckSubmodule deck, int drawCount, float interval)
public Cmd_DrawCards(DeckSubmodule deck, int drawCount, float interval = 0.1f)
{
this.isCustomDraw = false;
this.deck = deck;
@@ -31,7 +31,7 @@ namespace Continentis.MainGame.Commands
this.customDrawCards = null;
}
public Cmd_DrawCards(DeckSubmodule deck, List<CardInstance> customDrawCards, float interval)
public Cmd_DrawCards(DeckSubmodule deck, List<CardInstance> customDrawCards, float interval = 0.1f)
{
this.isCustomDraw = true;
this.deck = deck;

View File

@@ -1,4 +1,5 @@
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
using Continentis.MainGame.Card;
@@ -12,7 +13,6 @@ namespace Continentis.MainGame.Equipment
{
// Fundamental
private SerializedProperty _haveCustomClassProp;
private SerializedProperty _classFullNameProp;
private SerializedProperty _modNameProp;
private SerializedProperty _classNameProp;
private SerializedProperty _displayNameProp;
@@ -40,7 +40,6 @@ namespace Continentis.MainGame.Equipment
base.OnEnable();
// --- 在OnEnable中找到所有属性 ---
_haveCustomClassProp = serializedObject.FindProperty("haveCustomClass");
_classFullNameProp = serializedObject.FindProperty("classFullName");
_modNameProp = serializedObject.FindProperty("modName");
_classNameProp = serializedObject.FindProperty("className");
_displayNameProp = serializedObject.FindProperty("displayName");
@@ -75,14 +74,26 @@ namespace Continentis.MainGame.Equipment
if (_haveCustomClassProp.boolValue)
{
// 如果勾选则显示class选择器 (假设基类为EquipmentBase, 命名空间为.Equipments)
DrawTypeSelectorGUI(_classFullNameProp, "Equipment Class", typeof(EquipmentBase), "Continentis.Mods", ".Equipments");
if (DrawTypeSelectorGUI(_classNameProp, "Equipment Class", typeof(EquipmentBase), out Type outType, "Continentis.Mods", ".Equipments"))
{
string className = _classNameProp.stringValue;
string modName = outType.Namespace!.Replace("Continentis.Mods.", "").Split('.')[0];
string displayName = "Card_" + modName + "_" + className + "_DisplayName";
_modNameProp.stringValue = modName;
_displayNameProp.stringValue = displayName;
}
EditorGUI.BeginDisabledGroup(true);
}
else
EditorGUILayout.PropertyField(_modNameProp);
EditorGUILayout.PropertyField(_classNameProp);
EditorGUILayout.PropertyField(_displayNameProp);
if (_haveCustomClassProp.boolValue)
{
// 如果不勾选,则显示手动输入字段
EditorGUILayout.PropertyField(_modNameProp);
EditorGUILayout.PropertyField(_classNameProp);
EditorGUILayout.PropertyField(_displayNameProp);
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.PropertyField(_tagsProp, true);

View File

@@ -69,12 +69,13 @@ namespace Continentis.MainGame.Equipment
{
public static EquipmentBase GenerateEquipment(EquipmentData data, CharacterBase character = null)
{
Type logicType = data.haveCustomClass ? ModManager.GetType(data.classFullName) : typeof(EquipmentBase);
string typeID = "NoFunctionEquipment";
Type logicType = typeof(EquipmentBase);
if(logicType == null)
if (data.haveCustomClass)
{
Debug.LogError($"Card class '{data.classFullName}' not found in assemblies.");
return null;
typeID = ModManager.GetTypeID(data.modName, "Equipments", data.className);
logicType = ModManager.GetType(typeID);
}
if (Activator.CreateInstance(logicType) is EquipmentBase equipment)
@@ -85,7 +86,7 @@ namespace Continentis.MainGame.Equipment
return equipment;
}
Debug.LogError($"Failed to create equipment of type {data.classFullName}");
Debug.LogError($"Failed to create equipment of type {typeID}");
return null;
}
}

View File

@@ -15,7 +15,6 @@ namespace Continentis.MainGame.Equipment
{
[Header("Fundamental")]
public bool haveCustomClass;
public string classFullName;
public string modName;
public string className;
public string displayName;
@@ -43,12 +42,6 @@ namespace Continentis.MainGame.Equipment
//[Header("Upgrades")]
//public List<EquipmentData> upgrades;
}
public partial class EquipmentData
{
public string ModName => haveCustomClass ? classFullName.Split('_').First() : modName;
public string ClassName => haveCustomClass ? classFullName.Split('_').Last() : className;
}
public partial class EquipmentData
{

View File

@@ -60,6 +60,7 @@ namespace Continentis.MainGame
public partial class MainGameManager
{
public static void GenerateInfoText(string content, CombatCharacterViewBase characterView, Color color = default, float size = 1)
=> Instance.basePrefabs.GenerateInfoText(content, characterView, color, size);
}
}

View File

@@ -13,7 +13,7 @@ namespace Continentis.MainGame.Rules
{
base.Awake();
attributeRulesMerger = new AttributeRulesMerger();
attributeRulesMerger.rulesCollections.Add(ModManager.CreateInstance<AttributeRulesCollectionBase>("Basic_AttributeRulesCollection"));
attributeRulesMerger.rulesCollections.Add(ModManager.CreateInstance<AttributeRulesCollectionBase>("Basic.Rules.Basic_AttributeRulesCollection"));
attributeRulesMerger.ApplyRules();
}
}

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Continentis.MainGame.Character;
using Lean.Pool;
using SLSFramework.General;
using UnityEngine;
namespace Continentis.MainGame.UI
@@ -23,7 +24,8 @@ namespace Continentis.MainGame.UI
{
HUD_CharacterBuffIcon buffIcon = LeanPool.Spawn(buffIconPrefab, buffContainer).GetComponent<HUD_CharacterBuffIcon>();
buffIcon.Initialize(buff);
buffIcons.Add(buffIcon);
buffIcons.AddByPriority(buffIcon);
buffIcon.transform.SetSiblingIndex(buffIcons.IndexOf(buffIcon));
UpdateHud();
}

View File

@@ -1,33 +1,58 @@
using System;
using System.Collections.Generic;
using Continentis.MainGame.Character;
using DG.Tweening;
using Lean.Pool;
using SLSFramework.General;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace Continentis.MainGame.UI
{
public class HUD_CharacterBuffIcon : HUD_BaseIcon
public class HUD_CharacterBuffIcon : HUD_BaseIcon, IPrioritized
{
public int Priority { get; set; }
public CharacterBuffBase buff;
public Image buffTypeBackground;
public Image mainIcon;
public Sprite positive;
public Sprite negative;
public Sprite neutral;
public Sprite focusing;
public void Initialize(CharacterBuffBase buff)
{
this.buff = buff;
this.Priority = buff.Priority;
buff.iconSubmodule.buffIcon = this;
icon.sprite = buff.iconSubmodule.icon;
PlayApplyAnimation();
UpdateIcon();
}
public void PlayApplyAnimation()
{
Image spreadImage = LeanPool.Spawn(mainIcon.gameObject, rectTransform).GetComponent<Image>();
spreadImage.sprite = buff.iconSubmodule.icon;
spreadImage.rectTransform.localScale = Vector3.zero;
spreadImage.color = Color.white;
spreadImage.DOColor(new Color(1f, 1f, 1f, 0f), 1.1f).SetEase(Ease.Linear).Play();
spreadImage.rectTransform.DOScale(5f, 1.2f).SetEase(Ease.OutQuad).OnComplete(() =>
{
LeanPool.Despawn(spreadImage.gameObject);
}).Play();
}
public void PlayHintAnimation()
{
mainIcon.rectTransform.DOScale(1.25f, 0.2f).SetLoops(2, LoopType.Yoyo).SetEase(Ease.OutQuad).Play();
}
public override void UpdateIcon()
{
@@ -54,6 +79,7 @@ namespace Continentis.MainGame.UI
BuffType.Positive => positive,
BuffType.Negative => negative,
BuffType.Neutral => neutral,
BuffType.Focusing => focusing,
_ => buffTypeBackground.sprite
};
}