pkNX/pkNX.Randomization/Randomizers/Personal/PersonalRandomizer.cs
Kurt 2578ba073c Refactoring
File scoped namespaces for all lib projects
netstandard2.0 => net6; now uniform. bye netframework!
2022-10-01 17:08:17 -07:00

455 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using pkNX.Structures;
namespace pkNX.Randomization;
public class PersonalRandomizer : Randomizer
{
private const int tmcount = 100;
private const int eggGroupCount = 16;
private const int TypeCount = 18;
private readonly GameInfo Game;
private readonly IPersonalTable Table;
private readonly EvolutionSet[] Evolutions;
public PersonalRandSettings Settings { get; set; } = new();
public PersonalRandomizer(IPersonalTable table, GameInfo game, EvolutionSet[] evolutions)
{
Game = game;
Table = table;
Evolutions = evolutions;
if (File.Exists("bannedabilites.txt"))
{
var data = File.ReadAllLines("bannedabilities.txt");
var list = new List<int>(BannedAbilities);
list.AddRange(data.Select(z => Convert.ToInt32(z)));
BannedAbilities = list;
}
}
public override void Execute()
{
if (Settings.ModifyByEvolutions)
RandomizeChains();
else
RandomizeAllSpecies();
}
private void RandomizeAllSpecies()
{
for (ushort species = 0; species <= Game.MaxSpeciesID; species++)
RandomizeSpecies(species);
}
private bool[] processed = Array.Empty<bool>();
private void RandomizeChains()
{
processed = new bool[Table.Table.Length];
for (ushort species = 0; species <= Game.MaxSpeciesID; species++)
{
for (byte f = 0; f <= Table[species].FormCount; f++)
RandomizeChain(species, f);
}
}
private bool AlreadyProcessed(int index)
{
var p = processed;
if (p.Length <= index)
return false;
if (p[index])
return true;
p[index] = true;
return false;
}
private void RandomizeChain(ushort species, byte form)
{
var index = Table.GetFormIndex(species, form);
if (AlreadyProcessed(index))
return;
processed[index] = true;
var entry = Table[index];
Randomize(entry, species);
ProcessEvolutions(species, form, index);
}
private void ProcessEvolutions(int species, int form, int devolvedIndex)
{
var evoindex = GetEvolutionEntry((ushort)species, (byte)form);
var evos = Evolutions[evoindex];
if (evos.PossibleEvolutions.Length == 1)
{
var evo = evos.PossibleEvolutions[0];
var ei = Table.GetFormIndex(evo.Species, evo.Form);
if (AlreadyProcessed(ei))
return;
RandomizeSingleChain(evo, devolvedIndex);
ProcessEvolutions(evo.Species, evo.Form, ei);
}
else
{
foreach (var evo in evos.PossibleEvolutions)
{
var ei = Table.GetFormIndex(evo.Species, evo.Form);
if (AlreadyProcessed(ei))
return;
RandomizeSplitChain(evo, devolvedIndex);
ProcessEvolutions(evo.Species, evo.Form, ei);
}
}
}
private void RandomizeSingleChain(EvolutionMethod evo, int devolvedIndex)
{
var child = Table[devolvedIndex];
var z = Table.GetFormEntry(evo.Species, evo.Form);
RandomizeFrom(z, child, evo.Species);
}
private void RandomizeSplitChain(EvolutionMethod evo, int devolvedIndex)
{
var child = Table[devolvedIndex];
var z = Table.GetFormEntry(evo.Species, evo.Form);
RandomizeFrom(z, child, evo.Species);
}
private int GetEvolutionEntry(ushort species, byte form)
{
if (Game.Generation < 7)
return species;
return Table.GetFormIndex(species, form);
}
private void RandomizeSpecies(ushort species)
{
var entry = Table[species];
Randomize(entry, species);
var formCount = entry.FormCount;
for (byte form = 1; form <= formCount; form++)
{
entry = Table.GetFormEntry(species, form);
Randomize(entry, species);
}
}
public void RandomizeFrom(IPersonalInfo z, IPersonalInfo child, int species)
{
if (Settings.ModifyStats)
RandomizeStats(z);
if (Settings.ShuffleStats)
RandomShuffledStats(z);
if (Settings.ModifyTypes)
{
if (Settings.InheritType && Settings.InheritTypeSetting != ModifyState.All)
{
switch (Settings.InheritTypeSetting)
{
case ModifyState.Shared:
default:
z.Type1 = child.Type1;
z.Type2 = child.Type2;
break;
case ModifyState.Two when Rand.Next(100) < Settings.InheritTypeOnlyOneChance:
case ModifyState.One when Rand.Next(100) < Settings.InheritTypeOnlyOneChance:
switch (Rand.Next(2))
{
case 0:
z.Type1 = (Types)GetRandomType();
z.Type2 = child.Type2;
break;
case 1:
z.Type1 = child.Type1;
z.Type2 = (Types)GetRandomType();
break;
}
break;
case ModifyState.Two when Rand.Next(100) < Settings.InheritTypeNeitherChance:
RandomizeTypes(z);
break;
}
}
else
{
RandomizeTypes(z);
}
}
if (Settings.ModifyAbility)
{
if (Settings.InheritAbility)
{
Span<int> abils = stackalloc int[3];
child.GetAbilities(abils);
GetRandomAbilities(abils, Settings.InheritAbilitySetting);
z.SetAbilities(abils);
}
else
{
RandomizeAbilities(z);
}
}
if (z is IMovesInfo_1 mi)
{
if (Settings.ModifyLearnsetTM || Settings.ModifyLearnsetHM)
{
if (Settings.InheritChildTM)
mi.TMHM = ((IMovesInfo_1)child).TMHM;
else
RandomizeTMHM(mi);
}
if (Settings.ModifyLearnsetTypeTutors)
{
if (Settings.InheritChildSpecial)
mi.TypeTutors = ((IMovesInfo_1)child).TypeTutors;
else
RandomizeTypeTutors(mi, species);
}
}
if (Settings.ModifyLearnsetMoveTutors && z is IMovesInfo_2 mi2)
{
if (Settings.InheritChildTutor)
mi2.SpecialTutors = ((IMovesInfo_2)child).SpecialTutors;
else
RandomizeSpecialTutors(mi2);
}
if (Settings.ModifyEgg)
{
z.EggGroup1 = child.EggGroup1;
z.EggGroup2 = child.EggGroup2;
}
if (Settings.ModifyHeldItems)
{
if (Settings.InheritHeldItem)
{
z.Item1 = child.Item1;
z.Item2 = child.Item2;
z.Item3 = child.Item3;
}
else
{
RandomizeHeldItems(z);
}
}
ExecuteCatchRate(z);
}
private void GetRandomAbilities(Span<int> abils, ModifyState setting)
{
switch (setting)
{
case ModifyState.Shared:
return;
case ModifyState.Two when Rand.Next(100) < Settings.InheritAbilityNeitherChance:
GetRandomAbilities(abils, 1);
break;
case ModifyState.Two when Rand.Next(100) < Settings.InheritAbilityOnlyOneChance:
case ModifyState.One when Rand.Next(100) < Settings.InheritAbilityOnlyOneChance:
GetRandomAbilities(abils, 2);
break;
case ModifyState.All:
GetRandomAbilities(abils);
if (Rand.Next(100) < Settings.SameAbilityChance)
{
int index = Rand.Next(2);
abils[index ^ 1] = abils[index];
}
break;
}
}
public void Randomize(IPersonalInfo z, int species)
{
if (Settings.ModifyStats)
RandomizeStats(z);
if (Settings.ShuffleStats)
RandomShuffledStats(z);
if (Settings.ModifyTypes)
RandomizeTypes(z);
if (Settings.ModifyAbility)
RandomizeAbilities(z);
if (z is IMovesInfo_1 mi)
{
if (Settings.ModifyLearnsetTM || Settings.ModifyLearnsetHM)
RandomizeTMHM(mi);
if (Settings.ModifyLearnsetTypeTutors)
RandomizeTypeTutors(mi, species);
}
if (Settings.ModifyLearnsetMoveTutors && z is IMovesInfo_2 mi2)
RandomizeSpecialTutors(mi2);
if (Settings.ModifyEgg)
RandomizeEggGroups(z);
if (Settings.ModifyHeldItems)
RandomizeHeldItems(z);
ExecuteCatchRate(z);
}
private void ExecuteCatchRate(IPersonalInfo z)
{
if (Settings.CatchRate == CatchRate.Random)
z.CatchRate = Rand.Next(3, 251); // Random Catch Rate between 3 and 250.
else if (Settings.CatchRate == CatchRate.BSTScaled)
z.CatchRate = GetBSTCatchRate(z.GetBaseStatTotal());
}
private static int GetBSTCatchRate(int BST)
{
var c = 11 * (Math.Sqrt(Math.Max(0, 600 - BST)));
const int min = 3;
return (int)Math.Min(255, min + c);
}
private void RandomizeTMHM(IMovesInfo_1 z)
{
var tms = z.TMHM;
if (Settings.ModifyLearnsetTM)
{
for (int j = 0; j < tmcount; j++)
tms[j] = Rand.Next(100) < Settings.LearnTMPercent;
}
if (Settings.ModifyLearnsetHM)
{
for (int j = tmcount; j < tms.Length; j++)
tms[j] = Rand.Next(100) < Settings.LearnTMPercent;
}
z.TMHM = tms;
}
private void RandomizeTypeTutors(IMovesInfo_1 z, int species)
{
var t = z.TypeTutors;
for (int i = 0; i < t.Length; i++)
t[i] = Rand.Next(100) < Settings.LearnTypeTutorPercent;
// Make sure Rayquaza can learn Dragon Ascent.
if (!Game.XY && species == (int)Species.Rayquaza)
t[7] = true;
z.TypeTutors = t;
}
private void RandomizeSpecialTutors(IMovesInfo_2 z)
{
var tutors = z.SpecialTutors;
foreach (bool[] tutor in tutors)
{
for (int i = 0; i < tutor.Length; i++)
tutor[i] = Rand.Next(100) < Settings.LearnMoveTutorPercent;
}
z.SpecialTutors = tutors;
}
private void RandomizeAbilities(IPersonalAbility z)
{
Span<int> abils = stackalloc int[3];
z.GetAbilities(abils);
GetRandomAbilities(abils, Settings.Ability);
z.SetAbilities(abils);
}
private void GetRandomAbilities(Span<int> abils, int skip = 0)
{
for (int i = 0; i < abils.Length - skip; i++)
abils[i] = GetRandomAbility();
}
private void RandomizeEggGroups(IPersonalEgg z)
{
z.EggGroup1 = GetRandomEggGroup();
z.EggGroup2 = Rand.Next(100) < Settings.SameEggGroupChance ? z.EggGroup1 : GetRandomEggGroup();
}
private void RandomizeHeldItems(IPersonalItems z)
{
for (int j = 0; j < z.GetNumItems(); j++)
z.SetItemAtIndex(j, GetRandomHeldItem());
}
private void RandomizeTypes(IPersonalType z)
{
z.Type1 = (Types)GetRandomType();
z.Type2 = Rand.Next(0, 100) < Settings.SameTypeChance ? z.Type1 : (Types)GetRandomType();
}
private void RandomizeStats(IBaseStat z)
{
// Fiddle with Base Stats, don't muck with Shedinja.
if (z.GetBaseStatValue(0) == 1)
return;
int RandDeviation() => Rand.Next(Settings.StatDeviationMin, Settings.StatDeviationMax);
for (int i = 0; i < z.GetNumBaseStats(); i++)
{
if (!Settings.StatsToRandomize[i])
continue;
var val = z.GetBaseStatValue(i) * RandDeviation() / 100;
z.SetBaseStatValue(i, Math.Max(1, Math.Min(255, val)));
}
}
private static void RandomShuffledStats(IBaseStat z)
{
// Fiddle with Base Stats, don't muck with Shedinja.
if (z.GetBaseStatValue(0) == 1)
return;
var stats = new int[z.GetNumBaseStats()];
for (int i = 0; i < z.GetNumBaseStats(); i++)
stats[i] = z.GetBaseStatValue(i);
Util.Shuffle(stats);
for (int i = 0; i < z.GetNumBaseStats(); i++)
z.SetBaseStatValue(i, stats[i]);
}
private int GetRandomType() => Rand.Next(0, TypeCount);
private int GetRandomEggGroup() => Rand.Next(1, eggGroupCount);
private int GetRandomHeldItem() => Game.HeldItems.Length > 1 ? Game.HeldItems[Rand.Next(1, Game.HeldItems.Length)] : 0;
private readonly IList<int> BannedAbilities = Array.Empty<int>();
private int GetRandomAbility()
{
const int WonderGuard = 25;
while (true)
{
int newabil = Rand.Next(1, Game.MaxAbilityID + 1);
if (newabil == WonderGuard && Settings.WonderGuard == Permissive.No)
continue;
if (BannedAbilities.Contains(newabil))
continue;
return newabil;
}
}
}