This commit is contained in:
SoulliesOfficial
2026-04-01 12:23:27 -04:00
parent aff7ac0e03
commit c3b1561375
933 changed files with 114333 additions and 119360 deletions

View File

@@ -60,6 +60,16 @@ namespace Continentis.MainGame.Character
public partial class CombatBuffSubmodule
{
public void CombatStart()
{
buffList.For(buff => buff.eventSubmodule?.onCombatStart?.Invoke());
}
public void CombatEnd()
{
buffList.For(buff => buff.eventSubmodule?.onCombatEnd?.Invoke());
}
public void RoundStart()
{
buffList.For(buff => buff.roundCountSubmodule?.Update());
@@ -73,13 +83,13 @@ namespace Continentis.MainGame.Character
public void ActionStart()
{
Debug.Log($"{owner.data.displayName} is starting an action. Current action count this round: {owner.actionCountThisRound}");
//Debug.Log($"{owner.data.displayName} is starting an action. Current action count this round: {owner.actionCountThisRound}");
if (owner.actionCountThisRound == 0)
{
Debug.Log($"{owner.data.displayName} is starting their first action this round. Buff count of {buffList.Count} will update their round first action counts.");
//Debug.Log($"{owner.data.displayName} is starting their first action this round. Buff count of {buffList.Count} will update their round first action counts.");
buffList.For(buff =>
{
Debug.Log($"Updating round first action count for buff: {buff.contentSubmodule.displayName}");
//Debug.Log($"Updating round first action count for buff: {buff.contentSubmodule.displayName}");
buff.roundFirstActionCountSubmodule?.Update();
});
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6a7e70a75f86711418ab773b6494763e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -14,15 +14,17 @@ namespace Continentis.MainGame.Character
public DeckSubmodule(CharacterBase character) : base(character)
{
piles = new Dictionary<string, List<CardInstance>>();
piles.Add("Storage", new List<CardInstance>());
piles.Add("Hand", new List<CardInstance>());
piles.Add("Draw", new List<CardInstance>());
piles.Add("Discard", new List<CardInstance>());
piles.Add("Exhaust", new List<CardInstance>());
piles.Add("Grave", new List<CardInstance>());
piles.Add("Pool", new List<CardInstance>());
piles.Add("Intention", new List<CardInstance>());
piles = new Dictionary<string, List<CardInstance>>
{
{ Piles.Storage, new List<CardInstance>() },
{ Piles.Hand, new List<CardInstance>() },
{ Piles.Draw, new List<CardInstance>() },
{ Piles.Discard, new List<CardInstance>() },
{ Piles.Exhaust, new List<CardInstance>() },
{ Piles.Grave, new List<CardInstance>() },
{ Piles.Pool, new List<CardInstance>() },
{ Piles.Intention, new List<CardInstance>() }
};
}
}
@@ -51,11 +53,11 @@ namespace Continentis.MainGame.Character
if (cardCount > DrawPile.Count && DiscardPile.Count > 0)
{
Debug.Log("抽牌堆牌数不足,且弃牌堆有牌,正在洗牌...");
//Debug.Log("抽牌堆牌数不足,且弃牌堆有牌,正在洗牌...");
ReshuffleDeck();
}
Debug.Log($"准备抽取 {cardCount} 张卡牌。");
//Debug.Log($"准备抽取 {cardCount} 张卡牌。");
return new CommandGroup(ExecutionMode.Sequential,
new Cmd_DrawCards(this, cardCount, interval),
@@ -71,7 +73,7 @@ namespace Continentis.MainGame.Character
if (drawCardsGroup.groupContext.TryGet(CommandContextKeys.DrawnCards, out List<CardInstance> cards))
return cards;
Debug.LogWarning("[DeckSubmodule] groupContext 中未找到 DrawnCards。");
//Debug.LogWarning("[DeckSubmodule] groupContext 中未找到 DrawnCards。");
return new List<CardInstance>();
}
@@ -232,13 +234,13 @@ namespace Continentis.MainGame.Character
throw new KeyNotFoundException($"Pile '{pileName}' not found.");
}
public List<CardInstance> StoragePile => Pile("Storage");
public List<CardInstance> HandPile => Pile("Hand");
public List<CardInstance> DrawPile => Pile("Draw");
public List<CardInstance> DiscardPile => Pile("Discard");
public List<CardInstance> ExhaustPile => Pile("Exhaust");
public List<CardInstance> GravePile => Pile("Grave");
public List<CardInstance> PoolPile => Pile("Pool");
public List<CardInstance> IntentionPile => Pile("Intention");
public List<CardInstance> StoragePile => Pile(Piles.Storage);
public List<CardInstance> HandPile => Pile(Piles.Hand);
public List<CardInstance> DrawPile => Pile(Piles.Draw);
public List<CardInstance> DiscardPile => Pile(Piles.Discard);
public List<CardInstance> ExhaustPile => Pile(Piles.Exhaust);
public List<CardInstance> GravePile => Pile(Piles.Grave);
public List<CardInstance> PoolPile => Pile(Piles.Pool);
public List<CardInstance> IntentionPile => Pile(Piles.Intention);
}
}

