更新
This commit is contained in:
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a7e70a75f86711418ab773b6494763e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4cc14aaaad81bf3428dc9c19c1138a5d
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30d25484995a4fb45a05436995e9a0fa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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() { }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41e6e47005c77fb4994040c6b9763669
|
||||
Reference in New Issue
Block a user