Files
SVSimServer/SVSim.BattleEngine/Engine/Wizard/FullSimulationAI.cs
gamer147 957af3d1ec feat(battle-engine): full Unity/VFX/god-object shims + expanded copy closure (2570 files)
Authored Unity primitive/object-model shim, VFX layer (control-flow-preserving, InstantVfx never invokes its action -- headless suppression), god-object stubs (GameMgr/EffectMgr/UIManager with faithfully-extracted nested enums), View/UI/Touch tree, LitJson+BetterList+Tuple copied, third-party stubs. Discovered Roslyn header-error masking: fixing class-header type errors unmasks body references, so the true copy closure is ~2570 files (was 782 under masking). Errors: masked-25720 -> 268; our shim files compile clean. Remaining: ~50 residual shim/external types, 24 NGUI UI-base overrides, static-type fixes, plus likely 1-2 more unmask waves.
2026-06-05 17:22:20 -04:00

339 lines
12 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Cute;
namespace Wizard;
public class FullSimulationAI : IBattleSimulationAI
{
private EnemyAI AI;
private int bestMoveCount;
private bool _isOverComplexity;
private int _bestFirstDeadIndex;
private int _originalFieldInplayCount;
private ulong[] _defaultPlayptnHashArray;
private List<AIVirtualActionInfo> _simulationAllMoves;
public AIVirtualActionInfo Cr_FirstActionInfo { get; private set; }
public AIVirtualField Cr_MaxField { get; private set; }
public float Cr_MaxFieldValue { get; private set; }
public float Cr_MaxFieldValueAtSelfTurnEnd { get; private set; }
public List<AIVirtualActionInfo> Cr_MaxActionInfoSequence { get; private set; }
public PlayPtnWithToken Cr_BestPlayPtnWithToken { get; private set; }
public FullSimulationAI(EnemyAI ai, List<AIVirtualActionInfo> allMoves)
{
AI = ai;
Cr_FirstActionInfo = null;
bestMoveCount = int.MaxValue;
Cr_MaxFieldValue = float.MinValue;
Cr_MaxFieldValueAtSelfTurnEnd = float.MinValue;
Cr_MaxActionInfoSequence = null;
Cr_MaxField = null;
_isOverComplexity = false;
_originalFieldInplayCount = 0;
_simulationAllMoves = allMoves;
}
public IEnumerator _CrSimulate(AIVirtualField originalField, bool doesUseEvo, bool checkTimeOverLogic, PlayPtnWithToken[] bestPlayPtnsWithToken = null, SimulationAdditionalActionInfoSet additionalActions = null)
{
_ = originalField.ParamQuery;
Cr_FirstActionInfo = null;
bestMoveCount = int.MaxValue;
Cr_MaxFieldValue = float.MinValue;
Cr_MaxFieldValueAtSelfTurnEnd = float.MinValue;
Cr_MaxActionInfoSequence = null;
Cr_MaxField = null;
_isOverComplexity = false;
AI.IsInVirutalSimulation = true;
_originalFieldInplayCount = originalField.AllyInplayCards.Count((AIVirtualCard c) => !c.IsDead);
_defaultPlayptnHashArray = AISimulationUtility.CalculatePlayptnWithTokenHashs(originalField, bestPlayPtnsWithToken);
AI.IsFullSimulation = true;
ParallelJob job = ParallelJob.Dispatch(delegate
{
try
{
List<AIVirtualActionInfo> recursiveMoveList = null;
RunAllMoves(originalField, doesUseEvo, _simulationAllMoves, bestPlayPtnsWithToken, recursiveMoveList, additionalActions);
}
catch (Exception)
{
}
});
while (!job.isDone)
{
yield return null;
if (checkTimeOverLogic && AI.IsRunWeakLogic)
{
AI.IsInVirutalSimulation = false;
yield break;
}
}
AI.IsFullSimulation = false;
if (_isOverComplexity)
{
BestInPlayMoveAI safety = new BestInPlayMoveAI(AI);
yield return EnemyAICoroutine.GetInstance().StartCoroutine(safety._CrSimulate(originalField, doesUseEvo, checkTimeOverLogic: false, bestPlayPtnsWithToken, additionalActions));
Cr_FirstActionInfo = safety.Cr_FirstActionInfo;
Cr_MaxField = safety.Cr_MaxField;
Cr_MaxFieldValue = safety.Cr_MaxFieldValue;
Cr_MaxActionInfoSequence = safety.Cr_MaxActionInfoSequence;
}
AI.IsInVirutalSimulation = false;
}
private void RunAllMoves(AIVirtualField field, bool doesUseEvo, List<AIVirtualActionInfo> allMoveSet, PlayPtnWithToken[] bestPlayPtnsWithToken, List<AIVirtualActionInfo> recursiveMoveList, SimulationAdditionalActionInfoSet additionalActions)
{
if (AI.IsRunWeakLogic)
{
return;
}
if (allMoveSet.Count > AISimulationUtility.FULL_SIMULATION_MOVE_COUNT_TURESHOLD)
{
_isOverComplexity = true;
return;
}
AIVirtualTargetSelectAction aIVirtualTargetSelectAction = null;
if (additionalActions != null)
{
aIVirtualTargetSelectAction = additionalActions.ForceEvoAction;
}
if (aIVirtualTargetSelectAction != null && !IsAttackPlanContainsSituation(recursiveMoveList, aIVirtualTargetSelectAction) && !IsAttackPlanContainsSituation(allMoveSet, aIVirtualTargetSelectAction))
{
return;
}
List<ulong> list = new List<ulong>();
bool isPlayptnSimulation = bestPlayPtnsWithToken != null;
for (int i = 0; i < allMoveSet.Count && !AI.IsRunWeakLogic; i++)
{
AIVirtualField aIVirtualField = new AIVirtualField(field, isLatestAction: false, isPlayptnSimulation);
AIVirtualActionInfo cloneActionInfo = GetCloneActionInfo(aIVirtualField, allMoveSet[i]);
if (cloneActionInfo == null)
{
continue;
}
ulong hash = cloneActionInfo.GetHash();
if (list.Contains(hash))
{
continue;
}
List<AIVirtualActionInfo> list2 = ((recursiveMoveList == null) ? new List<AIVirtualActionInfo>() : new List<AIVirtualActionInfo>(recursiveMoveList));
list2.Add(cloneActionInfo);
if (AI.IsRunWeakLogic)
{
break;
}
switch (cloneActionInfo.ActionType)
{
case AIOperationType.PLAY:
RunPlay(cloneActionInfo, aIVirtualField);
break;
case AIOperationType.EVOLVE:
RunEvolution(cloneActionInfo, aIVirtualField);
break;
case AIOperationType.ATTACK:
RunAttack(cloneActionInfo, aIVirtualField);
break;
case AIOperationType.TURNEND:
RunTurnEnd(cloneActionInfo, aIVirtualField, bestPlayPtnsWithToken, list2, aIVirtualTargetSelectAction);
continue;
}
if (AI.IsRunWeakLogic)
{
break;
}
if (aIVirtualField.EnemyClass.IsDead || aIVirtualField.AllyClass.IsDead)
{
UpdateBestRecord(aIVirtualField, list2, null, 0, aIVirtualTargetSelectAction);
continue;
}
SimulationAdditionalActionInfoSet simulationAdditionalActionInfoSet = new SimulationAdditionalActionInfoSet
{
ForceEvoAction = ((cloneActionInfo.ActionType == AIOperationType.EVOLVE) ? null : aIVirtualTargetSelectAction),
ExtraActionBaseList = additionalActions?.ExtraActionBaseList,
PlayPtnRecord = additionalActions?.PlayPtnRecord
};
List<AIVirtualActionInfo> allMovesForFullSimulation = AISimulationUtility.GetAllMovesForFullSimulation(aIVirtualField, doesUseEvo, simulationAdditionalActionInfoSet);
RunAllMoves(aIVirtualField, doesUseEvo, allMovesForFullSimulation, bestPlayPtnsWithToken, list2, simulationAdditionalActionInfoSet);
if (_isOverComplexity || AI.IsRunWeakLogic)
{
break;
}
list.Add(hash);
}
}
private void RunPlay(AIVirtualActionInfo play, AIVirtualField field)
{
PlaySimulationInfo playInfo = AIPlayCardSimulationUtility.CreatePlaySimulationInfo(play.Actor, play, field);
AIVirtualPlaySimulator.PlayCard((AIVirtualTargetSelectAction)play, field, playInfo);
}
private void RunEvolution(AIVirtualActionInfo evolution, AIVirtualField field)
{
AIVirtualCard actor = evolution.Actor;
if (!actor.IsDead && !actor.IsEvolution)
{
actor.IsUseEvo = true;
AIVirtualEvolutionSimulator.ManualEvolve(evolution, field);
field.EvoUsedCard = actor;
}
}
private void RunAttack(AIVirtualActionInfo attackSituation, AIVirtualField field)
{
AIVirtualAttackInfo aIVirtualAttackInfo = attackSituation as AIVirtualAttackInfo;
AIVirtualCard actor = aIVirtualAttackInfo.Actor;
AIVirtualAttackSimulator.Attack(aIVirtualAttackInfo, field);
if (aIVirtualAttackInfo.AttackTarget.IsDead)
{
aIVirtualAttackInfo.IsBreakTarget = true;
}
if (aIVirtualAttackInfo.IsAttackSuccessed && actor.TagCollectionContainer.HasTag(AIPlayTagType.PuppetAttack))
{
actor.TagCollectionContainer.PuppetAttackTags.ExecutePuppetAttack(actor, aIVirtualAttackInfo, null);
}
if (aIVirtualAttackInfo.AttackTarget.IsDead)
{
aIVirtualAttackInfo.IsBreakTarget = true;
}
}
private void RunTurnEnd(AIVirtualActionInfo move, AIVirtualField field, PlayPtnWithToken[] bestPlayPtnsWithToken, List<AIVirtualActionInfo> passToNext, AIVirtualTargetSelectAction forceEvoSituation)
{
int openCount = 0;
if (bestPlayPtnsWithToken == null)
{
BattleSequencer.OnAfterExecuteAttackCommon(field, (AIVirtualTurnEndInfo)move, ref passToNext, isAttackToLeaderAgain: true);
}
else
{
openCount = _originalFieldInplayCount - field.AllyInplayCards.Count((AIVirtualCard c) => !c.IsDead);
}
UpdateBestRecord(field, passToNext, bestPlayPtnsWithToken, openCount, forceEvoSituation);
}
private void UpdateBestRecord(AIVirtualField fieldTemp, List<AIVirtualActionInfo> moves, PlayPtnWithToken[] bestPlayPtnsWithToken, int openCount, AISituationInfo forceEvoSituation)
{
if (forceEvoSituation != null && !IsAttackPlanContainsSituation(moves, forceEvoSituation))
{
return;
}
float num = AISimulationUtility.EvaluateField(fieldTemp, EvaluationType.SELF_TURN);
float num2 = num;
int bestFirstDeadActionIndex = int.MaxValue;
PlayPtnWithToken bestPlayPtnWithToken = null;
if (bestPlayPtnsWithToken != null)
{
num2 += AISimulationUtility.CalculateOneOnOneBothDeadBonus(moves);
num2 += AISimulationUtility.EvaluateLegalPlayPtnAfterSimulation(fieldTemp, moves, bestPlayPtnsWithToken, openCount, _defaultPlayptnHashArray, out bestPlayPtnWithToken, out bestFirstDeadActionIndex);
}
else
{
num2 -= (fieldTemp.EnemyClass.IsDead ? 0f : fieldTemp.EvaluateThreaten());
}
num2 -= (float)fieldTemp.ActionLength * 0.001f;
num2 += AISimulationUtility.CalculateMoveFirstBonusValue(moves);
bool flag = false;
if (fieldTemp.EnemyClass.IsDead)
{
if (Cr_MaxField == null || !Cr_MaxField.EnemyClass.IsDead || fieldTemp.ActionLength < bestMoveCount || (fieldTemp.ActionLength == bestMoveCount && EnemyAI.IsLargerThan(num2, Cr_MaxFieldValue)))
{
if (moves[0].ActionType == AIOperationType.TURNEND)
{
AI.IsTurnEndLethal = true;
}
flag = true;
}
}
else if (EnemyAI.IsLargerThan(num2, Cr_MaxFieldValue))
{
flag = true;
}
else if (EnemyAI.IsSameValue(num2, Cr_MaxFieldValue))
{
if (bestPlayPtnsWithToken == null)
{
if (EnemyAI.IsLargerThan(num, Cr_MaxFieldValueAtSelfTurnEnd))
{
flag = true;
}
}
else if (bestFirstDeadActionIndex < _bestFirstDeadIndex)
{
flag = true;
}
}
if (flag)
{
Cr_MaxFieldValue = num2;
Cr_MaxFieldValueAtSelfTurnEnd = num;
Cr_MaxActionInfoSequence = moves;
Cr_FirstActionInfo = moves[0];
bestMoveCount = fieldTemp.ActionLength;
Cr_MaxField = fieldTemp;
if (bestPlayPtnsWithToken != null)
{
_bestFirstDeadIndex = bestFirstDeadActionIndex;
Cr_BestPlayPtnWithToken = bestPlayPtnWithToken;
}
}
}
private AIVirtualActionInfo GetCloneActionInfo(AIVirtualField fieldTemp, AIVirtualActionInfo action)
{
if (action == null || action is AIVirtualTurnEndInfo)
{
return action;
}
AIVirtualCard aIVirtualCard = null;
aIVirtualCard = ((action.ActionType != AIOperationType.PLAY) ? fieldTemp.AllyInplayCards.Find((AIVirtualCard c) => c.CardIndex == action.OriginalCard.CardIndex) : fieldTemp.AllyHandCards.Find((AIVirtualCard c) => c.CardIndex == action.OriginalCard.CardIndex));
if (aIVirtualCard == null)
{
AIConsoleUtility.LogError("FullSimulationAI.GetCloneActionInfo() error!! Cannot find originalCard!!!!!");
return null;
}
if (action is AIVirtualTargetSelectAction aIVirtualTargetSelectAction)
{
return new AIVirtualTargetSelectAction(aIVirtualCard.IsSameCard(action.Actor) ? aIVirtualCard : new AIVirtualCard(action.Actor, fieldTemp), selectedTargetList: aIVirtualTargetSelectAction.SelectedTargets.GetSimilarTargetInfoSet(fieldTemp), original: aIVirtualCard, operationType: action.ActionType);
}
if (action is AIVirtualAttackInfo)
{
AIVirtualAttackInfo aIVirtualAttackInfo = action as AIVirtualAttackInfo;
AIVirtualCard originalAttackTarget = aIVirtualAttackInfo.AttackTarget;
AIVirtualCard target = fieldTemp.CardListSet.EnemyClassAndInplayCards.Find((AIVirtualCard c) => c.CardIndex == originalAttackTarget.CardIndex);
return new AIVirtualAttackInfo(aIVirtualCard, target);
}
return null;
}
private bool IsAttackPlanContainsSituation(List<AIVirtualActionInfo> moves, AISituationInfo situation)
{
if (moves == null || moves.Count <= 0)
{
return false;
}
for (int i = 0; i < moves.Count; i++)
{
if (moves[i].IsSameSituation(situation))
{
return true;
}
}
return false;
}
}