pkNX/pkNX.Randomization/Randomizers/MoveRandomizer.cs
Kurt 0936c08eb1 LZA 1.0.2
Cumulative changes from the team.

Co-Authored-By: Matt <17801814+sora10pls@users.noreply.github.com>
Co-Authored-By: SciresM <8676005+SciresM@users.noreply.github.com>
Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
2025-11-16 15:56:12 -06:00

179 lines
5.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using pkNX.Structures;
namespace pkNX.Randomization;
public class MoveRandomizer : Randomizer
{
private readonly IReadOnlyList<IMove> MoveData;
private readonly IPersonalTable SpeciesStat;
private readonly GameInfo Config;
private GenericRandomizer<int> RandMove;
internal MovesetRandSettings Settings = new();
public MoveRandomizer(GameInfo config, IReadOnlyList<IMove> moves, IPersonalTable t)
{
Config = config;
var maxMoveId = config.MaxMoveID;
MoveData = moves;
SpeciesStat = t;
RandMove = new GenericRandomizer<int>(Enumerable.Range(1, maxMoveId - 1).ToArray());
}
public override void Execute() => throw new Exception("Shouldn't be called.");
public static ReadOnlySpan<int> FixedDamageMoves => [49, 82];
public void Initialize(MovesetRandSettings settings, int[] bannedMoves)
{
Settings = settings;
var banned = new List<int>();
banned.AddRange(Legal.Taboo_Moves.Concat(Legal.Z_Moves).Concat(Legal.Max_Moves));
if (Settings.BanFixedDamageMoves)
banned.AddRange(FixedDamageMoves);
banned.AddRange(bannedMoves);
Settings.BannedMoves = banned.ToArray();
var all = Enumerable.Range(1, Config.MaxMoveID - 1);
var moves = all.Except(banned);
RandMove = new GenericRandomizer<int>(moves.ToArray());
}
public int[] GetRandomLearnset(int index, int movecount) => GetRandomLearnset(SpeciesStat[index], movecount);
public int[] GetRandomLearnset(IPersonalType Types, int movecount)
{
var oldSTABCount = Settings.STABCount;
Settings.STABCount = (int)(Settings.STABPercent * movecount / 100);
int[] moves = GetRandomMoveset(Types, movecount);
Settings.STABCount = oldSTABCount;
return moves;
}
public int[] GetRandomMoveset(int index, int movecount = 4) => GetRandomMoveset(SpeciesStat[index], movecount);
public int[] GetRandomMoveset(IPersonalType Types, int movecount = 4)
{
int loopctr = 0;
const int maxLoop = 666;
int[] moves;
do { moves = GetRandomMoves(Types, movecount); }
while (!IsMovesetMeetingRequirements(moves, Types, movecount) && loopctr++ <= maxLoop);
return moves;
}
private int[] GetRandomMoves(IPersonalType Types, int movecount = 4)
{
int[] moves = new int[movecount];
int i = 0;
if (Settings.STAB)
{
for (; i < Settings.STABCount; i++)
moves[i] = GetRandomSTABMove(Types);
}
for (; i < moves.Length; i++) // remainder of moves
moves[i] = RandMove.Next();
return moves;
}
private int GetRandomSTABMove(IPersonalType types)
{
int move;
int ctr = 0;
do { move = RandMove.Next(); }
while (!types.IsType((Types)MoveData[move].Type) && ctr++ < RandMove.Count);
return move;
}
private bool IsMovesetMeetingRequirements(int[] moves, IPersonalType types, int count)
{
if (Settings.DMG && Settings.DMGCount > moves.Count(move => MoveData[move].Category != 0))
return false;
if (Settings.STAB)
{
var stabCt = moves.Count(move => types.IsType((Types)MoveData[move].Type));
if (stabCt < Settings.STABCount)
return false;
}
if (moves.Any(Settings.BannedMoves.Contains))
return false;
return moves.Distinct().Count() == count;
}
public void ReorderMovesPower(IList<int> moves) => ReorderMovesPower(moves, MoveData);
private static void ReorderMovesPower(IList<int> moves, IReadOnlyList<IMove> movedata)
{
var data = moves.Select((Move, Index) => new { Index, Move, Data = movedata[Move] });
var powered = data.Where(z => z.Data.Power > 1).ToList();
var indexes = powered.ConvertAll(z => z.Index);
var order = powered.OrderBy(z => z.Data.Power * Math.Max(1, (z.Data.HitMin + z.Data.HitMax) / 2m)).ToList();
for (var i = 0; i < order.Count; i++)
moves[indexes[i]] = order[i].Move;
}
private static readonly int[] firstMoves =
[
1, // Pound
40, // Poison Sting
52, // Ember
55, // Water Gun
64, // Peck
71, // Absorb
84, // Thunder Shock
98, // Quick Attack
122, // Lick
141, // Leech Life
];
private static readonly GenericRandomizer<int> first = new(firstMoves);
public static int GetRandomFirstMoveAny()
{
first.Reset();
return first.Next();
}
public int GetRandomFirstMove(int index) => GetRandomFirstMove(SpeciesStat[index]);
public int GetRandomFirstMove(IPersonalType types)
{
first.Reset();
int ctr = 0;
int move;
do
{
move = first.Next();
if (++ctr == firstMoves.Length)
return move;
} while (!types.IsType((Types)MoveData[move].Type));
return move;
}
public bool SanitizeMovesetForBannedMoves(int[] moves, int index)
{
bool updated = false;
for (int m = 0; m < moves.Length; m++)
{
if (!Settings.BannedMoves.Contains(moves[m]))
continue;
updated = true;
moves[m] = GetRandomFirstMove(index);
}
return updated;
}
}