意图初步

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

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -18,5 +18,4 @@ MonoBehaviour:
- CharacterData_Basic_Assassin - CharacterData_Basic_Assassin
- CharacterData_Basic_Cleric - CharacterData_Basic_Cleric
enemyCharacters: enemyCharacters:
- CharacterData_Basic_MarshalOfTheUnderworld - CharacterData_Basic_MarshalOfUnderworld
- CharacterData_Basic_MarshalOfTheUnderworld

View File

@@ -18,16 +18,17 @@ MonoBehaviour:
- KeywordData_Basic_Buff_Assassin - KeywordData_Basic_Buff_Assassin
- KeywordData_Basic_Default - KeywordData_Basic_Default
cardDataIDList: cardDataIDList:
- CardData_Basic_AblazeInPurgatory
- CardData_Basic_HellfireBrand
- CardData_Basic_SoulCleave
- CardData_Basic_ArmyOfTheDead
- CardData_Basic_GreatswordSweep
- CardData_Basic_HellfireBlast
- CardData_Basic_NecromanticInfusion
- CardData_Basic_WrathOfUnderworld
- CardData_Basic_Rouse - CardData_Basic_Rouse
- CardData_Basic_Tactic - CardData_Basic_Tactic
- CardData_Basic_Whimsy - CardData_Basic_Whimsy
- CardData_Basic_AblazeInPurgatory
- CardData_Basic_ArmyOfTheDead
- CardData_Basic_HellfireBlast
- CardData_Basic_HellfireBrand
- CardData_Basic_NecromanticInfusion
- CardData_Basic_SoulCleave
- CardData_Basic_WrathOfTheUnderworld
- CardData_Basic_Bludgeon - CardData_Basic_Bludgeon
- CardData_Basic_DualStrike - CardData_Basic_DualStrike
- CardData_Basic_FireBolt - CardData_Basic_FireBolt
@@ -105,7 +106,7 @@ MonoBehaviour:
- CharacterData_Basic_Cleric - CharacterData_Basic_Cleric
- CharacterData_Basic_Knight - CharacterData_Basic_Knight
- CharacterData_Basic_Mage - CharacterData_Basic_Mage
- CharacterData_Basic_MarshalOfTheUnderworld - CharacterData_Basic_MarshalOfUnderworld
- CharacterData_Basic_SkeletonGuard - CharacterData_Basic_SkeletonGuard
equipmentDataIDList: equipmentDataIDList:
- EquipmentData_Basic_SteelBracer - EquipmentData_Basic_SteelBracer

View File

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

View File

@@ -13,18 +13,32 @@ MonoBehaviour:
m_Name: CardData_Basic_NecromanticInfusion m_Name: CardData_Basic_NecromanticInfusion
m_EditorClassIdentifier: m_EditorClassIdentifier:
modName: Basic modName: Basic
categoryName:
className: NecromanticInfusion className: NecromanticInfusion
displayName: Card_Basic_NecromanticInfusion_DisplayName displayName: Card_Basic_NecromanticInfusion_DisplayName
cardRarity: 40 cardRarity: 30
cardType: 10 cardType: 10
tags: [] keywords:
- TargetEnemies
cardSprite: {fileID: 21300000, guid: 4319eef242cf5a94cace1528f74bfe42, type: 3} cardSprite: {fileID: 21300000, guid: 4319eef242cf5a94cace1528f74bfe42, type: 3}
cardLayoutTags: []
functionText: Card_Basic_NecromanticInfusion_FunctionText functionText: Card_Basic_NecromanticInfusion_FunctionText
cardDescription: $Keyword("Exhaust"), Apply 50% Lifesteal amplification to all cardDescription:
allies. baseWeight: 0
baseWeight: 10
variableAttributes: variableAttributes:
dictionaryList: [] dictionaryList:
- Key: TargetCount
Value: 1
index: 0
isKeyDuplicated: 0
- Key: StaminaCost
Value: 2
index: 1
isKeyDuplicated: 0
- Key: ManaCost
Value: 0
index: 2
isKeyDuplicated: 0
dividerPosProp: 0.5 dividerPosProp: 0.5
originalAttributes: originalAttributes:
dictionaryList: [] dictionaryList: []

View File