View File

@@ -0,0 +1,16 @@
using UnityEngine;
namespace Continentis.MainGame
{
public static class Piles
{
public static string Storage = "Storage";
public static string Hand = "Hand";
public static string Draw = "Draw";
public static string Discard = "Discard";
public static string Exhaust = "Exhaust";
public static string Grave = "Grave";
public static string Pool = "Pool";
public static string Intention = "Intention";
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4cc14aaaad81bf3428dc9c19c1138a5d

View File

@@ -24,6 +24,9 @@ namespace Continentis.MainGame.Character
public OrderedDictionary<string, PrioritizedAction<CardInstance, List<CharacterBase>>> onBeforePlayCard; //使用卡牌前,参数为使用的卡牌
public OrderedDictionary<string, PrioritizedAction<CardInstance, List<CharacterBase>>> onAfterPlayCard; //使用卡牌后,参数为使用的卡牌
/// <summary>角色死亡时触发,供 Buff / 技能系统订阅响应</summary>
public OrderedDictionary<string, PrioritizedAction> onDeath; //角色死亡时
public EventSubmodule(CharacterBase character) : base(character)
{
onCombatStart = new OrderedDictionary<string, PrioritizedAction>();
@@ -42,6 +45,8 @@ namespace Continentis.MainGame.Character
onBeforePlayCard = new OrderedDictionary<string, PrioritizedAction<CardInstance, List<CharacterBase>>>();
onAfterPlayCard = new OrderedDictionary<string, PrioritizedAction<CardInstance, List<CharacterBase>>>();
onDeath = new OrderedDictionary<string, PrioritizedAction>();
onActionStart.InsertByPriority("StaminaRecover", new PrioritizedAction(() =>
{
owner.ModifyAttribute("Stamina", owner.GetAttribute("StaminaRecoverPerAction"));
@@ -89,15 +94,19 @@ namespace Continentis.MainGame.Character
public int blockedDamage; //格挡掉的伤害
public int shieldedDamage; //护盾吸收的伤害
public int hurtDamage; //实际受到的伤害
/// <summary>本次攻击的上下文,包含标签等扩展信息。</summary>
public AttackContext context;
public bool IsHurt => hurtDamage > 0; //是否实际受到伤害
public AttackResult(CharacterBase attacker, CharacterBase target, int startDamage, CardInstance attackCard, bool isDodged, int blocked, int shielded, int hurt)
public AttackResult(CharacterBase attacker, CharacterBase target, int startDamage, AttackContext context, bool isDodged, int blocked, int shielded, int hurt)
{
this.attacker = attacker;
this.target = target;
this.attackCard = attackCard;
this.attackCard = context?.sourceCard;
this.startDamage = startDamage;
this.context = context ?? new AttackContext();
this.isDodged = isDodged;
this.blockedDamage = blocked;
this.shieldedDamage = shielded;

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 30d25484995a4fb45a05436995e9a0fa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -2,12 +2,13 @@ using System;
using System.Collections.Generic;
using Continentis.MainGame.Card;
using SLSFramework.General;
using SoftCircuits.Collections;
using UnityEngine;
using UnityEngine.Events;
namespace Continentis.MainGame.Character
{
public class IntentionSubmodule : SubmoduleBase<CharacterBase>
public partial class IntentionSubmodule : SubmoduleBase<CharacterBase>
{
public List<IntentionBase> allIntentions;
public IntentionBase currentIntention;
@@ -15,12 +16,25 @@ namespace Continentis.MainGame.Character
public List<IntendedCard> intendedCards;
/// <summary>意图卡被移除后触发,参数为被移除的 IntendedCard 和它原来所在的索引。</summary>
public OrderedDictionary<string, PrioritizedAction<IntendedCard, int>> onIntendedCardRemoved;
/// <summary>意图卡被替换后触发,参数为旧 IntendedCard、新 IntendedCard 和所在的索引。</summary>
public OrderedDictionary<string, PrioritizedAction<IntendedCard, IntendedCard, int>> onIntendedCardReplaced;
/// <summary>意图卡被插入后触发,参数为新 IntendedCard 和插入的索引。</summary>
public OrderedDictionary<string, PrioritizedAction<IntendedCard, int>> onIntendedCardInserted;
public IntentionSubmodule(CharacterBase owner) : base(owner)
{
allIntentions = new List<IntentionBase>();
currentIntention = new IntentionBase(this);
getIntendedCards = owner.GetIntendedCards;
intendedCards = new List<IntendedCard>();
onIntendedCardRemoved = new OrderedDictionary<string, PrioritizedAction<IntendedCard, int>>();
onIntendedCardReplaced = new OrderedDictionary<string, PrioritizedAction<IntendedCard, IntendedCard, int>>();
onIntendedCardInserted = new OrderedDictionary<string, PrioritizedAction<IntendedCard, int>>();
}
}
@@ -71,6 +85,10 @@ namespace Continentis.MainGame.Character
}
/// <summary>NPC 本回合出牌前调用,可用于播放蓄力台词、切换动画状态等。</summary>
public virtual void PreAction() { }
/// <summary>NPC 本回合全部卡牌出完后调用,可用于播放结束台词、重置状态等。</summary>
public virtual void PostAction() { }
}
}

View File

@@ -0,0 +1,238 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Card;
using SLSFramework.General;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Continentis.MainGame.Character
{
public partial class IntentionSubmodule
{
// ──────────────────────────────────────────────────────────────
// 原子操作层
// ──────────────────────────────────────────────────────────────
/// <summary>移除指定位置的意图卡并销毁其视图。</summary>
/// <returns>被移除的 IntendedCard索引越界时返回 null。</returns>
public IntendedCard RemoveIntendedCardAt(int index)
{
if (!IsValidIndex(index)) return null;
IntendedCard removed = intendedCards[index];
removed.cardInstance.DestroyIntentionCardView();
intendedCards.RemoveAt(index);
onIntendedCardRemoved.Invoke(removed, index);
return removed;
}
/// <summary>替换指定位置的意图卡,销毁旧视图并生成新视图。</summary>
/// <returns>被替换掉的旧 IntendedCard索引越界时返回 null。</returns>
public IntendedCard ReplaceIntendedCardAt(int index, IntendedCard newCard)
{
if (!IsValidIndex(index)) return null;
if (newCard == null)
{
Debug.LogWarning("[IntentionSubmodule] ReplaceIntendedCardAt: newCard is null, performing remove instead.");
return RemoveIntendedCardAt(index);
}
IntendedCard oldCard = intendedCards[index];
oldCard.cardInstance.DestroyIntentionCardView();
intendedCards[index] = newCard;
newCard.cardInstance.GenerateIntentionCardView();
// 设置文本解析目标
if (newCard.targets.Count > 0)
{
newCard.cardInstance.Targeting(newCard.targets[0]);
newCard.cardInstance.contentSubmodule.dirtyMark = true;
}
onIntendedCardReplaced.Invoke(oldCard, newCard, index);
return oldCard;
}
/// <summary>在指定位置插入一张新意图卡并生成视图。</summary>
public void InsertIntendedCard(int index, IntendedCard newCard)
{
if (newCard == null)
{
Debug.LogWarning("[IntentionSubmodule] InsertIntendedCard: newCard is null, insertion skipped.");
return;
}
int clampedIndex = Mathf.Clamp(index, 0, intendedCards.Count);
intendedCards.Insert(clampedIndex, newCard);
newCard.cardInstance.GenerateIntentionCardView();
// 设置文本解析目标
if (newCard.targets.Count > 0)
{
newCard.cardInstance.Targeting(newCard.targets[0]);
newCard.cardInstance.contentSubmodule.dirtyMark = true;
}
onIntendedCardInserted.Invoke(newCard, clampedIndex);
}
/// <summary>在末尾追加一张新意图卡并生成视图。</summary>
public void AddIntendedCard(IntendedCard newCard)
{
InsertIntendedCard(intendedCards.Count, newCard);
}
// ──────────────────────────────────────────────────────────────
// 查询 / 工具层
// ──────────────────────────────────────────────────────────────
/// <summary>返回 intendedCards 中的随机索引,列表为空时返回 -1。</summary>
public int GetRandomIntendedCardIndex()
{
return intendedCards.Count == 0 ? -1 : Random.Range(0, intendedCards.Count);
}
/// <summary>
/// 使用过滤器从 intendedCards 中筛选出符合条件的意图卡索引列表。
/// </summary>
/// <param name="filter">返回 true 表示该意图卡通过筛选。</param>
public List<int> GetFilteredIntendedCardIndices(Func<IntendedCard, bool> filter)
{
List<int> result = new List<int>();
for (int i = 0; i < intendedCards.Count; i++)
{
if (filter(intendedCards[i]))
result.Add(i);
}
return result;
}
/// <summary>
/// 使用过滤器从 intendedCards 中随机选取一个符合条件的意图卡索引。
/// </summary>
/// <returns>随机索引,无符合条件的意图卡时返回 -1。</returns>
public int GetRandomFilteredIntendedCardIndex(Func<IntendedCard, bool> filter)
{
List<int> filtered = GetFilteredIntendedCardIndices(filter);
return filtered.Count == 0 ? -1 : filtered[Random.Range(0, filtered.Count)];
}
/// <summary>
/// 尝试从 PoolPile 中获取一张可替换的随机卡牌(排除当前已在 intendedCards 中的卡牌)。
/// </summary>
/// <param name="result">生成的 IntendedCard失败时为 null。</param>
/// <param name="checkAffordability">是否检查体力/法力消耗。</param>
/// <returns>是否成功找到可替换的卡牌。</returns>
public bool TryGetRandomReplacementCard(out IntendedCard result, bool checkAffordability = false)
{
result = null;
HashSet<CardInstance> currentCards = new HashSet<CardInstance>(
intendedCards.Select(ic => ic.cardInstance));
List<CardInstance> candidates = owner.deckSubmodule.PoolPile
.Where(card => !currentCards.Contains(card) && !card.weightSubmodule.forceIgnore)
.ToList();
if (checkAffordability)
{
int stamina = owner.GetAttribute(CharacterAttributes.Stamina);
int mana = owner.GetAttribute(CharacterAttributes.Mana);
candidates = candidates.Where(card =>
card.GetAttribute(CardAttributes.StaminaCost) <= stamina &&
card.GetAttribute(CardAttributes.ManaCost) <= mana).ToList();
}
if (candidates.Count == 0) return false;
CardInstance chosen = candidates[Random.Range(0, candidates.Count)];
if (!owner.CheckAvailabilityAndSetTargets(chosen, out List<CharacterBase> targets))
return false;
result = new IntendedCard(chosen, targets);
return true;
}
// ──────────────────────────────────────────────────────────────
// 组合操作层
// ──────────────────────────────────────────────────────────────
/// <summary>随机移除一张意图卡。</summary>
/// <returns>被移除的 IntendedCard列表为空时返回 null。</returns>
public IntendedCard RemoveRandomIntendedCard()
{
int index = GetRandomIntendedCardIndex();
return index < 0 ? null : RemoveIntendedCardAt(index);
}
/// <summary>
/// 随机移除一张符合过滤条件的意图卡。
/// </summary>
/// <param name="filter">返回 true 表示该意图卡可被移除。</param>
/// <returns>被移除的 IntendedCard无符合条件的意图卡时返回 null。</returns>
public IntendedCard RemoveRandomIntendedCard(Func<IntendedCard, bool> filter)
{
int index = GetRandomFilteredIntendedCardIndex(filter);
return index < 0 ? null : RemoveIntendedCardAt(index);
}
/// <summary>
/// 随机将一张意图卡替换为 PoolPile 中的另一张卡牌。
/// 若无可替换卡牌则降级为移除。
/// </summary>
/// <param name="checkAffordability">是否检查体力/法力消耗。</param>
/// <returns>操作是否成功执行(移除或替换均算成功)。</returns>
public bool ChangeRandomIntendedCard(bool checkAffordability = false)
{
int index = GetRandomIntendedCardIndex();
if (index < 0) return false;
if (TryGetRandomReplacementCard(out IntendedCard replacement, checkAffordability))
{
ReplaceIntendedCardAt(index, replacement);
}
else
{
RemoveIntendedCardAt(index);
}
return true;
}
/// <summary>
/// 随机将一张符合过滤条件的意图卡替换为 PoolPile 中的另一张卡牌。
/// 若无可替换卡牌则降级为移除。
/// </summary>
/// <param name="filter">返回 true 表示该意图卡可被替换。</param>
/// <param name="checkAffordability">是否检查体力/法力消耗。</param>
/// <returns>操作是否成功执行。</returns>
public bool ChangeRandomIntendedCard(Func<IntendedCard, bool> filter, bool checkAffordability = false)
{
int index = GetRandomFilteredIntendedCardIndex(filter);
if (index < 0) return false;
if (TryGetRandomReplacementCard(out IntendedCard replacement, checkAffordability))
{
ReplaceIntendedCardAt(index, replacement);
}
else
{
RemoveIntendedCardAt(index);
}
return true;
}
// ──────────────────────────────────────────────────────────────
// 内部工具
// ──────────────────────────────────────────────────────────────
private bool IsValidIndex(int index)
{
if (index >= 0 && index < intendedCards.Count) return true;
Debug.LogWarning($"[IntentionSubmodule] Index {index} out of range (count: {intendedCards.Count}).");
return false;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 41e6e47005c77fb4994040c6b9763669