意图初步

This commit is contained in:
SoulliesOfficial
2025-11-15 09:08:36 -05:00
parent 9a8eadef24
commit 85bbe2431c
33 changed files with 508 additions and 198 deletions

View File

@@ -43,7 +43,7 @@ namespace Continentis.MainGame.Card
public string functionText;
public string cardDescription;
[Header("Intention")] public float baseWeight = 1f;
[Header("Intention")] public float baseWeight = 0f;
[Header("Attributes")] [Tooltip("可变属性这个属性会自动设置BaseAttr进入Original设置AttrBaseAttrOffset=0以及DisplayAttr进入Current")]
public SerializableDictionary<string, float> variableAttributes = new SerializableDictionary<string, float>();

View File

@@ -12,9 +12,11 @@ namespace Continentis.MainGame.Card
[Header("References")]
public DeckSubmodule deck;
//public string currentPileName;
public ICardOwner owner;
public CharacterBase user;
public CombatTeam team;
public CombatTeam usingTeam;
public CardLogicBase cardLogic;
public CardLocation cardLocation;
public HandCardView handCardView;
@@ -29,11 +31,11 @@ namespace Continentis.MainGame.Card
this.user = owner as CharacterBase;
if (this.owner is CombatTeam team)
{
this.team = team;
this.usingTeam = team;
}
else if (this.owner is CharacterBase character)
{
this.team = character.team;
this.usingTeam = character.team;
}
this.deck = owner.deckSubmodule;

View File

@@ -19,7 +19,7 @@ namespace Continentis.MainGame.Card
public ICardOwner owner => cardInstance.owner;
public CharacterBase user => cardInstance.user;
public CombatTeam team => cardInstance.team;
public CombatTeam UsingTeam => cardInstance.usingTeam;
public HandCardView handCardView => cardInstance.handCardView;
public IntentionCardView intentionCardView => cardInstance.intentionCardView;
@@ -173,7 +173,7 @@ namespace Continentis.MainGame.Card
{
protected CardLogicBase card;
protected CharacterBase user => card.user;
protected CombatTeam team => card.team;
protected CombatTeam team => card.UsingTeam;
public virtual void Initialize(CardLogicBase card)
{

View File

@@ -176,6 +176,7 @@ namespace Continentis.MainGame.Card
}
cardInstance.user = user ?? CombatMainManager.Instance.currentCharacter;
cardInstance.user.recordSubmodule.RecordCardPlay(cardInstance);
if (!willCheckBeforePlay || CheckBeforePlay())
{
@@ -212,7 +213,10 @@ namespace Continentis.MainGame.Card
});
AfterPlayEffect(targetList);
playSubmodule.isDuringPlayEffect = false;
handCardView.isDuringPlaying = false;
if (handCardView != null)
{
handCardView.isDuringPlaying = false;
}
}));
return true;
}

View File