@@ -12,16 +12,18 @@ namespace Continentis.Mods.Basic.Cards
{ {
protected override List<CommandBase> PlayEffect(List<CharacterBase> targetList) protected override List<CommandBase> PlayEffect(List<CharacterBase> targetList)
{ {
base.PlayEffect(targetList); CommandGroup main = new CommandGroup(new Cmd_Function(() =>
{
CreateCharacterBuff<Prowess>(2).Apply(user, user, this);
}));
CommandGroup mainGroup = TargetListCommandGroup(targetList, ExecutionMode.Parallel, ExecutionMode.Parallel, CommandGroup weak = TargetListCommandGroup(targetList,
new Cmd_ParamFunction<CharacterBase>(0.2f, target => new Cmd_ParamFunction<CharacterBase>(0.2f, target =>
{ {
Basic_SoulAbsorption buff = new Basic_SoulAbsorption(50); CreateCharacterBuff<Weak>(2).Apply(target, user, this);
buff.Apply(target, user, this);
})); }));
return new List<CommandBase> { mainGroup }; return new List<CommandBase> { main, weak };
} }
} }
} }

View File

@@ -18,8 +18,6 @@ namespace Continentis.Mods.Basic.Cards
protected override List<CommandBase> PlayEffect(List<CharacterBase> targetList) protected override List<CommandBase> PlayEffect(List<CharacterBase> targetList)
{ {
base.PlayEffect(targetList);
CommandGroup singleTargetGroup = new CommandGroup(ExecutionMode.Parallel, CommandGroup singleTargetGroup = new CommandGroup(ExecutionMode.Parallel,
new Cmd_PlayAnimation(user.characterView, "Attack"), new Cmd_PlayAnimation(user.characterView, "Attack"),
new Cmd_ParamFunction<CharacterBase>(0.4f, target => new Cmd_ParamFunction<CharacterBase>(0.4f, target =>

View File

@@ -10,13 +10,13 @@ MonoBehaviour:
m_Enabled: 1 m_Enabled: 1
m_EditorHideFlags: 0 m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b6560183465e5944ba49e39974faafd4, type: 3} m_Script: {fileID: 11500000, guid: b6560183465e5944ba49e39974faafd4, type: 3}
m_Name: CharacterData_Basic_MarshalOfTheUnderworld m_Name: CharacterData_Basic_MarshalOfUnderworld
m_EditorClassIdentifier: m_EditorClassIdentifier:
haveCustomClass: 0 haveCustomClass: 1
classFullName: classFullName: MarshalOfUnderworld
modName: Basic modName: Basic
className: MarshalOfTheUnderworld className: MarshalOfUnderworld
displayName: Marshal Of The Underworld displayName: Marshal Of Underworld
tags: [] tags: []
avatar: {fileID: 21300000, guid: 8b60e98b9ca5e6a4587341cd27607e24, type: 3} avatar: {fileID: 21300000, guid: 8b60e98b9ca5e6a4587341cd27607e24, type: 3}
portrait: {fileID: 0} portrait: {fileID: 0}
@@ -144,7 +144,7 @@ MonoBehaviour:
index: 20 index: 20
isKeyDuplicated: 0 isKeyDuplicated: 0
- Key: StaminaRecoverPerAction - Key: StaminaRecoverPerAction
Value: 0 Value: 4
index: 21 index: 21
isKeyDuplicated: 0 isKeyDuplicated: 0
- Key: ManaRecoverPerAction - Key: ManaRecoverPerAction
@@ -192,6 +192,11 @@ MonoBehaviour:
prefabRefs: [] prefabRefs: []
derivativeCardDataRefs: [] derivativeCardDataRefs: []
derivativeCharacterDataRefs: [] derivativeCharacterDataRefs: []
initialDeckRef: [] initialDeckRef:
- CardData_Basic_ArmyOfTheDead
- CardData_Basic_GreatswordSweep
- CardData_Basic_HellfireBlast
- CardData_Basic_WrathOfUnderworld
- CardData_Basic_NecromanticInfusion
hudDataRefs: hudDataRefs:
- HUDData_Basic_Default - HUDData_Basic_Default

View File

@@ -0,0 +1,130 @@
using System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Card;
using Continentis.MainGame.Character;
using Continentis.MainGame.Combat;
using Continentis.Mods.Basic.Buffs;
using Continentis.Mods.Basic.Cards;
using UnityEngine;
namespace Continentis.Mods.Basic.Characters
{
public class MarshalOfUnderworld : CharacterLogicBase
{
public override void Initialize(CharacterBase character)
{
base.Initialize(character);
character.RegisterIntention(
new Normal(character.intentionSubmodule),
new SummonFirst(character.intentionSubmodule),
new UltimateAttackFirst(character.intentionSubmodule));
}
private class Normal : IntentionBase
{
public Normal(IntentionSubmodule intentionSubmodule) : base(intentionSubmodule)
{
}
public override void RefreshCardWeights()
{
characterDeck.PoolPile.ForEach(card =>
{
card.cardLogic.weightSubmodule.baseWeight = 0;
card.cardLogic.weightSubmodule.forceUse = false;
card.cardLogic.weightSubmodule.forceIgnore = false;
});
foreach (CardInstance card in characterDeck.PoolPile)
{
if (card.cardLogic is HellfireBlast)
{
card.cardLogic.weightSubmodule.baseWeight = 1;
}
else if (card.cardLogic is NecromanticInfusion)
{
if (characterRecord.GetLastActionsRecords(3)
.Any(rec => rec.cardsPlayed.Any(recCard => recCard.cardLogic is NecromanticInfusion)))
{
card.cardLogic.weightSubmodule.baseWeight = 0;
}
else
{
card.cardLogic.weightSubmodule.baseWeight = 1;
}
}
else if (card.cardLogic is GreatswordSweep)
{
card.cardLogic.weightSubmodule.baseWeight = 1;
}
}
}
}
private class SummonFirst : Normal
{
public SummonFirst(IntentionSubmodule intentionSubmodule) : base(intentionSubmodule)
{
Priority = 20;
}
public override bool Condition()
{
List<CharacterBase> allies = CombatMainManager.Instance.characterController.GetAllAllies(character);
return allies.Count == 1; // Only self is present
}
public override void RefreshCardWeights()
{
base.RefreshCardWeights();
foreach (CardInstance card in characterDeck.PoolPile)
{
if (card.cardLogic is ArmyOfTheDead)
{
card.cardLogic.weightSubmodule.forceUse = true;
}
}
}
}
private class UltimateAttackFirst : Normal
{
public UltimateAttackFirst(IntentionSubmodule intentionSubmodule) : base(intentionSubmodule)
{
Priority = 10;
}
public override bool Condition()
{
List<CharacterBase> targets = CombatMainManager.Instance.characterController.GetAllEnemies(character);
foreach (CharacterBase target in targets)
{
if (target.combatBuffSubmodule.TryGetBuff(out Burn burn))
{
if (burn.unitedStackSubmodule.stackAmount >= 5)
{
return true;
}
}
}
return false;
}
public override void RefreshCardWeights()
{
base.RefreshCardWeights();
foreach (CardInstance card in characterDeck.PoolPile)
{
if (card.cardLogic is WrathOfUnderworld)
{
card.cardLogic.weightSubmodule.forceUse = true;
}
}
}
}
}
}

View File

@@ -1,16 +0,0 @@
using UnityEngine;
public class MarshallOfUnderworld : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

@@ -9,6 +9,11 @@ namespace Continentis.Mods.Basic.Rules
{ {
public override void ApplyRules_ConvertCoreIntoGeneral(Dictionary<string, float> core, Dictionary<string, float> general) public override void ApplyRules_ConvertCoreIntoGeneral(Dictionary<string, float> core, Dictionary<string, float> general)
{ {
if (core.TryGetValue("DisableConversion", out float value) && value > 0) //如果禁用属性转换,则直接返回
{
return;
}
float level = core["Level"]; float level = core["Level"];
general["MaximumHealth"] += Mathf.FloorToInt(level * 3); general["MaximumHealth"] += Mathf.FloorToInt(level * 3);
general["MaximumMana"] += Mathf.FloorToInt(level / 2); general["MaximumMana"] += Mathf.FloorToInt(level / 2);

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,5 +17,10 @@ namespace Continentis.MainGame.Card
this.baseWeight = owner.cardData.baseWeight; this.baseWeight = owner.cardData.baseWeight;
this.currentWeight = 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.Card;
using Continentis.MainGame.Combat; using Continentis.MainGame.Combat;
using NaughtyAttributes; using NaughtyAttributes;
using SLSFramework.UModAssistance;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
using Random = UnityEngine.Random; using Random = UnityEngine.Random;
@@ -28,6 +29,7 @@ namespace Continentis.MainGame.Character
public Guid elementID { get; set; } public Guid elementID { get; set; }
public CharacterData data; public CharacterData data;
public CharacterLogicBase logicBase;
public Fraction fraction; public Fraction fraction;
public CombatTeam team; public CombatTeam team;
@@ -54,13 +56,17 @@ namespace Continentis.MainGame.Character
[ShowNativeProperty] [ShowNativeProperty]
public CombatBuffSubmodule combatBuffSubmodule { get; private set; } 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 as IGameElement).Initialize();
this.fraction = fraction; this.fraction = fraction;
this.data = data;
switch (fraction) switch (fraction)
{ {
case Fraction.Player: case Fraction.Player:
@@ -81,6 +87,33 @@ namespace Continentis.MainGame.Character
intentionSubmodule = new IntentionSubmodule(this); intentionSubmodule = new IntentionSubmodule(this);
statusSubmodule = new StatusSubmodule(this); statusSubmodule = new StatusSubmodule(this);
combatBuffSubmodule = new CombatBuffSubmodule(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 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 public partial class CharacterBase
{ {

View File

@@ -57,12 +57,6 @@ namespace Continentis.MainGame.Character
public List<string> hudDataRefs; 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 partial class CharacterData
{ {
public static CharacterData Get(string dataID) 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.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Card; using Continentis.MainGame.Card;
using Continentis.MainGame.Equipment; using Continentis.MainGame.Equipment;
using SLSFramework.General; using SLSFramework.General;
@@ -278,4 +279,136 @@ namespace Continentis.MainGame.Character
target.characterView.hudContainer.UpdateAllHUD(); 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;
using System.Collections.Generic; using System.Collections.Generic;
using Continentis.MainGame.Card; using Continentis.MainGame.Card;
using SLSFramework.General;
using UnityEngine; using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
@@ -8,6 +9,7 @@ namespace Continentis.MainGame.Character
{ {
public class IntentionSubmodule : SubmoduleBase<CharacterBase> public class IntentionSubmodule : SubmoduleBase<CharacterBase>
{ {
public List<IntentionBase> allIntentions;
public IntentionBase currentIntention; public IntentionBase currentIntention;
public UnityAction getIntendedCards; public UnityAction getIntendedCards;
@@ -15,7 +17,8 @@ namespace Continentis.MainGame.Character
public IntentionSubmodule(CharacterBase owner) : base(owner) public IntentionSubmodule(CharacterBase owner) : base(owner)
{ {
currentIntention = new IntentionBase(); allIntentions = new List<IntentionBase>();
currentIntention = new IntentionBase(this);
getIntendedCards = owner.GetIntendedCards; getIntendedCards = owner.GetIntendedCards;
intendedCards = new List<IntendedCard>(); intendedCards = new List<IntendedCard>();
} }
@@ -33,11 +36,30 @@ namespace Continentis.MainGame.Character
} }
} }
public class IntentionBase public class IntentionBase : IPrioritized
{ {
public int guaranteedStamina = 0; public IntentionSubmodule intentionSubmodule;
public int guaranteedMana = 0; public CharacterBase character => intentionSubmodule.owner;
public int maxCardCount = 999; 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() 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) public static CombatNPC GenerateCharacter(CharacterData data, Fraction fraction)
{ {
CombatNPC combatNpc = new CombatNPC(); CombatNPC combatNpc = new CombatNPC();
combatNpc.data = data; combatNpc.Initialize(fraction, data);
combatNpc.Initialize(fraction);
return combatNpc; return combatNpc;
} }

View File

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

View File

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

View File

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

View File

@@ -55,7 +55,7 @@ namespace SLSFramework.UModAssistance
{ {
ModHost host = await ModManager.LoadAsync(mod); ModHost host = await ModManager.LoadAsync(mod);
ModManager.RegisterTypesFromMod(host, typeof(RulesCollectionBase)); ModManager.RegisterTypesFromMod(host, typeof(RulesCollectionBase));
ModManager.RegisterTypesFromMod(host, typeof(CharacterBase)); ModManager.RegisterTypesFromMod(host, typeof(CharacterLogicBase));
ModManager.RegisterTypesFromMod(host, typeof(CardLogicBase)); ModManager.RegisterTypesFromMod(host, typeof(CardLogicBase));
ModManager.RegisterTypesFromMod(host, typeof(EquipmentBase)); ModManager.RegisterTypesFromMod(host, typeof(EquipmentBase));
ModManager.RegisterTypesFromMod(host, typeof(CardCombatBuffBase)); ModManager.RegisterTypesFromMod(host, typeof(CardCombatBuffBase));