Reorganized folder structure
This commit is contained in:
22
Plugins/PkmnLib.Plugin.Gen7/Gen7Plugin.cs
Normal file
22
Plugins/PkmnLib.Plugin.Gen7/Gen7Plugin.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using PkmnLib.Dynamic.ScriptHandling;
|
||||
using PkmnLib.Plugin.Gen7.Libraries;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7;
|
||||
|
||||
public class Gen7Plugin : Dynamic.ScriptHandling.Registry.Plugin
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Gen7";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override uint LoadOrder => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Register(ScriptRegistry registry)
|
||||
{
|
||||
registry.RegisterAssemblyScripts(typeof(Gen7Plugin).Assembly);
|
||||
registry.RegisterBattleStatCalculator(new Gen7BattleStatCalculator());
|
||||
registry.RegisterDamageCalculator(new Gen7DamageCalculator(true));
|
||||
registry.RegisterMiscLibrary(new Gen7MiscLibrary());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using PkmnLib.Dynamic.Libraries;
|
||||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Libraries;
|
||||
|
||||
public class Gen7BattleStatCalculator : IBattleStatCalculator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void CalculateFlatStats(IPokemon pokemon, StatisticSet<uint> stats)
|
||||
{
|
||||
stats.SetStatistic(Statistic.Hp, CalculateHealthStat(pokemon));
|
||||
stats.SetStatistic(Statistic.Attack, CalculateNormalStat(pokemon, Statistic.Attack));
|
||||
stats.SetStatistic(Statistic.Defense, CalculateNormalStat(pokemon, Statistic.Defense));
|
||||
stats.SetStatistic(Statistic.SpecialAttack, CalculateNormalStat(pokemon, Statistic.SpecialAttack));
|
||||
stats.SetStatistic(Statistic.SpecialDefense, CalculateNormalStat(pokemon, Statistic.SpecialDefense));
|
||||
stats.SetStatistic(Statistic.Speed, CalculateNormalStat(pokemon, Statistic.Speed));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint CalculateFlatStat(IPokemon pokemon, Statistic stat)
|
||||
{
|
||||
return stat switch
|
||||
{
|
||||
Statistic.Hp => CalculateHealthStat(pokemon),
|
||||
_ => CalculateNormalStat(pokemon, stat)
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CalculateBoostedStats(IPokemon pokemon, StatisticSet<uint> stats)
|
||||
{
|
||||
stats.SetStatistic(Statistic.Hp, CalculateBoostedStat(pokemon, Statistic.Hp));
|
||||
stats.SetStatistic(Statistic.Attack, CalculateBoostedStat(pokemon, Statistic.Attack));
|
||||
stats.SetStatistic(Statistic.Defense, CalculateBoostedStat(pokemon, Statistic.Defense));
|
||||
stats.SetStatistic(Statistic.SpecialAttack, CalculateBoostedStat(pokemon, Statistic.SpecialAttack));
|
||||
stats.SetStatistic(Statistic.SpecialDefense, CalculateBoostedStat(pokemon, Statistic.SpecialDefense));
|
||||
stats.SetStatistic(Statistic.Speed, CalculateBoostedStat(pokemon, Statistic.Speed));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint CalculateBoostedStat(IPokemon pokemon, Statistic stat)
|
||||
{
|
||||
var flatStat = CalculateFlatStat(pokemon, stat);
|
||||
var boostModifier = GetStatBoostModifier(pokemon, stat);
|
||||
var boostedStat = flatStat * boostModifier;
|
||||
if (boostedStat > uint.MaxValue) boostedStat = uint.MaxValue;
|
||||
return (uint)boostedStat;
|
||||
}
|
||||
|
||||
private static uint CalculateHealthStat(IPokemon pokemon)
|
||||
{
|
||||
var baseValue = (ulong)pokemon.Form.BaseStats.Hp;
|
||||
var iv = (ulong)pokemon.IndividualValues.Hp;
|
||||
var ev = (ulong)pokemon.EffortValues.Hp;
|
||||
var level = (ulong)pokemon.Level;
|
||||
var health = (((2 * baseValue + iv + (ev / 4)) * level) / 100) + level + 10;
|
||||
if (health > uint.MaxValue) health = uint.MaxValue;
|
||||
return (uint)health;
|
||||
}
|
||||
|
||||
private static uint CalculateNormalStat(IPokemon pokemon, Statistic statistic)
|
||||
{
|
||||
var baseValue = (ulong)pokemon.Form.BaseStats.GetStatistic(statistic);
|
||||
var iv = (ulong)pokemon.IndividualValues.GetStatistic(statistic);
|
||||
var ev = (ulong)pokemon.EffortValues.GetStatistic(statistic);
|
||||
var level = (ulong)pokemon.Level;
|
||||
var unmodified = (((2 * baseValue + iv + (ev / 4)) * level) / 100) + 5;
|
||||
var natureModifier = pokemon.Nature.GetStatModifier(statistic);
|
||||
var modified = (unmodified * natureModifier);
|
||||
if (modified > uint.MaxValue) modified = uint.MaxValue;
|
||||
return (uint)modified;
|
||||
}
|
||||
|
||||
private static float GetStatBoostModifier(IPokemon pokemon, Statistic statistic)
|
||||
{
|
||||
var boost = pokemon.StatBoost.GetStatistic(statistic);
|
||||
return boost switch
|
||||
{
|
||||
-6 => 2.0f / 8.0f,
|
||||
-5 => 2.0f / 7.0f,
|
||||
-4 => 2.0f / 6.0f,
|
||||
-3 => 2.0f / 5.0f,
|
||||
-2 => 2.0f / 4.0f,
|
||||
-1 => 2.0f / 3.0f,
|
||||
0 => 1.0f,
|
||||
1 => 3.0f / 2.0f,
|
||||
2 => 4.0f / 2.0f,
|
||||
3 => 5.0f / 2.0f,
|
||||
4 => 6.0f / 2.0f,
|
||||
5 => 7.0f / 2.0f,
|
||||
6 => 8.0f / 2.0f,
|
||||
_ => throw new System.ArgumentException("Stat boost was out of expected range of -6 to 6")
|
||||
};
|
||||
}
|
||||
}
|
||||
157
Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs
Normal file
157
Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using PkmnLib.Dynamic.Libraries;
|
||||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Dynamic.ScriptHandling;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Moves;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Libraries;
|
||||
|
||||
public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public uint GetDamage(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData)
|
||||
{
|
||||
var category = executingMove.UseMove.Category;
|
||||
if (category == MoveCategory.Status)
|
||||
return 0;
|
||||
if (hitData.Effectiveness == 0)
|
||||
return 0;
|
||||
|
||||
var levelModifier = (2.0f * executingMove.User.Level) / 5.0f + 2.0f;
|
||||
var basePower = (float)hitData.BasePower;
|
||||
var statModifier = GetStatModifier(executingMove, target, hitNumber, hitData);
|
||||
var damageModifier = GetDamageModifier(executingMove, target, hitNumber, hitData);
|
||||
|
||||
var floatDamage = MathF.Floor(levelModifier * basePower);
|
||||
floatDamage = MathF.Floor(floatDamage * statModifier);
|
||||
floatDamage = MathF.Floor(floatDamage / 50.0f) + 2.0f;
|
||||
floatDamage = MathF.Floor(floatDamage * damageModifier);
|
||||
if (executingMove.TargetCount > 1)
|
||||
floatDamage = MathF.Floor(floatDamage * 0.75f);
|
||||
|
||||
if (hitData.IsCritical)
|
||||
{
|
||||
var critModifier = 1.5f;
|
||||
// TODO: script hook to change the critical modifier
|
||||
floatDamage = MathF.Floor(floatDamage * critModifier);
|
||||
}
|
||||
|
||||
if (hasRandomness)
|
||||
{
|
||||
var battle = target.BattleData?.Battle;
|
||||
if (battle == null)
|
||||
throw new InvalidOperationException("Randomness is enabled, but no battle is set.");
|
||||
var random = battle.Random;
|
||||
var randomFactor = random.GetInt(85, 101) / 100.0f;
|
||||
floatDamage = MathF.Floor(floatDamage * randomFactor);
|
||||
}
|
||||
|
||||
if (executingMove.User.Types.Contains(hitData.Type))
|
||||
{
|
||||
var stabModifier = 1.5f;
|
||||
executingMove.RunScriptHook(script =>
|
||||
script.ChangeStabModifier(executingMove, target, hitNumber, ref stabModifier));
|
||||
floatDamage = MathF.Floor(floatDamage * stabModifier);
|
||||
}
|
||||
|
||||
floatDamage = MathF.Floor(floatDamage * hitData.Effectiveness);
|
||||
uint damage = floatDamage switch
|
||||
{
|
||||
> uint.MaxValue => uint.MaxValue,
|
||||
< 1 => 1,
|
||||
_ => (uint)floatDamage
|
||||
};
|
||||
// TODO: script hook to modify the damage
|
||||
// TODO: script hook to modify incoming damage
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte GetBasePower(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData)
|
||||
{
|
||||
if (executingMove.UseMove.Category == MoveCategory.Status)
|
||||
return 0;
|
||||
var basePower = hitData.BasePower;
|
||||
// TODO: script hook to modify the base power
|
||||
return basePower;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[SuppressMessage("ReSharper", "UnreachableSwitchArmDueToIntegerAnalysis")] // disabled because of the TODO
|
||||
public bool IsCritical(IBattle battle, IExecutingMove executingMove, IPokemon target, byte hitNumber)
|
||||
{
|
||||
if (executingMove.UseMove.Category == MoveCategory.Status)
|
||||
return false;
|
||||
byte critStage = 0;
|
||||
// TODO: script hook to modify the crit stage
|
||||
|
||||
var random = battle.Random;
|
||||
return critStage switch
|
||||
{
|
||||
0 => random.GetInt(24) == 0,
|
||||
1 => random.GetInt(8) == 0,
|
||||
2 => random.GetInt(2) == 0,
|
||||
_ => true
|
||||
};
|
||||
}
|
||||
|
||||
private static float GetStatModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData)
|
||||
{
|
||||
var category = executingMove.UseMove.Category;
|
||||
if (category == MoveCategory.Status)
|
||||
return 1;
|
||||
|
||||
var (offensive, defensive) = category switch
|
||||
{
|
||||
MoveCategory.Physical => (Statistic.Attack, Statistic.Defense),
|
||||
_ => (Statistic.SpecialAttack, Statistic.SpecialDefense),
|
||||
};
|
||||
|
||||
// Check if we can bypass the defensive stat boost on the target. We default to this if the
|
||||
// move is critical, and the target has a defensive stat boost of > 0, but a script is
|
||||
// allowed to change this.
|
||||
var bypassDefense = hitData.IsCritical && target.StatBoost.GetStatistic(defensive) > 0;
|
||||
// TODO: script hook
|
||||
|
||||
// Check if we can bypass the offensive stat boost on the user. We default to this if the
|
||||
// move is critical, and the user has an offensive stat boost of < 0, but a script is
|
||||
// allowed to change this.
|
||||
var bypassOffense = hitData.IsCritical && executingMove.User.StatBoost.GetStatistic(offensive) < 0;
|
||||
// TODO: script hook
|
||||
|
||||
var userStats = executingMove.User.BoostedStats;
|
||||
if (bypassOffense)
|
||||
userStats = executingMove.User.FlatStats;
|
||||
var offensiveStat = userStats.GetStatistic(offensive);
|
||||
|
||||
var targetStats = target.BoostedStats;
|
||||
if (bypassDefense)
|
||||
targetStats = target.FlatStats;
|
||||
var defensiveStat = targetStats.GetStatistic(defensive);
|
||||
|
||||
// TODO: script hook to modify the stats above
|
||||
|
||||
var modifier = (float)offensiveStat / defensiveStat;
|
||||
// TODO: script hook to modify the modifier
|
||||
|
||||
return modifier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the damage modifier. This is a value that defaults to 1.0, but can be modified by scripts
|
||||
/// to apply a raw modifier to the damage.
|
||||
/// </summary>
|
||||
private static float GetDamageModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber,
|
||||
IHitData hitData)
|
||||
{
|
||||
var modifier = 1.0f;
|
||||
|
||||
// TODO: script hook to modify the modifier
|
||||
|
||||
return modifier;
|
||||
}
|
||||
}
|
||||
38
Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7MiscLibrary.cs
Normal file
38
Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7MiscLibrary.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using PkmnLib.Dynamic;
|
||||
using PkmnLib.Dynamic.Libraries;
|
||||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Dynamic.Models.Choices;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Moves;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Libraries;
|
||||
|
||||
public class Gen7MiscLibrary : IMiscLibrary
|
||||
{
|
||||
private readonly IMoveData _struggleData = new MoveDataImpl("struggle", new TypeIdentifier(0),
|
||||
MoveCategory.Physical, 50,
|
||||
255, 255, MoveTarget.Any, 0,
|
||||
new SecondaryEffectImpl(-1, "struggle", new Dictionary<StringKey, object>()), []);
|
||||
|
||||
/// <inheritdoc />
|
||||
public ITurnChoice ReplacementChoice(IPokemon user, byte targetSide, byte targetPosition) =>
|
||||
new MoveChoice(user, new LearnedMoveImpl(_struggleData, MoveLearnMethod.Unknown), targetSide,
|
||||
targetPosition);
|
||||
|
||||
/// <inheritdoc />
|
||||
public TimeOfDay GetTimeOfDay()
|
||||
{
|
||||
var time = StaticHelpers.GetCurrentDateTime();
|
||||
var hour = time.Hour;
|
||||
return hour switch
|
||||
{
|
||||
>= 0 and <= 5 => TimeOfDay.Night,
|
||||
>= 6 and <= 9 => TimeOfDay.Morning,
|
||||
>= 10 and <= 16 => TimeOfDay.Day,
|
||||
17 => TimeOfDay.Evening,
|
||||
_ => TimeOfDay.Night
|
||||
};
|
||||
}
|
||||
}
|
||||
14
Plugins/PkmnLib.Plugin.Gen7/PkmnLib.Plugin.Gen7.csproj
Normal file
14
Plugins/PkmnLib.Plugin.Gen7/PkmnLib.Plugin.Gen7.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>12</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\PkmnLib.Dynamic\PkmnLib.Dynamic.csproj" />
|
||||
<ProjectReference Include="..\..\PkmnLib.Static\PkmnLib.Static.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user