mirror of
https://github.com/kwsch/pkNX.git
synced 2026-09-12 12:35:51 -05:00
Standardize using FormRandomizer for all randomizers
This commit is contained in:
@@ -10,14 +10,16 @@ public class EvolutionRandomizer : Randomizer
|
||||
private readonly EvolutionSet[] Evolutions;
|
||||
private readonly GameInfo Game;
|
||||
private readonly PersonalTable Personal;
|
||||
public readonly SpeciesRandomizer Randomizer;
|
||||
public readonly SpeciesRandomizer RandSpec;
|
||||
public readonly FormRandomizer RandForm;
|
||||
|
||||
public EvolutionRandomizer(GameInfo game, EvolutionSet[] evolutions, PersonalTable t)
|
||||
{
|
||||
Game = game;
|
||||
Personal = t;
|
||||
Evolutions = evolutions;
|
||||
Randomizer = new SpeciesRandomizer(Game, t);
|
||||
RandSpec = new SpeciesRandomizer(Game, t);
|
||||
RandForm = new FormRandomizer(t);
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
@@ -44,8 +46,8 @@ private void Randomize(EvolutionSet evos, int species)
|
||||
{
|
||||
if (evo.Method != 0)
|
||||
{
|
||||
evo.Species = Randomizer.GetRandomSpecies(evo.Species, species);
|
||||
evo.Form = Legal.GetRandomForme(evo.Species, false, true, Game.SWSH, Personal);
|
||||
evo.Species = RandSpec.GetRandomSpecies(evo.Species, species);
|
||||
evo.Form = RandForm.GetRandomForme(evo.Species, false, true, Game.SWSH, Personal.Table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using pkNX.Structures;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using static pkNX.Structures.Species;
|
||||
|
||||
@@ -13,11 +14,7 @@ public FormRandomizer(PersonalTable t)
|
||||
Personal = t;
|
||||
}
|
||||
|
||||
public bool AllowMega { get; set; } = false;
|
||||
public bool AllowAlolanForm { get; set; } = true;
|
||||
public bool AllowGalarianForm { get; set; } = true;
|
||||
|
||||
public int GetRandomForme(int species, PersonalInfo[] stats = null)
|
||||
public int GetRandomForme(int species, bool mega, bool alola, bool galar, PersonalInfo[] stats = null)
|
||||
{
|
||||
if (stats == null)
|
||||
stats = Personal.Table;
|
||||
@@ -30,7 +27,7 @@ public int GetRandomForme(int species, PersonalInfo[] stats = null)
|
||||
case Deerling:
|
||||
case Sawsbuck:
|
||||
return 31; // Random
|
||||
case Greninja when !AllowMega:
|
||||
case Greninja when !mega:
|
||||
case Eternatus:
|
||||
return 0;
|
||||
case Scatterbug:
|
||||
@@ -41,26 +38,42 @@ public int GetRandomForme(int species, PersonalInfo[] stats = null)
|
||||
return Util.Random.Next(7); // keep the core color a surprise
|
||||
|
||||
// Galarian Forms
|
||||
case Meowth when AllowGalarianForm: // Kanto, Alola, Galar
|
||||
case Meowth when galar: // Kanto, Alola, Galar
|
||||
return Util.Random.Next(3);
|
||||
case Darmanitan when AllowGalarianForm: // Standard, Zen, Galar Standard, Galar Zen
|
||||
case Darmanitan when galar: // Standard, Zen, Galar Standard, Galar Zen
|
||||
return Util.Random.Next(4);
|
||||
case Slowbro when AllowGalarianForm: // Slowbro-1 is Mega and Slowbro-2 is Galar, so only return 0 or 2
|
||||
{
|
||||
int form = Util.Random.Next(2);
|
||||
if (form == 1)
|
||||
form++;
|
||||
return form;
|
||||
}
|
||||
|
||||
// some species have 1 invalid form among several other valid forms, handle them here
|
||||
case Pikachu when Personal.TableLength == 1181:
|
||||
case Slowbro when galar:
|
||||
{
|
||||
int form = Util.Random.Next(stats[species].FormeCount - 1);
|
||||
int banned = GetInvalidForm(species, galar, Personal);
|
||||
if (form == banned)
|
||||
form++;
|
||||
return form;
|
||||
}
|
||||
}
|
||||
|
||||
if (AllowAlolanForm && Legal.EvolveToAlolanForms.Contains(species))
|
||||
if (Personal.TableLength == 980 && (species == (int)Pikachu || species == (int)Eevee)) // gg tableB -- no starters, they crash trainer battles.
|
||||
return 0;
|
||||
if (alola && Legal.EvolveToAlolanForms.Contains(species))
|
||||
return Util.Random.Next(2);
|
||||
if (AllowGalarianForm && Legal.EvolveToGalarForms.Contains(species))
|
||||
if (galar && Legal.EvolveToGalarForms.Contains(species))
|
||||
return Util.Random.Next(2);
|
||||
if (!Legal.BattleExclusiveForms.Contains(species) || AllowMega)
|
||||
if (!Legal.BattleExclusiveForms.Contains(species) || mega)
|
||||
return Util.Random.Next(stats[species].FormeCount); // Slot-Random
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int GetInvalidForm(int species, bool galar, PersonalTable stats)
|
||||
{
|
||||
return species switch
|
||||
{
|
||||
(int)Pikachu when stats.TableLength == 1181 => 8, // LGPE Partner Pikachu
|
||||
(int)Slowbro when galar => 1, // Mega Slowbro
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(species))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public class TrainerRandomizer : Randomizer
|
||||
public GenericRandomizer<int> Class { get; set; }
|
||||
public LearnsetRandomizer Learn { get; set; }
|
||||
public SpeciesRandomizer RandSpec { get; set; }
|
||||
public FormRandomizer RandForm { get; set; }
|
||||
public MoveRandomizer RandMove { get; set; }
|
||||
public int ClassCount { get; set; }
|
||||
public Func<TrainerPoke> GetBlank { get; set; }
|
||||
@@ -161,7 +162,7 @@ private void RandomizeSpecFormItem(IPokeData pk, int Type)
|
||||
else // every other pkm
|
||||
{
|
||||
pk.Species = RandSpec.GetRandomSpeciesType(pk.Species, Type);
|
||||
pk.Form = Legal.GetRandomForme(pk.Species, Settings.AllowRandomMegaForms, true, true, Personal);
|
||||
pk.Form = RandForm.GetRandomForme(pk.Species, Settings.AllowRandomMegaForms, true, true, Personal.Table);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +181,7 @@ private void RandomizeSpecForm(TrainerPoke7b pk, int type)
|
||||
else
|
||||
{
|
||||
pk.Species = RandSpec.GetRandomSpeciesType(pk.Species, type);
|
||||
pk.Form = Legal.GetRandomForme(pk.Species, Settings.AllowRandomMegaForms, true, false, Personal);
|
||||
pk.Form = RandForm.GetRandomForme(pk.Species, Settings.AllowRandomMegaForms, true, false, Personal.Table);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +255,7 @@ private void UpdatePKMFromSettings(TrainerPoke pk)
|
||||
return;
|
||||
|
||||
c.Species = AllowedGigantamaxes[Util.Random.Next(AllowedGigantamaxes.Length)];
|
||||
c.Form = c.Species == (int)Species.Pikachu || c.Species == (int)Species.Meowth ? 0 : Legal.GetRandomForme(c.Species, false, false, false, Personal); // Pikachu & Meowth altforms can't gmax
|
||||
c.Form = c.Species == (int)Species.Pikachu || c.Species == (int)Species.Meowth ? 0 : RandForm.GetRandomForme(c.Species, false, false, false, Personal.Table); // Pikachu & Meowth altforms can't gmax
|
||||
}
|
||||
if (Settings.MaxDynamaxLevel && c.CanDynamax)
|
||||
c.DynamaxLevel = 10;
|
||||
|
||||
@@ -6,58 +6,6 @@ namespace pkNX.Structures
|
||||
{
|
||||
public static partial class Legal
|
||||
{
|
||||
public static int GetRandomForme(int species, bool mega, bool alola, bool galar, PersonalTable stats)
|
||||
{
|
||||
if (stats == null)
|
||||
return 0;
|
||||
if (stats[species].FormeCount <= 1)
|
||||
return 0;
|
||||
|
||||
if (species == (int)Species.Unown || species == (int)Species.Deerling || species == (int)Species.Sawsbuck)
|
||||
return 31; // Random
|
||||
if ((species == (int)Species.Greninja && !mega) || species == (int)Species.Eternatus)
|
||||
return 0;
|
||||
if (species == (int)Species.Scatterbug || species == (int)Species.Spewpa || species == (int)Species.Vivillon)
|
||||
return 30; // save file specific
|
||||
if (species == (int)Species.Minior)
|
||||
return Util.Rand.Next(7); // keep the core color a surprise
|
||||
|
||||
if (galar)
|
||||
{
|
||||
switch ((Species)species)
|
||||
{
|
||||
case Species.Meowth:
|
||||
return Util.Rand.Next(3); // Kanto, Alola, Galar
|
||||
case Species.Darmanitan:
|
||||
return Util.Rand.Next(4); // Standard, Zen, Galar Standard, Galar Zen
|
||||
case Species.Slowbro:
|
||||
{
|
||||
int form = Util.Rand.Next(2); // Slowbro-1 is Mega and Slowbro-2 is Galar, so only return 0 or 2
|
||||
if (form == 1)
|
||||
form++;
|
||||
return form;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.TableLength == 980 && (species == (int)Species.Pikachu || species == (int)Species.Eevee)) // gg tableB -- no starters, they crash trainer battles.
|
||||
return 0;
|
||||
if (stats.TableLength == 1181 && species == (int)Species.Pikachu) // SWSH -- disallow Partner Pikachu from LGPE, but allow World Cap
|
||||
{
|
||||
int form = Util.Rand.Next(9);
|
||||
if (form == 8)
|
||||
form++;
|
||||
return form;
|
||||
}
|
||||
if (EvolveToAlolanForms.Contains(species))
|
||||
return alola ? Util.Rand.Next(2) : 0;
|
||||
if (EvolveToGalarForms.Contains(species))
|
||||
return galar ? Util.Rand.Next(2) : 0;
|
||||
if (!BattleExclusiveForms.Contains(species) || mega)
|
||||
return Util.Rand.Next(stats[species].FormeCount); // Slot-Random
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the current level with a scaling factor, returning a modified level.
|
||||
/// </summary>
|
||||
@@ -176,6 +124,7 @@ public static int[] GetRandomItemList(GameVersion game)
|
||||
{362, new[] {763}}, // Glalie @ Glalitite
|
||||
{373, new[] {769}}, // Salamence @ Salamencite
|
||||
{376, new[] {758}}, // Metagross @ Metagrossite
|
||||
// Rayquaza requires Dragon Ascent, no Held Item
|
||||
{428, new[] {768}}, // Lopunny @ Lopunnite
|
||||
{475, new[] {756}}, // Gallade @ Galladite
|
||||
{531, new[] {757}}, // Audino @ Audinite
|
||||
|
||||
@@ -136,7 +136,7 @@ public PersonalInfo GetFormeEntry(int species, int forme)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Count of entries in the table, which includes default species entries and their separate AltForm ID entreis.
|
||||
/// Count of entries in the table, which includes default species entries and their separate AltForm ID entries.
|
||||
/// </summary>
|
||||
public int TableLength => Table.Length;
|
||||
|
||||
|
||||
@@ -120,11 +120,12 @@ void Randomize()
|
||||
var spec = EditUtil.Settings.Species;
|
||||
spec.Gen2 = spec.Gen3 = spec.Gen4 = spec.Gen5 = spec.Gen6 = spec.Gen7 = false;
|
||||
var srand = new SpeciesRandomizer(ROM.Info, ROM.Data.PersonalData);
|
||||
var frand = new FormRandomizer(ROM.Data.PersonalData);
|
||||
srand.Initialize(spec);
|
||||
foreach (var t in objs)
|
||||
{
|
||||
t.Species = (Species)srand.GetRandomSpecies((int)t.Species);
|
||||
t.Form = Legal.GetRandomForme((int)t.Species, false, true, false, ROM.Data.PersonalData);
|
||||
t.Form = frand.GetRandomForme((int)t.Species, false, true, false, ROM.Data.PersonalData.Table);
|
||||
t.Nature = Nature.Random25;
|
||||
t.Gender = FixedGender.Random;
|
||||
t.Shiny = Shiny.Random;
|
||||
@@ -155,12 +156,13 @@ void Randomize()
|
||||
var spec = EditUtil.Settings.Species;
|
||||
spec.Gen2 = spec.Gen3 = spec.Gen4 = spec.Gen5 = spec.Gen6 = spec.Gen7 = false;
|
||||
var srand = new SpeciesRandomizer(ROM.Info, ROM.Data.PersonalData);
|
||||
var frand = new FormRandomizer(ROM.Data.PersonalData);
|
||||
srand.Initialize(spec, 808, 809); // can only catch 1-151 in wild
|
||||
foreach (var t in objs)
|
||||
{
|
||||
t.Species = (Species)srand.GetRandomSpecies((int)t.Species);
|
||||
t.RequiredSpecies = (Species)srand.GetRandomSpecies((int)t.Species);
|
||||
t.Form = Legal.GetRandomForme((int)t.Species, false, true, false, ROM.Data.PersonalData);
|
||||
t.Form = frand.GetRandomForme((int)t.Species, false, true, false, ROM.Data.PersonalData.Table);
|
||||
t.RequiredForm = 0; // can't catch wild alolan forms
|
||||
t.Nature = Nature.Random - 1;
|
||||
t.Gender = FixedGender.Random;
|
||||
@@ -192,12 +194,13 @@ void Randomize()
|
||||
var spec = EditUtil.Settings.Species;
|
||||
spec.Gen2 = spec.Gen3 = spec.Gen4 = spec.Gen5 = spec.Gen6 = spec.Gen7 = false;
|
||||
var srand = new SpeciesRandomizer(ROM.Info, ROM.Data.PersonalData);
|
||||
var frand = new FormRandomizer(ROM.Data.PersonalData);
|
||||
srand.Initialize(spec);
|
||||
for (int i = 2; i < objs.Length; i++) // skip starters
|
||||
{
|
||||
var t = objs[i];
|
||||
t.Species = (Species)srand.GetRandomSpecies((int)t.Species);
|
||||
t.Form = Legal.GetRandomForme((int)t.Species, false, true, false, ROM.Data.PersonalData);
|
||||
t.Form = frand.GetRandomForme((int)t.Species, false, true, false, ROM.Data.PersonalData.Table);
|
||||
t.Nature = Nature.Random25;
|
||||
t.Gender = FixedGender.Random;
|
||||
t.Shiny = Shiny.Random;
|
||||
|
||||
@@ -237,13 +237,14 @@ void Randomize()
|
||||
|
||||
var spec = EditUtil.Settings.Species;
|
||||
var srand = new SpeciesRandomizer(ROM.Info, ROM.Data.PersonalData);
|
||||
var frand = new FormRandomizer(ROM.Data.PersonalData);
|
||||
srand.Initialize(spec, ban);
|
||||
foreach (var t in arr)
|
||||
{
|
||||
foreach (var p in t.Entries)
|
||||
{
|
||||
p.Species = srand.GetRandomSpecies(p.Species);
|
||||
p.AltForm = Legal.GetRandomForme(p.Species, false, true, true, ROM.Data.PersonalData);
|
||||
p.AltForm = frand.GetRandomForme(p.Species, false, true, true, ROM.Data.PersonalData.Table);
|
||||
p.Ability = 4; // "A4" -- 1, 2, or H
|
||||
p.Gender = 0; // random
|
||||
p.IsGigantamax = false; // don't allow gmax flag on non-gmax species
|
||||
@@ -278,9 +279,7 @@ void Randomize()
|
||||
foreach (var t in arr)
|
||||
{
|
||||
foreach (var i in t.Entries)
|
||||
{
|
||||
i.ItemID = (uint)PossibleHeldItems[Randomization.Util.Random.Next(PossibleHeldItems.Length)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,9 +309,7 @@ void Randomize()
|
||||
foreach (var t in arr)
|
||||
{
|
||||
foreach (var i in t.Entries)
|
||||
{
|
||||
i.ItemID = (uint)PossibleHeldItems[Randomization.Util.Random.Next(PossibleHeldItems.Length)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,13 +342,14 @@ void Randomize()
|
||||
|
||||
var spec = EditUtil.Settings.Species;
|
||||
var srand = new SpeciesRandomizer(ROM.Info, ROM.Data.PersonalData);
|
||||
var frand = new FormRandomizer(ROM.Data.PersonalData);
|
||||
srand.Initialize(spec, ban);
|
||||
foreach (var t in encounters)
|
||||
{
|
||||
if (t.Species >= Species.Zacian && t.Species <= Species.Eternatus) // Eternatus crashes when changed, keep Zacian and Zamazenta to make final boss battle fair
|
||||
continue;
|
||||
t.Species = (Species)srand.GetRandomSpecies((int)t.Species);
|
||||
t.AltForm = Legal.GetRandomForme((int)t.Species, false, true, true, ROM.Data.PersonalData);
|
||||
t.AltForm = frand.GetRandomForme((int)t.Species, false, true, true, ROM.Data.PersonalData.Table);
|
||||
t.Ability = Randomization.Util.Random.Next(1, 4); // 1, 2, or H
|
||||
t.HeldItem = PossibleHeldItems[Randomization.Util.Random.Next(PossibleHeldItems.Length)];
|
||||
t.Nature = Nature.Random25;
|
||||
@@ -391,6 +389,7 @@ void Randomize()
|
||||
|
||||
var spec = EditUtil.Settings.Species;
|
||||
var srand = new SpeciesRandomizer(ROM.Info, ROM.Data.PersonalData);
|
||||
var frand = new FormRandomizer(ROM.Data.PersonalData);
|
||||
srand.Initialize(spec, ban);
|
||||
foreach (var t in gifts)
|
||||
{
|
||||
@@ -398,12 +397,12 @@ void Randomize()
|
||||
if (t.CanGigantamax)
|
||||
{
|
||||
t.Species = (Species)Legal.GigantamaxForms[Randomization.Util.Random.Next(Legal.GigantamaxForms.Length)];
|
||||
t.AltForm = t.Species == Species.Pikachu || t.Species == Species.Meowth ? 0 : Legal.GetRandomForme((int)t.Species, false, false, false, ROM.Data.PersonalData); // Pikachu & Meowth altforms can't gmax
|
||||
t.AltForm = t.Species == Species.Pikachu || t.Species == Species.Meowth ? 0 : frand.GetRandomForme((int)t.Species, false, false, false, ROM.Data.PersonalData.Table); // Pikachu & Meowth altforms can't gmax
|
||||
}
|
||||
else
|
||||
{
|
||||
t.Species = (Species)srand.GetRandomSpecies((int)t.Species);
|
||||
t.AltForm = Legal.GetRandomForme((int)t.Species, false, true, true, ROM.Data.PersonalData);
|
||||
t.AltForm = frand.GetRandomForme((int)t.Species, false, true, true, ROM.Data.PersonalData.Table);
|
||||
}
|
||||
|
||||
t.Ability = Randomization.Util.Random.Next(1, 4); // 1, 2, or H
|
||||
@@ -445,12 +444,13 @@ void Randomize()
|
||||
|
||||
var spec = EditUtil.Settings.Species;
|
||||
var srand = new SpeciesRandomizer(ROM.Info, ROM.Data.PersonalData);
|
||||
var frand = new FormRandomizer(ROM.Data.PersonalData);
|
||||
srand.Initialize(spec, ban);
|
||||
foreach (var t in trades)
|
||||
{
|
||||
// what you receive
|
||||
t.Species = (Species)srand.GetRandomSpecies((int)t.Species);
|
||||
t.AltForm = Legal.GetRandomForme((int)t.Species, false, true, true, ROM.Data.PersonalData);
|
||||
t.AltForm = frand.GetRandomForme((int)t.Species, false, true, true, ROM.Data.PersonalData.Table);
|
||||
t.Ability = Randomization.Util.Random.Next(1, 4); // 1, 2, or H
|
||||
t.Ball = (Ball)Randomization.Util.Random.Next(1, 15); // packed bit, only allows for 15 balls
|
||||
t.HeldItem = PossibleHeldItems[Randomization.Util.Random.Next(PossibleHeldItems.Length)];
|
||||
@@ -463,7 +463,7 @@ void Randomize()
|
||||
|
||||
// what you trade
|
||||
t.RequiredSpecies = (Species)srand.GetRandomSpecies((int)t.RequiredSpecies);
|
||||
t.RequiredForm = Legal.GetRandomForme((int)t.RequiredSpecies, false, true, true, ROM.Data.PersonalData);
|
||||
t.RequiredForm = frand.GetRandomForme((int)t.RequiredSpecies, false, true, true, ROM.Data.PersonalData.Table);
|
||||
t.RequiredNature = Nature.Random25; // any
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,6 +595,7 @@ private TrainerRandomizer GetRandomizer()
|
||||
}
|
||||
|
||||
var rspec = new SpeciesRandomizer(Game.Info, Personal);
|
||||
var rform = new FormRandomizer(Personal);
|
||||
rspec.Initialize((SpeciesSettings)PG_Species.SelectedObject, ban);
|
||||
learn.Moves = moves;
|
||||
var evos = Game.Data.EvolutionData;
|
||||
@@ -604,6 +605,7 @@ private TrainerRandomizer GetRandomizer()
|
||||
Learn = learn,
|
||||
RandMove = rmove,
|
||||
RandSpec = rspec,
|
||||
RandForm = rform,
|
||||
GetBlank = () => (Game.Info.SWSH ? (TrainerPoke)new TrainerPoke8() : new TrainerPoke7b()), // this should probably be less specific
|
||||
};
|
||||
trand.Initialize((TrainerRandSettings)PG_RTrainer.SelectedObject, (SpeciesSettings)PG_Species.SelectedObject);
|
||||
|
||||
@@ -489,7 +489,7 @@ private void B_RandEvo_Click(object sender, EventArgs e)
|
||||
.Where(z => !z.Present).Select(z => z.Species).ToArray();
|
||||
}
|
||||
|
||||
rand.Randomizer.Initialize(settings, ban);
|
||||
rand.RandSpec.Initialize(settings, ban);
|
||||
rand.Execute();
|
||||
LoadIndex(CB_Species.SelectedIndex);
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
@@ -502,7 +502,7 @@ private void B_TradeEvo_Click(object sender, EventArgs e)
|
||||
if (ROM.Info.GG)
|
||||
settings.Gen2 = settings.Gen3 = settings.Gen4 = settings.Gen5 = settings.Gen6 = settings.Gen7 = settings.Gen8 = false;
|
||||
var rand = new EvolutionRandomizer(ROM.Info, Editor.Evolve.LoadAll(), Editor.Personal);
|
||||
rand.Randomizer.Initialize(settings);
|
||||
rand.RandSpec.Initialize(settings);
|
||||
rand.ExecuteTrade();
|
||||
LoadIndex(CB_Species.SelectedIndex);
|
||||
System.Media.SystemSounds.Asterisk.Play();
|
||||
|
||||
@@ -192,7 +192,7 @@ void ApplyRand(IList<EncounterSlot8> slots)
|
||||
}
|
||||
|
||||
s.Species = rand.GetRandomSpecies(s.Species);
|
||||
s.Form = fr.GetRandomForme(s.Species);
|
||||
s.Form = fr.GetRandomForme(s.Species, false, true, true, ROM.Data.PersonalData.Table);
|
||||
if (fill)
|
||||
s.Probability = RandomScaledRates[slots.Count][i];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user