2025-10-03 00:02:43 -04:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using Continentis.MainGame.Character;
|
2025-10-23 00:49:44 -04:00
|
|
|
|
using Continentis.MainGame.Combat;
|
2025-10-03 00:02:43 -04:00
|
|
|
|
using Continentis.MainGame.Commands;
|
2025-10-23 00:49:44 -04:00
|
|
|
|
using SLSFramework.General;
|
|
|
|
|
|
using SLSFramework.UModAssistance;
|
2025-10-03 00:02:43 -04:00
|
|
|
|
using UniRx;
|
2025-10-23 00:49:44 -04:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using Random = UnityEngine.Random;
|
2025-10-03 00:02:43 -04:00
|
|
|
|
|
|
|
|
|
|
namespace Continentis.MainGame.Card
|
|
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
public partial class CardInstance
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-23 00:49:44 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 选中目标时触发的效果,效果在所有逻辑组件的Targeting之前执行(在SetUp函数生成EventSubmodule的时候)。
|
|
|
|
|
|
/// 如果必须需要在逻辑组件之后执行,请重写Initialize函数。
|
|
|
|
|
|
/// </summary>
|
2025-11-15 12:17:34 -05:00
|
|
|
|
public virtual void Targeting(CharacterBase target)
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
eventSubmodule.onTargeting.Invoke(target);
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-23 00:49:44 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 取消选中目标时触发的效果,效果在所有逻辑组件的Untargeting之前执行(在SetUp函数生成EventSubmodule的时候)。
|
|
|
|
|
|
/// 如果必须需要在逻辑组件之后执行,请重写Initialize函数。
|
|
|
|
|
|
/// </summary>
|
2025-11-15 12:17:34 -05:00
|
|
|
|
public virtual void Untargeting()
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
eventSubmodule.onUntargeting.Invoke();
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-15 12:17:34 -05:00
|
|
|
|
public partial class CardInstance
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-23 00:49:44 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 刷新卡牌属性
|
|
|
|
|
|
/// </summary>
|
2025-10-03 00:02:43 -04:00
|
|
|
|
public void RefreshCardAttributes()
|
|
|
|
|
|
{
|
|
|
|
|
|
if(user == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
attributeSubmodule.RefreshAllAttributes();
|
2025-12-10 18:22:26 -05:00
|
|
|
|
Untargeting();
|
|
|
|
|
|
/*if ((handCardView == null && intentionCardView == null))
|
2025-10-23 00:49:44 -04:00
|
|
|
|
{
|
2025-12-10 18:22:26 -05:00
|
|
|
|
Untargeting();
|
2025-10-23 00:49:44 -04:00
|
|
|
|
}
|
2025-12-10 18:22:26 -05:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (handCardView != null && !handCardView.isSelecting)
|
|
|
|
|
|
{
|
|
|
|
|
|
Untargeting();
|
|
|
|
|
|
}
|
|
|
|
|
|
}*/
|
2025-11-08 09:50:55 -05:00
|
|
|
|
|
|
|
|
|
|
contentSubmodule.dirtyMark = true;
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-23 00:49:44 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据卡牌内容应用属性变化
|
|
|
|
|
|
/// </summary>
|
2025-11-15 12:17:34 -05:00
|
|
|
|
public void ApplyAttributeChangesByCard()
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
cardLogic.ApplyAttributeChangesByCard();
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-15 12:17:34 -05:00
|
|
|
|
public partial class CardInstance
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-23 00:49:44 -04:00
|
|
|
|
public virtual void DetectTargetsValidity(out List<CharacterBase> valid, out List<CharacterBase> notMet, out List<CharacterBase> invalid)
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-23 00:49:44 -04:00
|
|
|
|
List<CharacterBase> characters = CombatMainManager.Instance.characterController.characters;
|
2025-10-30 12:07:59 -04:00
|
|
|
|
invalid = new List<CharacterBase>(characters);
|
2025-10-23 00:49:44 -04:00
|
|
|
|
notMet = new List<CharacterBase>();
|
2025-10-03 00:02:43 -04:00
|
|
|
|
valid = new List<CharacterBase>();
|
|
|
|
|
|
|
|
|
|
|
|
int targetCount = attributeSubmodule.targetCount;
|
|
|
|
|
|
|
|
|
|
|
|
if (targetCount <= -2)
|
|
|
|
|
|
{
|
2025-10-30 12:07:59 -04:00
|
|
|
|
Debug.LogError("Invalid target count setting on card: " + contentSubmodule.cardName);
|
2025-10-23 00:49:44 -04:00
|
|
|
|
return;
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
2025-10-30 12:07:59 -04:00
|
|
|
|
|
|
|
|
|
|
if (HasKeyword("TargetAll"))
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-30 12:07:59 -04:00
|
|
|
|
valid.AddRange(characters);
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
2025-10-30 12:07:59 -04:00
|
|
|
|
else
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-11-12 01:20:19 -06:00
|
|
|
|
if (HasKeyword("TargetAllies") || HasKeyword("Blessing"))
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-30 12:07:59 -04:00
|
|
|
|
valid.AddRange(user.fraction is Fraction.Ally or Fraction.Player
|
|
|
|
|
|
? characters.Where(character => character.fraction is Fraction.Ally or Fraction.Player)
|
|
|
|
|
|
: characters.Where(character => character.fraction == user.fraction));
|
|
|
|
|
|
valid.Remove(user);
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
2025-10-30 12:07:59 -04:00
|
|
|
|
|
|
|
|
|
|
if (HasKeyword("TargetSelf"))
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-30 12:07:59 -04:00
|
|
|
|
valid.Add(user);
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-30 12:07:59 -04:00
|
|
|
|
if (HasKeyword("TargetEnemies"))
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-30 12:07:59 -04:00
|
|
|
|
valid.AddRange(user.fraction is Fraction.Ally or Fraction.Player
|
|
|
|
|
|
? characters.Where(character => character.fraction is Fraction.Enemy or Fraction.Neutral)
|
|
|
|
|
|
: characters.Where(character => character.fraction != user.fraction));
|
|
|
|
|
|
|
|
|
|
|
|
//处理保护,嘲讽等
|
|
|
|
|
|
if (targetCount != -1)
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-30 12:07:59 -04:00
|
|
|
|
List<CharacterBase> protectedTargets = valid.Where(target => target.statusSubmodule.HasStatus(StatusType.Protected)).ToList();
|
2025-10-23 00:49:44 -04:00
|
|
|
|
notMet.AddRange(protectedTargets);
|
2025-10-30 12:07:59 -04:00
|
|
|
|
valid.RemoveRange(protectedTargets);
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-30 12:07:59 -04:00
|
|
|
|
|
|
|
|
|
|
foreach (CharacterBase validTarget in valid)
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-30 12:07:59 -04:00
|
|
|
|
invalid.Remove(validTarget);
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-23 00:49:44 -04:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-10-03 00:02:43 -04:00
|
|
|
|
|
|
|
|
|
|
public virtual bool CheckBeforePlay()
|
|
|
|
|
|
{
|
2025-10-23 00:49:44 -04:00
|
|
|
|
if (!user.CheckEnoughStamina(GetAttribute("StaminaCost")))
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-23 00:49:44 -04:00
|
|
|
|
MainGameManager.Instance.basePrefabs.GenerateInfoText("Not Enough Stamina", user.characterView);
|
2025-10-03 00:02:43 -04:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-23 00:49:44 -04:00
|
|
|
|
if (!user.CheckEnoughMana(GetAttribute("ManaCost")))
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-10-23 00:49:44 -04:00
|
|
|
|
MainGameManager.Instance.basePrefabs.GenerateInfoText("Not Enough Mana", user.characterView);
|
2025-10-03 00:02:43 -04:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 打出卡牌
|
|
|
|
|
|
/// 注意,这个函数内部包含了命令队列的调用
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="targetList">目标列表</param>
|
|
|
|
|
|
/// <param name="user">使用者</param>
|
|
|
|
|
|
/// <param name="willCheckBeforePlay">打出之前是否进行可用性检测</param>
|
|
|
|
|
|
public bool Play(List<CharacterBase> targetList, CharacterBase user = null, bool willCheckBeforePlay = true)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (handCardView != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (handCardView.isDuringPlaying)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
handCardView.isDuringPlaying = true;
|
|
|
|
|
|
}
|
2025-10-23 00:49:44 -04:00
|
|
|
|
|
2025-11-15 12:17:34 -05:00
|
|
|
|
this.user = user ?? CombatMainManager.Instance.currentCharacter;
|
|
|
|
|
|
this.user.recordSubmodule.RecordCardPlay(this);
|
2025-10-03 00:02:43 -04:00
|
|
|
|
|
|
|
|
|
|
if (!willCheckBeforePlay || CheckBeforePlay())
|
|
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
this.user.ModifyStamina(-GetAttribute("StaminaCost"));
|
|
|
|
|
|
this.user.ModifyMana(-GetAttribute("ManaCost"));
|
2025-10-23 00:49:44 -04:00
|
|
|
|
|
|
|
|
|
|
Debug.Log($"Starting to play card: {contentSubmodule.cardName}");
|
2025-10-03 00:02:43 -04:00
|
|
|
|
|
2025-10-23 00:49:44 -04:00
|
|
|
|
CommandQueueManager.Instance.AddCommand(new Cmd_Function(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
playSubmodule.isDuringPlayEffect = true;
|
|
|
|
|
|
eventSubmodule.onBeforePlay.Invoke(targetList);
|
2025-11-15 12:17:34 -05:00
|
|
|
|
this.user.eventSubmodule?.onBeforePlayCard.Invoke(this, targetList);
|
|
|
|
|
|
this.user.combatBuffSubmodule.buffList.For(buff =>
|
2025-10-23 00:49:44 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
buff.eventSubmodule?.onBeforePlayCard.Invoke(this, targetList);
|
2025-10-23 00:49:44 -04:00
|
|
|
|
});
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
2025-11-08 09:50:55 -05:00
|
|
|
|
CommandQueueManager.Instance.AddCommands(PlayEffect(targetList));
|
2025-11-15 12:17:34 -05:00
|
|
|
|
CommandQueueManager.Instance.AddCommands(cardLogic.PlayEffect(targetList));
|
2025-10-23 00:49:44 -04:00
|
|
|
|
CommandQueueManager.Instance.AddCommand(new Cmd_Function(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
eventSubmodule.onAfterPlay.Invoke(targetList);
|
|
|
|
|
|
combatBuffSubmodule.buffList.For(buff => buff.usageSubmodule?.UpdateModule());
|
2025-11-15 12:17:34 -05:00
|
|
|
|
this.user.eventSubmodule.onAfterPlayCard.Invoke(this, targetList);
|
|
|
|
|
|
this.user.combatBuffSubmodule.buffList.For(buff =>
|
2025-10-23 00:49:44 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
buff.eventSubmodule?.onAfterPlayCard.Invoke(this, targetList);
|
2025-10-23 00:49:44 -04:00
|
|
|
|
});
|
|
|
|
|
|
AfterPlayEffect(targetList);
|
2025-11-15 12:17:34 -05:00
|
|
|
|
cardLogic.AfterPlayEffect(targetList);
|
2025-10-23 00:49:44 -04:00
|
|
|
|
playSubmodule.isDuringPlayEffect = false;
|
2025-11-15 09:08:36 -05:00
|
|
|
|
if (handCardView != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
handCardView.isDuringPlaying = false;
|
|
|
|
|
|
}
|
2025-10-23 00:49:44 -04:00
|
|
|
|
}));
|
2025-10-03 00:02:43 -04:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (handCardView != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
handCardView.isDuringPlaying = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-08 09:50:55 -05:00
|
|
|
|
protected virtual List<CommandBase> PlayEffect(List<CharacterBase> targetList)
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
return new List<CommandBase>();
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected virtual void AfterPlayEffect(List<CharacterBase> targetList)
|
|
|
|
|
|
{
|
2025-10-30 23:31:29 -04:00
|
|
|
|
if (contentSubmodule.cardType == CardType.Power)
|
|
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
CommandQueueManager.Instance.AddCommand(user.deckSubmodule.UsePowerCard(this));
|
2025-10-30 23:31:29 -04:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-01 06:13:58 -04:00
|
|
|
|
if (HasKeyword("Exhaust"))
|
|
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
CommandQueueManager.Instance.AddCommand(user.deckSubmodule.ExhaustCard(this));
|
2025-11-01 06:13:58 -04:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(HasKeyword("Exhaustible"))
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
if (!HasAttribute("ExhaustibleCount"))
|
2025-11-01 06:13:58 -04:00
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Exhaustible card missing ExhaustibleCount attribute: " + contentSubmodule.cardName);
|
2025-11-15 12:17:34 -05:00
|
|
|
|
CommandQueueManager.Instance.AddCommand(user.deckSubmodule.ExhaustCard(this));
|
2025-11-01 06:13:58 -04:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-15 12:17:34 -05:00
|
|
|
|
ModifyAttribute("ExhaustibleCount", -1);
|
2025-11-01 06:13:58 -04:00
|
|
|
|
|
2025-11-15 12:17:34 -05:00
|
|
|
|
if(GetAttribute("ExhaustibleCount") <= 0)
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
CommandQueueManager.Instance.AddCommand(user.deckSubmodule.ExhaustCard(this));
|
2025-11-01 06:13:58 -04:00
|
|
|
|
return;
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
2025-11-01 06:13:58 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (user is PlayerHero playerHero)
|
|
|
|
|
|
{
|
2025-11-12 00:17:30 -06:00
|
|
|
|
if(!HasKeyword("Reuseable"))
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
CommandQueueManager.Instance.AddCommand(playerHero.deckSubmodule.DiscardCard(this, false));
|
2025-10-23 00:49:44 -04:00
|
|
|
|
CommandQueueManager.Instance.AddCommand(new Cmd_Function(() =>
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
|
|
|
|
|
if (handCardView != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
handCardView.isDuringPlaying = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (user is CombatNPC npc)
|
|
|
|
|
|
{
|
2025-11-01 06:13:58 -04:00
|
|
|
|
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-15 12:17:34 -05:00
|
|
|
|
public partial class CardInstance
|
2025-10-03 00:02:43 -04:00
|
|
|
|
{
|
2025-11-15 12:17:34 -05:00
|
|
|
|
public void UpgradeCard()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (owner is not CombatTeam)
|
|
|
|
|
|
{
|
|
|
|
|
|
KeyValuePair<string, List<CardInstance>> currentPile = deck.GetCardLocation(this, out int index);
|
|
|
|
|
|
if (!cardData.upgradeNode.isTerminalNode)
|
|
|
|
|
|
{
|
|
|
|
|
|
DestroyHandCardView();
|
2025-10-23 00:49:44 -04:00
|
|
|
|
|
2025-11-15 12:17:34 -05:00
|
|
|
|
CardData newData = cardData.upgradeNode.upgradeCards[0]; //后续可改为选择升级方向
|
|
|
|
|
|
CardLogicBase newLogic = CardLogicBase.GenerateCardLogic(newData);
|
|
|
|
|
|
this.cardLogic = newLogic;
|
|
|
|
|
|
newLogic.card = this;
|
|
|
|
|
|
this.cardLogic.Initialize(this);
|
|
|
|
|
|
if (user is PlayerHero)
|
|
|
|
|
|
GenerateHandCardView(CombatUIManager.Instance.combatMainPage.Pile(currentPile.Key), index);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-03 00:02:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|