pk3DS/pk3DS.Core/Randomizers/PersonalRandomizer.cs
2024-06-02 18:56:55 -05:00

306 lines
9.2 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using pk3DS.Core.Structures;
using pk3DS.Core.Structures.PersonalInfo;
namespace pk3DS.Core.Randomizers;
public class PersonalRandomizer : IRandomizer
{
private readonly Random rnd = Util.Rand;
private const decimal LearnTMPercent = 35; // Average Learnable TMs is 35.260.
private const decimal LearnTypeTutorPercent = 2; //136 special tutor moves learnable by species in Untouched ORAS.
private const decimal LearnMoveTutorPercent = 30; //10001 tutor moves learnable by 826 species in Untouched ORAS.
private const int tmcount = 100;
private const int eggGroupCount = 16;
private readonly GameConfig Game;
private readonly PersonalInfo[] Table;
// Randomization Settings
public int TypeCount;
public bool ModifyCatchRate = true;
public bool ModifyLearnsetTM = true;
public bool ModifyLearnsetHM = true;
public bool ModifyLearnsetTypeTutors = true;
public bool ModifyLearnsetMoveTutors = true;
public bool ModifyHeldItems = true;
public bool ModifyAbilities = true;
public bool AllowWonderGuard = true;
public bool ModifyStats = true;
public bool ShuffleStats = true;
public decimal StatDeviation = 25;
public bool[] StatsToRandomize = [true, true, true, true, true, true];
public bool ModifyTypes = true;
public decimal SameTypeChance = 50;
public bool ModifyEggGroup = true;
public decimal SameEggGroupChance = 50;
//public bool Advanced { get; set; } = false;
public bool TMInheritance { get; set; }
public bool ModifyLearnsetSmartly { get; set; }
public ushort[] MoveIDsTMs { private get; set; }
public Move[] Moves => Game.Moves;
public EvolutionSet[] Evos => Game.Evolutions;
public PersonalRandomizer(PersonalInfo[] table, GameConfig game)
{
Game = game;
Table = table;
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 void Execute()
{
for (var i = 1; i < Table.Length; i++)
Randomize(Table[i], i);
if (TMInheritance)
PropagateTMs(Table, Evos);
}
private void PropagateTMs(PersonalInfo[] table, EvolutionSet[] evos)
{
int specCount = Game.MaxSpeciesID;
var HandledIndexes = new HashSet<int>();
for (int species = 1; species <= specCount; species++)
{
var entry = table[species];
PropagateDown(entry, species, 0);
for (int form = 0; form < entry.FormeCount; form++)
PropagateDown(entry, species, form);
}
void PropagateDown(PersonalInfo pi, int species, int form)
{
int index = pi.FormeIndex(species, form);
if (index == species && form != 0)
return;
if (index >= evos.Length)
index = species;
PropagateDownIndex(pi, index);
}
void PropagateDownIndex(PersonalInfo pi, int index)
{
if (HandledIndexes.Contains(index))
return;
var evoList = evos[index];
foreach (var evo in evoList.PossibleEvolutions.Where(z => z.Species != 0))
{
var espec = evo.Species;
var eform = evo.Form;
var evoIndex = table[espec].FormeIndex(espec, eform);
if (evoIndex >= table.Length)
continue;
if (!HandledIndexes.Contains(evoIndex))
table[evoIndex].TMHM = pi.TMHM;
else // pre-evolution encountered! take the higher evolution's TM's since they have been propagated up already...
pi.TMHM = table[evoIndex].TMHM;
HandledIndexes.Add(evoIndex);
PropagateDownIndex(pi, evoIndex); // recurse for the rest of the evo chain
}
}
}
public void Randomize(PersonalInfo z, int index)
{
// Fiddle with Learnsets
if (ModifyLearnsetTM || ModifyLearnsetHM)
{
if (!ModifyLearnsetSmartly)
RandomizeTMHMSimple(z);
else
RandomizeTMHMAdvanced(z);
}
if (ModifyLearnsetTypeTutors)
RandomizeTypeTutors(z, index);
if (ModifyLearnsetMoveTutors)
RandomizeSpecialTutors(z);
if (ModifyStats)
RandomizeStats(z);
if (ShuffleStats)
RandomShuffledStats(z);
if (ModifyAbilities)
RandomizeAbilities(z);
if (ModifyEggGroup)
RandomizeEggGroups(z);
if (ModifyHeldItems)
RandomizeHeldItems(z);
if (ModifyTypes)
RandomizeTypes(z);
if (ModifyCatchRate)
z.CatchRate = rnd.Next(3, 251); // Random Catch Rate between 3 and 250.
}
private void RandomizeTMHMAdvanced(PersonalInfo z)
{
var tms = z.TMHM;
//var types = z.Types;
bool CanLearn(Move _)
{
//var type = m.Type;
//bool typeMatch = types.Any(t => t == type);
// todo: how do I learn move?
return rnd.Next(0, 100) < LearnTMPercent;
}
if (ModifyLearnsetTM)
{
for (int j = 0; j < tmcount; j++)
{
var moveID = MoveIDsTMs[j];
var move = Moves[moveID];
tms[j] = CanLearn(move);
}
}
if (ModifyLearnsetHM)
{
for (int j = tmcount; j < tms.Length; j++)
{
var moveID = MoveIDsTMs[j];
var move = Moves[moveID];
tms[j] = CanLearn(move);
}
}
z.TMHM = tms;
}
private void RandomizeTMHMSimple(PersonalInfo z)
{
var tms = z.TMHM;
if (ModifyLearnsetTM)
{
for (int j = 0; j < tmcount; j++)
tms[j] = rnd.Next(0, 100) < LearnTMPercent;
}
if (ModifyLearnsetHM)
{
for (int j = tmcount; j < tms.Length; j++)
tms[j] = rnd.Next(0, 100) < LearnTMPercent;
}
z.TMHM = tms;
}
private void RandomizeTypeTutors(PersonalInfo z, int index)
{
var t = z.TypeTutors;
for (int i = 0; i < t.Length; i++)
t[i] = rnd.Next(0, 100) < LearnTypeTutorPercent;
// Make sure Rayquaza can learn Dragon Ascent.
if (!Game.XY && index is 384 or 814)
t[7] = true;
z.TypeTutors = t;
}
private void RandomizeSpecialTutors(PersonalInfo z)
{
var tutors = z.SpecialTutors;
foreach (bool[] tutor in tutors)
{
for (int i = 0; i < tutor.Length; i++)
tutor[i] = rnd.Next(0, 100) < LearnMoveTutorPercent;
}
z.SpecialTutors = tutors;
}
private void RandomizeAbilities(PersonalInfo z)
{
var abils = z.Abilities;
for (int i = 0; i < abils.Length; i++)
abils[i] = GetRandomAbility();
z.Abilities = abils;
}
private void RandomizeEggGroups(PersonalInfo z)
{
var egg = z.EggGroups;
egg[0] = GetRandomEggGroup();
egg[1] = rnd.Next(0, 100) < SameEggGroupChance ? egg[0] : GetRandomEggGroup();
z.EggGroups = egg;
}
private void RandomizeHeldItems(PersonalInfo z)
{
var item = z.Items;
for (int j = 0; j < item.Length; j++)
item[j] = GetRandomHeldItem();
z.Items = item;
}
private void RandomizeTypes(PersonalInfo z)
{
var t = z.Types;
t[0] = GetRandomType();
t[1] = rnd.Next(0, 100) < SameTypeChance ? t[0] : GetRandomType();
z.Types = t;
}
private void RandomizeStats(PersonalInfo z)
{
// Fiddle with Base Stats, don't muck with Shedinja.
var stats = z.Stats;
if (stats[0] == 1)
return;
for (int i = 0; i < stats.Length; i++)
{
if (!StatsToRandomize[i])
continue;
var l = Math.Min(255, (int)(stats[i] * (1 - (StatDeviation / 100))));
var h = Math.Min(255, (int)(stats[i] * (1 + (StatDeviation / 100))));
stats[i] = Math.Max(5, rnd.Next(l, h));
}
z.Stats = stats;
}
private static void RandomShuffledStats(PersonalInfo z)
{
// Fiddle with Base Stats, don't muck with Shedinja.
var stats = z.Stats;
if (stats[0] == 1)
return;
Util.Shuffle(stats);
z.Stats = stats;
}
private int GetRandomType() => rnd.Next(0, TypeCount);
private int GetRandomEggGroup() => rnd.Next(1, eggGroupCount);
private int GetRandomHeldItem() => Game.Info.HeldItems[rnd.Next(1, Game.Info.HeldItems.Length)];
private readonly IReadOnlyList<int> BannedAbilities = [];
private int GetRandomAbility()
{
const int WonderGuard = 25;
int newabil;
do newabil = rnd.Next(1, Game.Info.MaxAbilityID + 1);
while ((newabil == WonderGuard && !AllowWonderGuard) || BannedAbilities.Contains(newabil));
return newabil;
}
}