@@ -17,5 +17,10 @@ namespace Continentis.MainGame.Card
this.baseWeight = owner.cardData.baseWeight;
this.currentWeight = baseWeight;
}
public void RefreshCurrentWeight()
{
this.currentWeight = baseWeight;
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using Continentis.MainGame.Card;
using Continentis.MainGame.Combat;
using NaughtyAttributes;
using SLSFramework.UModAssistance;
using UnityEngine;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
@@ -28,6 +29,7 @@ namespace Continentis.MainGame.Character
public Guid elementID { get; set; }
public CharacterData data;
public CharacterLogicBase logicBase;
public Fraction fraction;
public CombatTeam team;
@@ -54,13 +56,17 @@ namespace Continentis.MainGame.Character
[ShowNativeProperty]
public CombatBuffSubmodule combatBuffSubmodule { get; private set; }
[ShowNativeProperty]
public RecordSubmodule recordSubmodule { get; private set; }
public void Initialize(Fraction fraction)
public virtual void Initialize(Fraction fraction, CharacterData data)
{
(this as IGameElement).Initialize();
this.fraction = fraction;
this.data = data;
switch (fraction)
{
case Fraction.Player:
@@ -81,6 +87,33 @@ namespace Continentis.MainGame.Character
intentionSubmodule = new IntentionSubmodule(this);
statusSubmodule = new StatusSubmodule(this);
combatBuffSubmodule = new CombatBuffSubmodule(this);
recordSubmodule = new RecordSubmodule(this);
this.logicBase = GenerateCharacterLogic(data);
this.logicBase.Initialize(this);
}
/// <summary>
/// 生成卡牌逻辑实例
/// </summary>
public static CharacterLogicBase GenerateCharacterLogic(CharacterData data)
{
string typeID = ModManager.GetTypeID(data.modName, "Characters", "", data.className);
Type logicType = ModManager.GetType(typeID);
if(logicType == null)
{
return new CharacterLogicBase();
}
if (Activator.CreateInstance(logicType) is CharacterLogicBase logic)
{
return logic;
}
Debug.LogError($"Card class '{typeID}' not found or could not be instantiated.");
return null;
}
}
@@ -88,124 +121,6 @@ namespace Continentis.MainGame.Character
{
public CombatCharacterViewBase characterView;
}
public partial class CharacterBase
{
public virtual void GetIntendedCards()
{
bool CanAfford(CardInstance card, int stamina, int mana)
{
return card.cardLogic.GetAttribute("StaminaCost") <= stamina &&
card.cardLogic.GetAttribute("ManaCost") <= mana;
}
bool CheckAvailabilityAndSetTargets(CardInstance card, out List<CharacterBase> targets)
{
card.cardLogic.DetectTargetsValidity(out List<CharacterBase> valid, out _, out _);
if (valid.Count == 0 || !card.cardLogic.CheckBeforePlay())
{
targets = null;
return false; // 无有效目标或无法使用则跳过
}
targets = card.cardLogic.SetRandomTargets(valid);
return true;
}
IntentionBase currentIntention = intentionSubmodule.currentIntention;
List<CardInstance> availableCards = deckSubmodule.PoolPile;
List<IntendedCard> intended = new List<IntendedCard>();
int currentStamina = GetAttribute("Stamina");
int remainingStamina = currentStamina - currentIntention.guaranteedStamina;
int currentMana = GetAttribute("Mana");
int remainingMana = currentMana - currentIntention.guaranteedMana;
List<CardInstance> forced = new List<CardInstance>();
List<CardInstance> normal = new List<CardInstance>();
foreach (CardInstance card in availableCards)
{
if (card.cardLogic.weightSubmodule.forceUse)
{
forced.Add(card);
}
else
{
normal.Add(card);
}
}
intentionSubmodule.intendedCards.Clear();
//(characterView.hudContainer.enablingHUDs["Intention"] as Intention)?.Clear();
// 1. 优先处理强制选择卡牌
foreach (CardInstance card in forced)
{
if (currentIntention.maxCardCount > 0 && intended.Count >= currentIntention.maxCardCount)
{
break; // 已达数量上限
}
if (CanAfford(card, remainingStamina, remainingMana))
{
if(!CheckAvailabilityAndSetTargets(card, out List<CharacterBase> targets))
{
continue; // 无有效目标或无法使用则跳过
}
intended.Add(new IntendedCard(card, targets));
remainingStamina -= card.cardLogic.GetAttribute("StaminaCost");
remainingMana -= card.cardLogic.GetAttribute("ManaCost");
}
// 行动力不足则跳过该卡
}
// 2. 在剩余普通卡牌中基于权重随机选取
while (intended.Count < currentIntention.maxCardCount)
{
// 筛选出当前资源下还能出的牌
List<CardInstance> affordableCards = normal.FindAll(card => CanAfford(card, remainingStamina, remainingMana));
if (affordableCards.Count == 0)
{
break;
}
float totalWeight = affordableCards.Sum(card => card.cardLogic.weightSubmodule.currentWeight);
if (totalWeight <= 0f) break;
float r = Random.value * totalWeight;
float accum = 0f;
CardInstance chosen = null;
foreach (CardInstance card in affordableCards)
{
accum += card.cardLogic.weightSubmodule.currentWeight;
if (r <= accum)
{
chosen = card;
break;
}
}
if (chosen != null)
{
if (!CheckAvailabilityAndSetTargets(chosen, out List<CharacterBase> targets))
{
normal.Remove(chosen);
continue; // 无有效目标或无法使用则跳过
}
intended.Add(new IntendedCard(chosen, targets));
normal.Remove(chosen);
remainingStamina -= chosen.cardLogic.GetAttribute("StaminaCost");
remainingMana -= chosen.cardLogic.GetAttribute("ManaCost");
}
}
intentionSubmodule.intendedCards.AddRange(intended);
}
}
public partial class CharacterBase
{

View File

@@ -57,12 +57,6 @@ namespace Continentis.MainGame.Character
public List<string> hudDataRefs;
}
public partial class CharacterData
{
public string ModName => haveCustomClass ? classFullName.Split('_').First() : modName;
public string ClassName => haveCustomClass ? classFullName.Split('_').Last() : className;
}
public partial class CharacterData
{
public static CharacterData Get(string dataID)

View File

@@ -0,0 +1,16 @@
using Continentis.MainGame.Card;
using Continentis.MainGame.Equipment;
using SLSFramework.UModAssistance;
using UnityEngine;
namespace Continentis.MainGame.Character
{
public partial class CharacterLogicBase
{
protected CharacterBase character;
public virtual void Initialize(CharacterBase character)
{
this.character = character;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 59a1c52238303dc41bd58e565f7fbeda

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Card;
using Continentis.MainGame.Equipment;
using SLSFramework.General;
@@ -278,4 +279,136 @@ namespace Continentis.MainGame.Character
target.characterView.hudContainer.UpdateAllHUD();
}
}
public partial class CharacterBase
{
public virtual void RegisterIntention(params IntentionBase[] intentions)
{
intentionSubmodule.allIntentions.AddRange(intentions);
}
public virtual void IntentionBrain()
{
List<IntentionBase> availableIntentions = intentionSubmodule.allIntentions.Where(intention => intention.Condition()).ToList();
availableIntentions.Sort();
intentionSubmodule.currentIntention = availableIntentions.FirstOrDefault() ?? new IntentionBase(intentionSubmodule);
intentionSubmodule.currentIntention.RefreshCardWeights();
intentionSubmodule.currentIntention.RefreshTargets();
}
public virtual void GetIntendedCards()
{
bool CanAfford(CardInstance card, int stamina, int mana)
{
return card.cardLogic.GetAttribute("StaminaCost") <= stamina &&
card.cardLogic.GetAttribute("ManaCost") <= mana;
}
bool CheckAvailabilityAndSetTargets(CardInstance card, out List<CharacterBase> targets)
{
card.cardLogic.DetectTargetsValidity(out List<CharacterBase> valid, out _, out _);
if (valid.Count == 0 || !card.cardLogic.CheckBeforePlay())
{
targets = null;
return false; // 无有效目标或无法使用则跳过
}
targets = card.cardLogic.SetRandomTargets(valid);
return true;
}
IntentionBase currentIntention = intentionSubmodule.currentIntention;
List<CardInstance> availableCards = deckSubmodule.PoolPile;
List<IntendedCard> intended = new List<IntendedCard>();
int currentStamina = GetAttribute("Stamina");
int remainingStamina = currentStamina - currentIntention.guaranteedStamina;
int currentMana = GetAttribute("Mana");
int remainingMana = currentMana - currentIntention.guaranteedMana;
List<CardInstance> forced = new List<CardInstance>();
List<CardInstance> normal = new List<CardInstance>();
foreach (CardInstance card in availableCards)
{
if (card.cardLogic.weightSubmodule.forceUse)
{
forced.Add(card);
}
else
{
normal.Add(card);
}
}
intentionSubmodule.intendedCards.Clear();
//(characterView.hudContainer.enablingHUDs["Intention"] as Intention)?.Clear();
// 1. 优先处理强制选择卡牌
foreach (CardInstance card in forced)
{
if (currentIntention.maxCardCount > 0 && intended.Count >= currentIntention.maxCardCount)
{
break; // 已达数量上限
}
if (CanAfford(card, remainingStamina, remainingMana))
{
if(!CheckAvailabilityAndSetTargets(card, out List<CharacterBase> targets))
{
continue; // 无有效目标或无法使用则跳过
}
intended.Add(new IntendedCard(card, targets));
remainingStamina -= card.cardLogic.GetAttribute("StaminaCost");
remainingMana -= card.cardLogic.GetAttribute("ManaCost");
}
// 行动力不足则跳过该卡
}
// 2. 在剩余普通卡牌中基于权重随机选取
while (intended.Count < currentIntention.maxCardCount)
{
// 筛选出当前资源下还能出的牌
List<CardInstance> affordableCards = normal.FindAll(card => CanAfford(card, remainingStamina, remainingMana));
if (affordableCards.Count == 0)
{
break;
}
float totalWeight = affordableCards.Sum(card => card.cardLogic.weightSubmodule.currentWeight);
if (totalWeight <= 0f) break;
float r = Random.value * totalWeight;
float accum = 0f;
CardInstance chosen = null;
foreach (CardInstance card in affordableCards)
{
accum += card.cardLogic.weightSubmodule.currentWeight;
if (r <= accum)
{
chosen = card;
break;
}
}
if (chosen != null)
{
if (!CheckAvailabilityAndSetTargets(chosen, out List<CharacterBase> targets))
{
normal.Remove(chosen);
continue; // 无有效目标或无法使用则跳过
}
intended.Add(new IntendedCard(chosen, targets));
normal.Remove(chosen);
remainingStamina -= chosen.cardLogic.GetAttribute("StaminaCost");
remainingMana -= chosen.cardLogic.GetAttribute("ManaCost");
}
}
intentionSubmodule.intendedCards.AddRange(intended);
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Continentis.MainGame.Card;
using SLSFramework.General;
using UnityEngine;
using UnityEngine.Events;
@@ -8,6 +9,7 @@ namespace Continentis.MainGame.Character
{
public class IntentionSubmodule : SubmoduleBase<CharacterBase>
{
public List<IntentionBase> allIntentions;
public IntentionBase currentIntention;
public UnityAction getIntendedCards;
@@ -15,7 +17,8 @@ namespace Continentis.MainGame.Character
public IntentionSubmodule(CharacterBase owner) : base(owner)
{
currentIntention = new IntentionBase();
allIntentions = new List<IntentionBase>();
currentIntention = new IntentionBase(this);
getIntendedCards = owner.GetIntendedCards;
intendedCards = new List<IntendedCard>();
}
@@ -33,11 +36,30 @@ namespace Continentis.MainGame.Character
}
}
public class IntentionBase
public class IntentionBase : IPrioritized
{
public int guaranteedStamina = 0;
public int guaranteedMana = 0;
public int maxCardCount = 999;
public IntentionSubmodule intentionSubmodule;
public CharacterBase character => intentionSubmodule.owner;
public DeckSubmodule characterDeck => character.deckSubmodule;
public RecordSubmodule characterRecord => character.recordSubmodule;
public int Priority { get; protected set; }
public int guaranteedStamina;
public int guaranteedMana;
public int maxCardCount;
public IntentionBase(IntentionSubmodule intentionSubmodule)
{
this.intentionSubmodule = intentionSubmodule;
this.Priority = 0;
this.guaranteedStamina = 0;
this.guaranteedMana = 0;
this.maxCardCount = 999;
}
public virtual bool Condition()
{
return true;
}
public virtual void RefreshCardWeights()
{
@@ -48,5 +70,7 @@ namespace Continentis.MainGame.Character
{
}
}
}

View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Card;
using UnityEngine;
namespace Continentis.MainGame.Character
{
public partial class RecordSubmodule : SubmoduleBase<CharacterBase>
{
public int currentRound;
public int currentAction;
public List<ActionRecord> actionRecords;
public RecordSubmodule(CharacterBase owner) : base(owner)
{
actionRecords = new List<ActionRecord>();
}
public void SetAction(int round, int actionIndex)
{
currentRound = round;
currentAction = actionIndex;
actionRecords.Add(new ActionRecord(round, actionIndex, new List<CardInstance>()));
}
}
public partial class RecordSubmodule
{
public void RecordCardPlay(CardInstance card)
{
if (actionRecords.Count == 0)
{
Debug.LogWarning("No action record to add card play to.");
return;
}
ActionRecord currentRecord = actionRecords[actionRecords.Count - 1];
currentRecord.cardsPlayed.Add(card);
Debug.Log($"在回合 {currentRecord.round} 行动 {currentRecord.actionIndex} 中记录了卡牌 {card.cardLogic.contentSubmodule.cardName} 的使用。");
}
}
public partial class RecordSubmodule
{
public ActionRecord GetRecord(int round, int actionIndex)
{
return actionRecords.FirstOrDefault(record => record.round == round && record.actionIndex == actionIndex);
}
public List<ActionRecord> GetLastActionsRecords(int count)
{
return actionRecords.Skip(Mathf.Max(0, actionRecords.Count - count)).ToList();
}
public List<ActionRecord> GetLastRoundsRecords(int roundCount)
{
int minRound = Mathf.Max(0, currentRound - roundCount + 1);
return actionRecords.Where(record => record.round >= minRound).ToList();
}
}
[Serializable]
public class ActionRecord
{
public int round;
public int actionIndex;
public List<CardInstance> cardsPlayed;
public ActionRecord(int round, int actionIndex, List<CardInstance> cardsPlayed)
{
this.round = round;
this.actionIndex = actionIndex;
this.cardsPlayed = cardsPlayed;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 95504a2d65ce1fa4d82cbdb23ef16892

View File

@@ -13,8 +13,7 @@ namespace Continentis.MainGame.Character
public static CombatNPC GenerateCharacter(CharacterData data, Fraction fraction)
{
CombatNPC combatNpc = new CombatNPC();
combatNpc.data = data;
combatNpc.Initialize(fraction);
combatNpc.Initialize(fraction, data);
return combatNpc;
}

View File

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

View File

@@ -16,8 +16,7 @@ namespace Continentis.MainGame.Character
public static PlayerHero GenerateCharacter(CharacterData data)
{
PlayerHero playerHero = new PlayerHero();
playerHero.data = data;
playerHero.Initialize(Fraction.Player);
playerHero.Initialize(Fraction.Player, data);
return playerHero;
}

View File

@@ -103,6 +103,9 @@ namespace Continentis.MainGame.Combat
{
if (character is CombatNPC npc)
{
npc.IntentionBrain();
npc.deckSubmodule.PoolPile.ForEach(card => card.cardLogic.weightSubmodule.RefreshCurrentWeight());
npc.intentionSubmodule.getIntendedCards.Invoke();
foreach (IntendedCard intendedCard in npc.intentionSubmodule.intendedCards)
{
@@ -144,6 +147,7 @@ namespace Continentis.MainGame.Combat
currentCharacter = characterController.actionOrderList[0];
currentCharacter.eventSubmodule.onActionStart.Invoke();
currentCharacter.recordSubmodule.SetAction(currentRound, ++currentActionIndex);
if (currentCharacter is PlayerHero playerHero)
{