diff --git a/pkNX.Randomization/Randomizers/LearnsetRandomizer.cs b/pkNX.Randomization/Randomizers/LearnsetRandomizer.cs
new file mode 100644
index 00000000..0b998485
--- /dev/null
+++ b/pkNX.Randomization/Randomizers/LearnsetRandomizer.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using pkNX.Structures;
+
+namespace pkNX.Randomization
+{
+ ///
+ /// randomizer.
+ ///
+ public class LearnsetRandomizer : Randomizer
+ {
+ private readonly Learnset[] Learnsets;
+ private readonly GameInfo Game;
+ public readonly MoveRandomizer moverand;
+ public PersonalTable Personal;
+ public Move[] Moves;
+
+ public LearnSettings Settings { get; set; }
+
+ public LearnsetRandomizer(GameInfo game, Learnset[] learnsets, PersonalTable t)
+ {
+ Game = game;
+ Learnsets = learnsets;
+ Personal = t;
+ }
+
+ private static readonly int[] MetronomeMove = { 118 };
+ private static readonly int[] MetronomeLevel = { 1 };
+
+ public void ExecuteMetronome()
+ {
+ foreach (var learn in Learnsets)
+ learn.Update(MetronomeMove, MetronomeLevel);
+ }
+
+ public void ExecuteExpandOnly()
+ {
+ foreach (var learn in Learnsets)
+ {
+ var count = learn.Count;
+ if (count == 0)
+ continue;
+ if (count >= Settings.ExpandTo)
+ continue;
+
+ int diff = Settings.ExpandTo - count;
+ var moves = learn.Moves;
+ Array.Resize(ref moves, Settings.ExpandTo);
+ var levels = learn.Moves;
+ Array.Resize(ref levels, Settings.ExpandTo);
+ for (int i = count; i < Settings.ExpandTo; i++)
+ {
+ moves[i] = 1;
+ levels[i] = Math.Min(100, levels[count - 1] + diff);
+ }
+ learn.Update(moves, levels);
+ }
+ }
+
+ public void Initialize(Move[] moves, LearnSettings settings)
+ {
+ Moves = moves;
+ Settings = settings;
+
+ moverand.STAB = settings.STAB;
+ moverand.STABPercent = settings.STABPercent;
+ }
+
+ public LearnsetRandomizer(Learnset[] sets)
+ {
+ moverand = new MoveRandomizer(Game, Moves, Personal);
+ Learnsets = sets;
+ Settings.STABPercent = 52.3m;
+ }
+
+ public IList BannedMoves { set => moverand.BannedMoves = value; }
+
+ public override void Execute()
+ {
+ for (var i = 0; i < Learnsets.Length; i++)
+ Randomize(Learnsets[i], i);
+ }
+
+ private void Randomize(Learnset set, int index)
+ {
+ int[] moves = GetRandomMoves(set.Count, index);
+ int[] levels = GetRandomLevels(set, moves.Length);
+
+ if (Settings.Learn4Level1)
+ {
+ for (int i = 0; i < Math.Min(4, levels.Length); ++i)
+ levels[i] = 1;
+ }
+
+ set.Update(moves, levels);
+ }
+
+ private int[] GetRandomLevels(Learnset set, int count)
+ {
+ int[] levels = new int[count];
+ if (count == 0)
+ return levels;
+ if (Settings.Spread)
+ {
+ levels[0] = 1;
+ decimal increment = Settings.SpreadTo / (decimal)count;
+ for (int i = 1; i < count; i++)
+ levels[i] = (int)(i * increment);
+ return levels;
+ }
+ if (levels.Length == count && levels.Length == set.Levels.Length)
+ return set.Levels; // don't modify
+
+ var exist = set.Levels;
+ int lastlevel = Math.Min(1, exist.LastOrDefault());
+ exist.CopyTo(levels, 0);
+ for (int i = exist.Length; i < levels.Length; i++)
+ levels[i] = Math.Max(100, lastlevel + (exist.Length - i + 1));
+
+ return levels;
+ }
+
+ private int[] GetRandomMoves(int count, int index)
+ {
+ count = Settings.Expand ? Settings.ExpandTo : count;
+
+ int[] moves = new int[count];
+ if (count == 0)
+ return moves;
+ moves[0] = Settings.STABFirst ? moverand.GetRandomFirstMove(index) : moverand.GetRandomFirstMoveAny();
+ var rand = moverand.GetRandomLearnset(index, count - 1);
+
+ // STAB Moves (if requested) come first; randomize the order of moves
+ Util.Shuffle(rand);
+ if (Settings.OrderByPower)
+ moverand.ReorderMovesPower(rand);
+ rand.CopyTo(moves, 1);
+ return moves;
+ }
+
+ public int[] GetHighPoweredMoves(int species, int form, int count = 4)
+ {
+ int index = Personal.GetFormeIndex(species, form);
+ var learn = Learnsets[index];
+ return learn.GetHighPoweredMoves(count, Moves);
+ }
+
+ public int[] GetCurrentMoves(int species, int form, int level, int count = 4)
+ {
+ int i = Personal.GetFormeIndex(species, form);
+ var moves = Learnsets[i].GetEncounterMoves(level);
+ Array.Resize(ref moves, count);
+ return moves;
+ }
+ }
+}
\ No newline at end of file
diff --git a/pkNX.Randomization/Randomizers/MoveRandomizer.cs b/pkNX.Randomization/Randomizers/MoveRandomizer.cs
new file mode 100644
index 00000000..3910c6d4
--- /dev/null
+++ b/pkNX.Randomization/Randomizers/MoveRandomizer.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using pkNX.Structures;
+
+namespace pkNX.Randomization
+{
+ public class MoveRandomizer : Randomizer
+ {
+ private readonly Move[] MoveData;
+ private readonly PersonalTable SpeciesStat;
+
+ private readonly GenericRandomizer RandMove;
+
+ public MoveRandomizer(GameInfo config, Move[] moves, PersonalTable t)
+ {
+ var maxMoveId = config.MaxMoveID;
+ MoveData = moves;
+ SpeciesStat = t;
+ RandMove = new GenericRandomizer(Enumerable.Range(1, maxMoveId - 1).ToArray());
+ }
+
+ public override void Execute() => throw new Exception("Shouldn't be called.");
+
+ public bool DMG = true;
+ public int DMGCount = 2;
+ public bool STAB = true;
+ public int STABCount = 2;
+ public decimal STABPercent = 100;
+ public IList BannedMoves = new int[0];
+ public static readonly int[] FixedDamageMoves = { 49, 82 };
+
+ public int[] GetRandomLearnset(int index, int movecount) => GetRandomLearnset(SpeciesStat[index].Types, movecount);
+
+ public int[] GetRandomLearnset(int[] Types, int movecount)
+ {
+ var oldSTABCount = STABCount;
+ STABCount = (int)(STABPercent * movecount / 100);
+ int[] moves = GetRandomMoveset(Types, movecount);
+ STABCount = oldSTABCount;
+ return moves;
+ }
+
+ public int[] GetRandomMoveset(int index, int movecount = 4) => GetRandomMoveset(SpeciesStat[index].Types, movecount);
+
+ public int[] GetRandomMoveset(int[] Types, int movecount = 4)
+ {
+ int loopctr = 0;
+ const int maxLoop = 666;
+
+ int[] moves;
+ do { moves = GetRandomMoves(Types, movecount); }
+ while (!IsMovesetMeetingRequirements(moves, movecount) && loopctr++ <= maxLoop);
+
+ return moves;
+ }
+
+ private int[] GetRandomMoves(int[] Types, int movecount = 4)
+ {
+ int[] moves = new int[movecount];
+ if (STAB)
+ {
+ for (int i = 0; i < STABCount; i++)
+ moves[i] = GetRandomSTABMove(Types);
+ }
+
+ for (int i = 0; i < moves.Length; i++) // remainder of moves
+ moves[i] = RandMove.Next();
+ return moves;
+ }
+
+ private int GetRandomSTABMove(int[] types)
+ {
+ int move;
+ do { move = RandMove.Next(); }
+ while (!types.Contains(MoveData[move].Type));
+ return move;
+ }
+
+ private bool IsMovesetMeetingRequirements(int[] moves, int count)
+ {
+ if (DMG && DMGCount > moves.Count(move => MoveData[move].Category != 0))
+ return false;
+
+ if (moves.Any(BannedMoves.Contains))
+ return false;
+
+ return moves.Distinct().Count() == count;
+ }
+
+ public void ReorderMovesPower(IList moves)
+ {
+ 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.Select(z => z.Index).ToList();
+ 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 first = new GenericRandomizer(firstMoves);
+
+ public int GetRandomFirstMoveAny()
+ {
+ first.Reset();
+ return first.Next();
+ }
+
+ public int GetRandomFirstMove(int index) => GetRandomFirstMove(SpeciesStat[index].Types);
+
+ public int GetRandomFirstMove(int[] types)
+ {
+ first.Reset();
+ int ctr = 0;
+ int move;
+ do
+ {
+ move = first.Next();
+ if (++ctr == firstMoves.Length)
+ return move;
+ } while (!types.Contains(MoveData[move].Type));
+ return move;
+ }
+
+ public bool SanitizeMovesetForBannedMoves(int[] moves, int index)
+ {
+ bool updated = false;
+ for (int m = 0; m < moves.Length; m++)
+ {
+ if (!BannedMoves.Contains(moves[m]))
+ continue;
+ updated = true;
+ moves[m] = GetRandomFirstMove(index);
+ }
+
+ return updated;
+ }
+ }
+}
\ No newline at end of file
diff --git a/pkNX.Randomization/Randomizers/Settings/LearnSettings.cs b/pkNX.Randomization/Randomizers/Settings/LearnSettings.cs
new file mode 100644
index 00000000..00d263b1
--- /dev/null
+++ b/pkNX.Randomization/Randomizers/Settings/LearnSettings.cs
@@ -0,0 +1,34 @@
+using System.ComponentModel;
+
+namespace pkNX.Randomization
+{
+ public class LearnSettings
+ {
+ private const string General = nameof(General);
+ private const string Misc = nameof(Misc);
+
+ [Category(General), Description("Expands the learnset to the specified count.")]
+ public bool Expand { get; set; } = true;
+ [Category(General), Description("Count to expand the learnset to.")]
+ public int ExpandTo { get; set; } = 25;
+
+ [Category(General), Description("Evenly spreads learned moves out from level 1 to the specified end level.")]
+ public bool Spread { get; set; } = true;
+ [Category(General), Description("Level to end learning level up moves.")]
+ public int SpreadTo { get; set; } = 75;
+
+ [Category(General), Description("Requires a certain percent of moves to have STAB.")]
+ public bool STAB { get; set; }
+ [Category(General), Description("Required percent of moves having STAB.")]
+ public decimal STABPercent { get; set; }
+
+ [Category(Misc), Description("Reorders moves so that moves are learned with increasing power.")]
+ public bool OrderByPower { get; set; } = true;
+
+ [Category(Misc), Description("Requires the first move learned to be STAB.")]
+ public bool STABFirst { get; set; } = true;
+
+ [Category(Misc), Description("Requires 4 moves to be available at level 1.")]
+ public bool Learn4Level1 { get; set; } = false;
+ }
+}
\ No newline at end of file
diff --git a/pkNX.Randomization/Randomizers/RandSettings.cs b/pkNX.Randomization/Randomizers/Settings/RandSettings.cs
similarity index 100%
rename from pkNX.Randomization/Randomizers/RandSettings.cs
rename to pkNX.Randomization/Randomizers/Settings/RandSettings.cs
diff --git a/pkNX.Randomization/Randomizers/SpeciesSettings.cs b/pkNX.Randomization/Randomizers/Settings/SpeciesSettings.cs
similarity index 100%
rename from pkNX.Randomization/Randomizers/SpeciesSettings.cs
rename to pkNX.Randomization/Randomizers/Settings/SpeciesSettings.cs
diff --git a/pkNX.Randomization/SpeciesSetting.cs b/pkNX.Randomization/SpeciesSetting.cs
deleted file mode 100644
index e928ef59..00000000
--- a/pkNX.Randomization/SpeciesSetting.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using pkNX.Structures;
-
-namespace pkNX.Randomization
-{
- public class SpeciesSetting
- {
- public bool Gen1 { get; set; }
- public bool Gen2 { get; set; }
- public bool Gen3 { get; set; }
- public bool Gen4 { get; set; }
- public bool Gen5 { get; set; }
- public bool Gen6 { get; set; }
- public bool Gen7 { get; set; }
-
- public bool L { get; set; } = false;
- public bool E { get; set; } = false;
- public bool Shedinja = false;
- public bool EXP { get; set; } = false;
- public bool BST { get; set; } = true;
- public bool Type { get; set; } = false;
-
- private readonly GameVersion Game;
- private readonly int Generation;
-
- public SpeciesSetting(GameVersion game)
- {
- Game = game;
- Generation = Game.GetGeneration();
- Gen1 = Generation <= 1;
- Gen2 = Generation <= 2;
- Gen3 = Generation <= 3;
- Gen4 = Generation <= 4;
- Gen5 = Generation <= 5;
- Gen6 = Generation <= 6;
- Gen7 = Generation <= 7;
- }
- }
-}
diff --git a/pkNX.Structures/Learnset/Learnset.cs b/pkNX.Structures/Learnset/Learnset.cs
index 188953e8..efd95114 100644
--- a/pkNX.Structures/Learnset/Learnset.cs
+++ b/pkNX.Structures/Learnset/Learnset.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
namespace pkNX.Structures
{
@@ -85,5 +86,12 @@ public void Update(int[] moves, int[] levels)
Levels = levels;
Count = Moves.Length;
}
+
+ public int[] GetHighPoweredMoves(int count, Move[] movedata)
+ {
+ var moves = Moves.OrderByDescending(move => movedata[move].Power).Distinct().Take(count).ToArray();
+ Array.Resize(ref moves, count);
+ return moves;
+ }
}
}
diff --git a/pkNX.WinForms/Subforms/PokeDataUI.Designer.cs b/pkNX.WinForms/Subforms/PokeDataUI.Designer.cs
index 036d75e7..35612892 100644
--- a/pkNX.WinForms/Subforms/PokeDataUI.Designer.cs
+++ b/pkNX.WinForms/Subforms/PokeDataUI.Designer.cs
@@ -126,14 +126,16 @@ private void InitializeComponent()
this.B_RandPersonal = new System.Windows.Forms.Button();
this.PG_Personal = new System.Windows.Forms.PropertyGrid();
this.Tab_RLearn = new System.Windows.Forms.TabPage();
+ this.B_LearnMetronome = new System.Windows.Forms.Button();
+ this.B_LearnExpand = new System.Windows.Forms.Button();
+ this.B_RandLearn = new System.Windows.Forms.Button();
+ this.PG_Learn = new System.Windows.Forms.PropertyGrid();
this.Tab_REvo = new System.Windows.Forms.TabPage();
this.B_TradeEvo = new System.Windows.Forms.Button();
this.B_RandEvo = new System.Windows.Forms.Button();
this.PG_Evolution = new System.Windows.Forms.PropertyGrid();
this.CB_Species = new System.Windows.Forms.ComboBox();
this.PB_MonSprite = new System.Windows.Forms.PictureBox();
- this.B_RandLearn = new System.Windows.Forms.Button();
- this.PG_Learn = new System.Windows.Forms.PropertyGrid();
this.tabControl1.SuspendLayout();
this.Tab_Personal.SuspendLayout();
this.Tab_Learn.SuspendLayout();
@@ -1175,6 +1177,8 @@ private void InitializeComponent()
//
// Tab_RLearn
//
+ this.Tab_RLearn.Controls.Add(this.B_LearnMetronome);
+ this.Tab_RLearn.Controls.Add(this.B_LearnExpand);
this.Tab_RLearn.Controls.Add(this.B_RandLearn);
this.Tab_RLearn.Controls.Add(this.PG_Learn);
this.Tab_RLearn.Location = new System.Drawing.Point(4, 22);
@@ -1184,6 +1188,46 @@ private void InitializeComponent()
this.Tab_RLearn.Text = "Randomize Learn";
this.Tab_RLearn.UseVisualStyleBackColor = true;
//
+ // B_LearnMetronome
+ //
+ this.B_LearnMetronome.Location = new System.Drawing.Point(228, 80);
+ this.B_LearnMetronome.Name = "B_LearnMetronome";
+ this.B_LearnMetronome.Size = new System.Drawing.Size(135, 23);
+ this.B_LearnMetronome.TabIndex = 5;
+ this.B_LearnMetronome.Text = "Metronome Mode";
+ this.B_LearnMetronome.UseVisualStyleBackColor = true;
+ this.B_LearnMetronome.Click += new System.EventHandler(this.B_LearnMetronome_Click);
+ //
+ // B_LearnExpand
+ //
+ this.B_LearnExpand.Location = new System.Drawing.Point(87, 80);
+ this.B_LearnExpand.Name = "B_LearnExpand";
+ this.B_LearnExpand.Size = new System.Drawing.Size(135, 23);
+ this.B_LearnExpand.TabIndex = 4;
+ this.B_LearnExpand.Text = "Expand Movepools";
+ this.B_LearnExpand.UseVisualStyleBackColor = true;
+ this.B_LearnExpand.Click += new System.EventHandler(this.B_LearnExpand_Click);
+ //
+ // B_RandLearn
+ //
+ this.B_RandLearn.Location = new System.Drawing.Point(6, 80);
+ this.B_RandLearn.Name = "B_RandLearn";
+ this.B_RandLearn.Size = new System.Drawing.Size(75, 23);
+ this.B_RandLearn.TabIndex = 3;
+ this.B_RandLearn.Text = "Randomize";
+ this.B_RandLearn.UseVisualStyleBackColor = true;
+ this.B_RandLearn.Click += new System.EventHandler(this.B_RandLearn_Click);
+ //
+ // PG_Learn
+ //
+ this.PG_Learn.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.PG_Learn.Location = new System.Drawing.Point(6, 109);
+ this.PG_Learn.Name = "PG_Learn";
+ this.PG_Learn.Size = new System.Drawing.Size(411, 350);
+ this.PG_Learn.TabIndex = 2;
+ //
// Tab_REvo
//
this.Tab_REvo.Controls.Add(this.B_TradeEvo);
@@ -1245,26 +1289,6 @@ private void InitializeComponent()
this.PB_MonSprite.TabIndex = 91;
this.PB_MonSprite.TabStop = false;
//
- // B_RandLearn
- //
- this.B_RandLearn.Location = new System.Drawing.Point(6, 80);
- this.B_RandLearn.Name = "B_RandLearn";
- this.B_RandLearn.Size = new System.Drawing.Size(75, 23);
- this.B_RandLearn.TabIndex = 3;
- this.B_RandLearn.Text = "Randomize";
- this.B_RandLearn.UseVisualStyleBackColor = true;
- this.B_RandLearn.Click += new System.EventHandler(this.B_RandLearn_Click);
- //
- // PG_Learn
- //
- this.PG_Learn.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
- | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.PG_Learn.Location = new System.Drawing.Point(6, 109);
- this.PG_Learn.Name = "PG_Learn";
- this.PG_Learn.Size = new System.Drawing.Size(411, 350);
- this.PG_Learn.TabIndex = 2;
- //
// PokeDataUI
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -1403,5 +1427,7 @@ private void InitializeComponent()
private System.Windows.Forms.Button B_TradeEvo;
private System.Windows.Forms.Button B_RandLearn;
private System.Windows.Forms.PropertyGrid PG_Learn;
+ private System.Windows.Forms.Button B_LearnExpand;
+ private System.Windows.Forms.Button B_LearnMetronome;
}
}
\ No newline at end of file
diff --git a/pkNX.WinForms/Subforms/PokeDataUI.cs b/pkNX.WinForms/Subforms/PokeDataUI.cs
index 9bb2ee4f..700b1adf 100644
--- a/pkNX.WinForms/Subforms/PokeDataUI.cs
+++ b/pkNX.WinForms/Subforms/PokeDataUI.cs
@@ -52,7 +52,7 @@ public PokeDataUI(PokeEditor editor, GameManager rom)
PG_Personal.SelectedObject = new PersonalRandSettings();
PG_Evolution.SelectedObject = new SpeciesSettings();
- PG_Learn.SelectedObject = null;
+ PG_Learn.SelectedObject = new LearnSettings();
}
public GameManager ROM { get; set; }
@@ -469,7 +469,39 @@ private void B_TradeEvo_Click(object sender, EventArgs e)
private void B_RandLearn_Click(object sender, EventArgs e)
{
- WinFormsUtil.Alert("Not implemented yet.");
+ SaveCurrent();
+ var settings = (LearnSettings)PG_Learn.SelectedObject;
+ var rand = new LearnsetRandomizer(ROM.Info, Editor.Learn.LoadAll(), Editor.Personal);
+ rand.Initialize(ROM.Data.MoveData.LoadAll(), settings);
+ rand.Execute();
+ LoadIndex(CB_Species.SelectedIndex);
+ System.Media.SystemSounds.Asterisk.Play();
+ }
+
+ private void B_LearnExpand_Click(object sender, EventArgs e)
+ {
+ var settings = (LearnSettings)PG_Learn.SelectedObject;
+ if (!settings.Expand)
+ {
+ WinFormsUtil.Error("Expand moves not selected. Please double check settings.",
+ "Not expanding learnsets.");
+ return;
+ }
+ var rand = new LearnsetRandomizer(ROM.Info, Editor.Learn.LoadAll(), Editor.Personal);
+ rand.Initialize(ROM.Data.MoveData.LoadAll(), settings);
+ rand.ExecuteExpandOnly();
+ LoadIndex(CB_Species.SelectedIndex);
+ System.Media.SystemSounds.Asterisk.Play();
+ }
+
+ private void B_LearnMetronome_Click(object sender, EventArgs e)
+ {
+ var settings = (LearnSettings)PG_Learn.SelectedObject;
+ var rand = new LearnsetRandomizer(ROM.Info, Editor.Learn.LoadAll(), Editor.Personal);
+ rand.Initialize(ROM.Data.MoveData.LoadAll(), settings);
+ rand.ExecuteMetronome();
+ LoadIndex(CB_Species.SelectedIndex);
+ System.Media.SystemSounds.Asterisk.Play();
}
private void B_Save_Click(object sender, EventArgs e)