546 lines
21 KiB
C#
546 lines
21 KiB
C#
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Continentis.MainGame.Character;
|
||
using Continentis.MainGame.Combat;
|
||
using Continentis.MainGame.UI;
|
||
using SLSUtilities.General;
|
||
using UnityEngine;
|
||
using UnityEngine.EventSystems;
|
||
using UnityEngine.InputSystem;
|
||
|
||
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>();
|
||
|
||
#region Multi-Target Selection State
|
||
|
||
/// <summary>是否正处于多目标逐次点击选择模式。</summary>
|
||
private bool isMultiTargetSelecting;
|
||
|
||
/// <summary>多目标模式下已选中的目标列表(可含重复)。</summary>
|
||
private readonly List<CharacterBase> multiTargetSelectedList = new List<CharacterBase>();
|
||
|
||
/// <summary>多目标模式需要的总选择次数。</summary>
|
||
private int multiTargetRequired;
|
||
|
||
/// <summary>多目标模式下卡牌固定显示的位置(箭头起点)。</summary>
|
||
private Vector3 multiTargetCardAnchor;
|
||
|
||
#endregion
|
||
|
||
protected override void Update()
|
||
{
|
||
base.Update();
|
||
|
||
if (isMultiTargetSelecting)
|
||
{
|
||
HandleMultiTargetInput();
|
||
}
|
||
}
|
||
|
||
public override void OnPointerEnter(PointerEventData eventData)
|
||
{
|
||
base.OnPointerEnter(eventData);
|
||
card.contentSubmodule.dirtyMark = true;
|
||
if (CombatUIManager.Instance.selectingCardView == null)
|
||
{
|
||
canvas.overrideSorting = true;
|
||
canvas.sortingLayerName = "UI";
|
||
canvas.sortingOrder = 10;
|
||
}
|
||
}
|
||
|
||
public override void OnPointerExit(PointerEventData eventData)
|
||
{
|
||
base.OnPointerExit(eventData);
|
||
|
||
if (!isSelecting)
|
||
{
|
||
canvas.overrideSorting = false;
|
||
canvas.sortingOrder = 0;
|
||
}
|
||
}
|
||
|
||
public void OnPointerClick(PointerEventData eventData)
|
||
{
|
||
if (CombatUIManager.Instance.combatMainPage.handCardSelector.gameObject.activeInHierarchy)
|
||
{
|
||
if(isOccupied) return;
|
||
|
||
if (CombatUIManager.Instance.combatMainPage.handCardSelector.selectedCards.Contains(card))
|
||
{
|
||
CombatUIManager.Instance.combatMainPage.handCardSelector.Deselect(this);
|
||
}
|
||
else
|
||
{
|
||
CombatUIManager.Instance.combatMainPage.handCardSelector.Select(this);
|
||
}
|
||
}
|
||
else if (CombatUIManager.Instance.combatMainPage.customCardSelector.gameObject.activeInHierarchy)
|
||
{
|
||
if(isOccupied) return;
|
||
|
||
if (CombatUIManager.Instance.combatMainPage.customCardSelector.selectedCards.Contains(card))
|
||
{
|
||
CombatUIManager.Instance.combatMainPage.customCardSelector.Deselect(this);
|
||
}
|
||
else
|
||
{
|
||
CombatUIManager.Instance.combatMainPage.customCardSelector.Select(this);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void OnBeginDrag(PointerEventData eventData)
|
||
{
|
||
isSelecting = true;
|
||
CombatUIManager.Instance.selectingCardView = this;
|
||
|
||
card.user = CombatMainManager.Instance.currentCharacter;
|
||
card.DetectTargetsValidity(out validTargets, out conditionNotMetTargets, out invalidTargets);
|
||
|
||
int targetCount = card.attributeSubmodule.targetCount;
|
||
|
||
if (targetCount > 1 && !card.HasKeyword(CardKeywords.RandomTarget))
|
||
{
|
||
// 多目标手动选择模式:先执行通用的可打出校验
|
||
if (!CheckCanStartPlay())
|
||
{
|
||
ResetSelectionState();
|
||
return;
|
||
}
|
||
EnterMultiTargetMode(targetCount);
|
||
}
|
||
else if (targetCount == 1)
|
||
{
|
||
CombatUIManager.Instance.arrowsPage.GeneratePointerArrow(cardTransform.position, cardTransform.position, true);
|
||
}
|
||
else if (targetCount == -1)
|
||
{
|
||
CombatUIManager.Instance.arrowsPage.GeneratePointerArrow(cardTransform.position, cardTransform.position, true);
|
||
|
||
for (int i = 0; i < validTargets.Count + conditionNotMetTargets.Count - 1; i++)
|
||
{
|
||
CombatUIManager.Instance.arrowsPage.GeneratePointerArrow(cardTransform.position, cardTransform.position, false);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void OnDrag(PointerEventData eventData)
|
||
{
|
||
// 多目标模式下拖拽无效,交互由 Update 中的点击处理
|
||
if (isMultiTargetSelecting) return;
|
||
|
||
RectTransform arrowCanvasRect = CombatUIManager.Instance.arrowsPage.rectTransform;
|
||
Camera uiCamera = CombatUIManager.Instance.uiCamera;
|
||
Camera worldCamera = CombatUIManager.Instance.combatCamera;
|
||
|
||
// 先检查悬停的视图是否和当前记录的目标视图发生了变化
|
||
if (CombatUIManager.Instance.hoveringCharacterView != currentTargetingCharacterView)
|
||
{
|
||
// 如果悬停视图不是空的,说明鼠标移动到了一个新的目标上
|
||
if (CombatUIManager.Instance.hoveringCharacterView != null)
|
||
{
|
||
card.Targeting(CombatUIManager.Instance.hoveringCharacterView.character);
|
||
currentTargetingCharacterView = CombatUIManager.Instance.hoveringCharacterView;
|
||
}
|
||
else // 悬停视图是空的,说明鼠标离开了之前的目标
|
||
{
|
||
card.Untargeting();
|
||
currentTargetingCharacterView = null;
|
||
}
|
||
|
||
card.contentSubmodule.dirtyMark = true;
|
||
}
|
||
|
||
Vector3 startPosition = cardTransform.position;
|
||
Vector3 endPosition = SpaceConverter.ScreenPointToUIPoint(arrowCanvasRect, eventData.position, uiCamera);
|
||
PointerArrow mainPointerArrow = CombatUIManager.Instance.arrowsPage.mainPointerArrow;
|
||
|
||
CombatCharacterViewBase hoveringCharacterView = CombatUIManager.Instance.hoveringCharacterView;
|
||
CharacterBase hoveringCharacter = hoveringCharacterView != null ? hoveringCharacterView.character : null;
|
||
|
||
if (card.attributeSubmodule.targetCount == 1)
|
||
{
|
||
mainPointerArrow.SetArrow(startPosition, endPosition);
|
||
|
||
if (hoveringCharacter != null)
|
||
{
|
||
if (validTargets.Contains(hoveringCharacter))
|
||
{
|
||
mainPointerArrow.SetColor(Color.white);
|
||
}
|
||
else if (conditionNotMetTargets.Contains(hoveringCharacter))
|
||
{
|
||
mainPointerArrow.SetColor(Color.yellow);
|
||
}
|
||
else if (invalidTargets.Contains(hoveringCharacter))
|
||
{
|
||
mainPointerArrow.SetColor(Color.red);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
mainPointerArrow.SetColor(Color.clear);
|
||
}
|
||
}
|
||
else if (card.attributeSubmodule.targetCount == -1)
|
||
{
|
||
mainPointerArrow.SetArrow(startPosition, endPosition);
|
||
|
||
if (validTargets.Count > 0)
|
||
{
|
||
if (hoveringCharacter != null)
|
||
{
|
||
mainPointerArrow.SetColor(Color.clear);
|
||
}
|
||
|
||
if (validTargets.Contains(hoveringCharacter))
|
||
{
|
||
mainPointerArrow.SetColor(Color.white);
|
||
}
|
||
else if (conditionNotMetTargets.Contains(hoveringCharacter))
|
||
{
|
||
mainPointerArrow.SetColor(Color.yellow);
|
||
}
|
||
else if (invalidTargets.Contains(hoveringCharacter))
|
||
{
|
||
mainPointerArrow.SetColor(Color.red);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
mainPointerArrow.SetColor(Color.red);
|
||
return;
|
||
}
|
||
|
||
if (hoveringCharacter != null && (validTargets.Contains(hoveringCharacter) || conditionNotMetTargets.Contains(hoveringCharacter)))
|
||
{
|
||
List<CharacterBase> validTargetsExceptHovering =
|
||
validTargets.FindAll(target => target != hoveringCharacter);
|
||
|
||
List<CharacterBase> notMetTargetsExceptHovering =
|
||
conditionNotMetTargets.FindAll(target => target != hoveringCharacter);
|
||
|
||
for (int i = 0; i < validTargetsExceptHovering.Count; i++)
|
||
{
|
||
PointerArrow arrow = CombatUIManager.Instance.arrowsPage.otherPointerArrows[i];
|
||
CharacterBase target = validTargetsExceptHovering[i];
|
||
Vector3 targetViewPosition = target.characterView.transform.position;
|
||
arrow.gameObject.SetActive(true);
|
||
Vector3 otherEndPosition = SpaceConverter.WorldPointToUIPoint(arrowCanvasRect, targetViewPosition, worldCamera, uiCamera);
|
||
arrow.SetArrow(startPosition, otherEndPosition);
|
||
arrow.SetColor(Color.white);
|
||
}
|
||
|
||
for (int i = validTargetsExceptHovering.Count; i < validTargetsExceptHovering.Count + notMetTargetsExceptHovering.Count; i++)
|
||
{
|
||
PointerArrow arrow = CombatUIManager.Instance.arrowsPage.otherPointerArrows[i];
|
||
CharacterBase target = notMetTargetsExceptHovering[i];
|
||
Vector3 targetViewPosition = target.characterView.transform.position;
|
||
arrow.gameObject.SetActive(true);
|
||
Vector3 otherEndPosition = SpaceConverter.WorldPointToUIPoint(arrowCanvasRect, targetViewPosition, worldCamera, uiCamera);
|
||
arrow.SetArrow(startPosition, otherEndPosition);
|
||
arrow.SetColor(Color.yellow);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
CombatUIManager.Instance.arrowsPage.otherPointerArrows.ForEach(arrow => { arrow.gameObject.SetActive(false); });
|
||
}
|
||
}
|
||
else if (card.attributeSubmodule.targetCount == 0
|
||
|| card.HasKeyword(CardKeywords.RandomTarget))
|
||
{
|
||
Vector3 cardPosition = SpaceConverter.ScreenPointToUIPoint(arrowCanvasRect, eventData.position, uiCamera);
|
||
cardTransform.position = cardPosition;
|
||
}
|
||
}
|
||
|
||
public void OnEndDrag(PointerEventData eventData)
|
||
{
|
||
// 多目标模式下,拖拽结束只是从拖拽过渡到点击模式,不做打出判定
|
||
if (isMultiTargetSelecting) return;
|
||
|
||
CombatCharacterViewBase hoveringCharacterView = CombatUIManager.Instance.hoveringCharacterView;
|
||
CharacterBase hoveringCharacter = hoveringCharacterView != null ? hoveringCharacterView.character : null;
|
||
Camera uiCamera = CombatUIManager.Instance.uiCamera;
|
||
CombatUIManager.Instance.arrowsPage.ClearPointerArrows();
|
||
|
||
if (isSelecting)
|
||
{
|
||
isSelecting = false;
|
||
isHovering = false;
|
||
CombatUIManager.Instance.selectingCardView = null;
|
||
canvas.overrideSorting = false;
|
||
canvas.sortingOrder = 0;
|
||
card.eventSubmodule.onUntargeting();
|
||
card.contentSubmodule.dirtyMark = true;
|
||
}
|
||
else
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!CheckCanStartPlay()) return;
|
||
|
||
if (!card.HasKeyword("TargetSelf") && !card.HasKeyword(CardKeywords.RandomTarget))
|
||
{
|
||
if (!validTargets.Contains(hoveringCharacter))
|
||
{
|
||
// 当前鼠标悬停的目标不在有效目标列表中
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 根据目标类型,打出卡牌
|
||
if (card.attributeSubmodule.targetCount == 1)
|
||
{
|
||
if (hoveringCharacter != null && validTargets.Contains(hoveringCharacter))
|
||
{
|
||
if (!card.Play(new List<CharacterBase>() { hoveringCharacter }))
|
||
{
|
||
card.eventSubmodule.onUntargeting();
|
||
card.contentSubmodule.dirtyMark = true;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
RectTransform dropZone = CombatUIManager.Instance.combatMainPage.dropZone;
|
||
bool isInDropZone = RectTransformUtility.RectangleContainsScreenPoint(dropZone, eventData.position, uiCamera);
|
||
List<CharacterBase> targetList = new List<CharacterBase>();
|
||
|
||
if (card.attributeSubmodule.targetCount == -1)
|
||
{
|
||
targetList.AddRange(validTargets);
|
||
}
|
||
else if (card.HasKeyword(CardKeywords.RandomTarget))
|
||
{
|
||
targetList.AddRange(card.SetRandomTargets(validTargets));
|
||
}
|
||
else if (card.HasKeyword("TargetSelf") && card.attributeSubmodule.targetCount == 0)
|
||
{
|
||
targetList.Add(card.user);
|
||
}
|
||
|
||
if (!isInDropZone)
|
||
{
|
||
card.Play(targetList);
|
||
}
|
||
}
|
||
}
|
||
|
||
#region Play Validation
|
||
|
||
/// <summary>
|
||
/// 检查卡牌是否可以被打出(Unplayable / Prerequisite 校验)。
|
||
/// 校验失败时会生成提示文本。
|
||
/// </summary>
|
||
private bool CheckCanStartPlay()
|
||
{
|
||
if (card.HasKeyword("Unplayable"))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if (!card.HasKeyword("Prerequisite"))
|
||
{
|
||
CharacterBase currentCharacter = CombatMainManager.Instance.currentCharacter;
|
||
List<CardInstance> handPile = currentCharacter.deckSubmodule.HandPile;
|
||
for (int i = 0; i < handPile.Count; i++)
|
||
{
|
||
if (handPile[i].HasKeyword("Prerequisite"))
|
||
{
|
||
MainGameManager.Instance.basePrefabs.GenerateInfoText(
|
||
"Keyword_Prerequisite_Warning".Localize(), currentCharacter.characterView);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>重置选择状态(不触发 Untargeting 事件)。</summary>
|
||
private void ResetSelectionState()
|
||
{
|
||
isSelecting = false;
|
||
isHovering = false;
|
||
CombatUIManager.Instance.selectingCardView = null;
|
||
canvas.overrideSorting = false;
|
||
canvas.sortingOrder = 0;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Multi-Target Selection
|
||
|
||
/// <summary>进入多目标逐次选择模式。</summary>
|
||
private void EnterMultiTargetMode(int required)
|
||
{
|
||
isMultiTargetSelecting = true;
|
||
multiTargetRequired = required;
|
||
multiTargetSelectedList.Clear();
|
||
|
||
// 记录卡牌当前位置作为箭头起点锚点
|
||
multiTargetCardAnchor = cardTransform.position;
|
||
|
||
UpdateMultiTargetHint();
|
||
}
|
||
|
||
/// <summary>多目标模式下的输入处理,在 Update 中调用。</summary>
|
||
private void HandleMultiTargetInput()
|
||
{
|
||
// Escape 取消全部
|
||
if (Keyboard.current.escapeKey.wasPressedThisFrame)
|
||
{
|
||
ExitMultiTargetMode();
|
||
return;
|
||
}
|
||
|
||
// 右键撤销上一步
|
||
if (Mouse.current.rightButton.wasPressedThisFrame)
|
||
{
|
||
UndoLastMultiTarget();
|
||
return;
|
||
}
|
||
|
||
// 左键选择目标
|
||
if (Mouse.current.leftButton.wasPressedThisFrame)
|
||
{
|
||
CombatCharacterViewBase hoveringView = CombatUIManager.Instance.hoveringCharacterView;
|
||
if (hoveringView == null) return;
|
||
|
||
CharacterBase hoveringCharacter = hoveringView.character;
|
||
|
||
// 判断是否为有效目标
|
||
if (!validTargets.Contains(hoveringCharacter)) return;
|
||
|
||
// 判断是否允许重复选择
|
||
if (!card.HasKeyword(CardKeywords.AllowDuplicateTargets)
|
||
&& multiTargetSelectedList.Contains(hoveringCharacter))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 选中目标
|
||
multiTargetSelectedList.Add(hoveringCharacter);
|
||
|
||
// 生成固定箭头指向该目标
|
||
RectTransform arrowCanvasRect = CombatUIManager.Instance.arrowsPage.rectTransform;
|
||
Camera worldCamera = CombatUIManager.Instance.combatCamera;
|
||
Camera uiCamera = CombatUIManager.Instance.uiCamera;
|
||
Vector3 targetWorldPos = hoveringView.transform.position;
|
||
Vector3 targetUIPos = SpaceConverter.WorldPointToUIPoint(
|
||
arrowCanvasRect, targetWorldPos, worldCamera, uiCamera);
|
||
|
||
// 第一支箭头为 main,后续为 other
|
||
bool isMain = multiTargetSelectedList.Count == 1;
|
||
CombatUIManager.Instance.arrowsPage.GeneratePointerArrow(
|
||
multiTargetCardAnchor, targetUIPos, isMain);
|
||
|
||
// 触发 TargetingEffect 更新伤害预览
|
||
card.Targeting(hoveringCharacter);
|
||
card.contentSubmodule.dirtyMark = true;
|
||
|
||
UpdateMultiTargetHint();
|
||
|
||
// 检查是否选满
|
||
if (multiTargetSelectedList.Count >= multiTargetRequired)
|
||
{
|
||
ConfirmMultiTargetPlay();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>撤销多目标模式下的上一步选择。</summary>
|
||
private void UndoLastMultiTarget()
|
||
{
|
||
if (multiTargetSelectedList.Count > 0)
|
||
{
|
||
multiTargetSelectedList.RemoveAt(multiTargetSelectedList.Count - 1);
|
||
CombatUIManager.Instance.arrowsPage.RemoveLastPointerArrow();
|
||
|
||
// 如果还有已选目标,触发最后一个目标的 TargetingEffect
|
||
if (multiTargetSelectedList.Count > 0)
|
||
{
|
||
card.Targeting(multiTargetSelectedList[^1]);
|
||
}
|
||
else
|
||
{
|
||
card.Untargeting();
|
||
}
|
||
|
||
card.contentSubmodule.dirtyMark = true;
|
||
UpdateMultiTargetHint();
|
||
}
|
||
|
||
// 已选归零 → 取消选择,卡牌归位
|
||
if (multiTargetSelectedList.Count == 0)
|
||
{
|
||
ExitMultiTargetMode();
|
||
}
|
||
}
|
||
|
||
/// <summary>选满后自动打出。</summary>
|
||
private void ConfirmMultiTargetPlay()
|
||
{
|
||
List<CharacterBase> targets = new List<CharacterBase>(multiTargetSelectedList);
|
||
|
||
// 清理多目标状态
|
||
isMultiTargetSelecting = false;
|
||
multiTargetSelectedList.Clear();
|
||
multiTargetRequired = 0;
|
||
CombatUIManager.Instance.arrowsPage.ClearPointerArrows();
|
||
|
||
isSelecting = false;
|
||
isHovering = false;
|
||
CombatUIManager.Instance.selectingCardView = null;
|
||
canvas.overrideSorting = false;
|
||
canvas.sortingOrder = 0;
|
||
|
||
// 打出卡牌
|
||
if (!card.Play(targets))
|
||
{
|
||
card.eventSubmodule.onUntargeting();
|
||
card.contentSubmodule.dirtyMark = true;
|
||
}
|
||
}
|
||
|
||
/// <summary>退出多目标选择模式(取消操作),清理所有状态,卡牌归位。</summary>
|
||
private void ExitMultiTargetMode()
|
||
{
|
||
isMultiTargetSelecting = false;
|
||
multiTargetSelectedList.Clear();
|
||
multiTargetRequired = 0;
|
||
|
||
CombatUIManager.Instance.arrowsPage.ClearPointerArrows();
|
||
|
||
isSelecting = false;
|
||
isHovering = false;
|
||
CombatUIManager.Instance.selectingCardView = null;
|
||
canvas.overrideSorting = false;
|
||
canvas.sortingOrder = 0;
|
||
|
||
card.eventSubmodule.onUntargeting();
|
||
card.contentSubmodule.dirtyMark = true;
|
||
}
|
||
|
||
/// <summary>更新多目标选择的计数提示。</summary>
|
||
private void UpdateMultiTargetHint()
|
||
{
|
||
string hint = $"{multiTargetSelectedList.Count} / {multiTargetRequired}";
|
||
MainGameManager.Instance.basePrefabs.GenerateInfoText(
|
||
hint, card.user.characterView, Color.cyan);
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |