Implements experience gain
All checks were successful
Build / Build (push) Successful in 1m49s

This commit is contained in:
2025-11-02 23:20:07 +01:00
parent f00453448f
commit 90eaeb1a72
7 changed files with 83 additions and 2 deletions

View File

@@ -39,6 +39,12 @@ public interface IDynamicLibrary
/// </summary> /// </summary>
ICaptureLibrary CaptureLibrary { get; } ICaptureLibrary CaptureLibrary { get; }
/// <summary>
/// The experience gain calculator deals with the calculation of experience gained by a
/// Pokémon after defeating another Pokémon.
/// </summary>
IExperienceGainCalculator ExperienceGainCalculator { get; }
/// <summary> /// <summary>
/// A holder of the script types that can be resolved by this library. /// A holder of the script types that can be resolved by this library.
/// </summary> /// </summary>
@@ -64,12 +70,13 @@ public class DynamicLibraryImpl : IDynamicLibrary
return new DynamicLibraryImpl(load.StaticLibrary, load.Registry.BattleStatCalculator!, return new DynamicLibraryImpl(load.StaticLibrary, load.Registry.BattleStatCalculator!,
load.Registry.DamageCalculator!, load.Registry.MiscLibrary!, load.Registry.CaptureLibrary!, load.Resolver, load.Registry.DamageCalculator!, load.Registry.MiscLibrary!, load.Registry.CaptureLibrary!, load.Resolver,
load.Registry.ExplicitAIHandlers); load.Registry.ExplicitAIHandlers, load.Registry.ExperienceGainCalculator!);
} }
private DynamicLibraryImpl(IStaticLibrary staticLibrary, IBattleStatCalculator statCalculator, private DynamicLibraryImpl(IStaticLibrary staticLibrary, IBattleStatCalculator statCalculator,
IDamageCalculator damageCalculator, IMiscLibrary miscLibrary, ICaptureLibrary captureLibrary, IDamageCalculator damageCalculator, IMiscLibrary miscLibrary, ICaptureLibrary captureLibrary,
ScriptResolver scriptResolver, IReadOnlyExplicitAIHandlers explicitAIHandlers) ScriptResolver scriptResolver, IReadOnlyExplicitAIHandlers explicitAIHandlers,
IExperienceGainCalculator experienceGainCalculator)
{ {
StaticLibrary = staticLibrary; StaticLibrary = staticLibrary;
StatCalculator = statCalculator; StatCalculator = statCalculator;
@@ -77,6 +84,7 @@ public class DynamicLibraryImpl : IDynamicLibrary
MiscLibrary = miscLibrary; MiscLibrary = miscLibrary;
ScriptResolver = scriptResolver; ScriptResolver = scriptResolver;
ExplicitAIHandlers = explicitAIHandlers; ExplicitAIHandlers = explicitAIHandlers;
ExperienceGainCalculator = experienceGainCalculator;
CaptureLibrary = captureLibrary; CaptureLibrary = captureLibrary;
} }
@@ -95,6 +103,9 @@ public class DynamicLibraryImpl : IDynamicLibrary
/// <inheritdoc /> /// <inheritdoc />
public ICaptureLibrary CaptureLibrary { get; } public ICaptureLibrary CaptureLibrary { get; }
/// <inheritdoc />
public IExperienceGainCalculator ExperienceGainCalculator { get; }
/// <inheritdoc /> /// <inheritdoc />
public ScriptResolver ScriptResolver { get; } public ScriptResolver ScriptResolver { get; }

View File

@@ -0,0 +1,14 @@
using PkmnLib.Dynamic.Models;
namespace PkmnLib.Dynamic.Libraries;
/// <summary>
/// Calculates experience gain for Pokémon battles.
/// </summary>
public interface IExperienceGainCalculator
{
/// <summary>
/// Calculates the experience a Pokémon gains when defeating another Pokémon.
/// </summary>
uint CalculateExperienceGain(IPokemon defeatedPokemon, IPokemon victoriousPokemon);
}

View File

@@ -36,6 +36,8 @@ public static class LibraryLoader
throw new InvalidOperationException("Misc library not found in plugins."); throw new InvalidOperationException("Misc library not found in plugins.");
if (registry.CaptureLibrary is null) if (registry.CaptureLibrary is null)
throw new InvalidOperationException("Capture library not found in plugins."); throw new InvalidOperationException("Capture library not found in plugins.");
if (registry.ExperienceGainCalculator is null)
throw new InvalidOperationException("Experience gain calculator not found in plugins.");
var scriptResolver = new ScriptResolver(registry.ScriptTypes, registry.ItemScriptTypes); var scriptResolver = new ScriptResolver(registry.ScriptTypes, registry.ItemScriptTypes);
return new LoadResult(registry, scriptResolver, staticLibrary); return new LoadResult(registry, scriptResolver, staticLibrary);
} }

View File

@@ -1184,6 +1184,11 @@ public class PokemonImpl : ScriptSource, IPokemon
BattleData.BattleSide.MarkFaint(BattleData.Position); BattleData.BattleSide.MarkFaint(BattleData.Position);
BattleData.BattleSide.ForceClearPokemonFromField(BattleData.Position); BattleData.BattleSide.ForceClearPokemonFromField(BattleData.Position);
foreach (var opponent in BattleData.SeenOpponents.WhereNotNull())
{
opponent.AddExperience(Library.ExperienceGainCalculator.CalculateExperienceGain(this, opponent));
}
// Validate the battle state to see if the battle is over. // Validate the battle state to see if the battle is over.
BattleData.Battle.ValidateBattleState(); BattleData.Battle.ValidateBattleState();
} }

View File

@@ -19,6 +19,7 @@ public class ScriptRegistry
private IDamageCalculator? _damageCalculator; private IDamageCalculator? _damageCalculator;
private IMiscLibrary? _miscLibrary; private IMiscLibrary? _miscLibrary;
private ICaptureLibrary? _captureLibrary; private ICaptureLibrary? _captureLibrary;
private IExperienceGainCalculator? _experienceGainCalculator;
/// <summary> /// <summary>
/// Automatically register all scripts in the given assembly that have the <see cref="ScriptAttribute"/>, and /// Automatically register all scripts in the given assembly that have the <see cref="ScriptAttribute"/>, and
@@ -110,11 +111,18 @@ public class ScriptRegistry
public void RegisterCaptureLibrary<T>(T captureLibrary) where T : ICaptureLibrary => public void RegisterCaptureLibrary<T>(T captureLibrary) where T : ICaptureLibrary =>
_captureLibrary = captureLibrary; _captureLibrary = captureLibrary;
/// <summary>
/// Register an experience gain calculator.
/// </summary>
public void RegisterExperienceGainCalculator<T>(T experienceGainCalculator) where T : IExperienceGainCalculator =>
_experienceGainCalculator = experienceGainCalculator;
internal IReadOnlyDictionary<(ScriptCategory category, StringKey name), Func<Script>> ScriptTypes => _scriptTypes; internal IReadOnlyDictionary<(ScriptCategory category, StringKey name), Func<Script>> ScriptTypes => _scriptTypes;
internal IReadOnlyDictionary<StringKey, Func<IItem, ItemScript>> ItemScriptTypes => _itemScriptTypes; internal IReadOnlyDictionary<StringKey, Func<IItem, ItemScript>> ItemScriptTypes => _itemScriptTypes;
internal IBattleStatCalculator? BattleStatCalculator => _battleStatCalculator; internal IBattleStatCalculator? BattleStatCalculator => _battleStatCalculator;
internal IDamageCalculator? DamageCalculator => _damageCalculator; internal IDamageCalculator? DamageCalculator => _damageCalculator;
internal IMiscLibrary? MiscLibrary => _miscLibrary; internal IMiscLibrary? MiscLibrary => _miscLibrary;
internal ICaptureLibrary? CaptureLibrary => _captureLibrary; internal ICaptureLibrary? CaptureLibrary => _captureLibrary;
internal IExperienceGainCalculator? ExperienceGainCalculator => _experienceGainCalculator;
public ExplicitAIHandlers ExplicitAIHandlers { get; } = new(); public ExplicitAIHandlers ExplicitAIHandlers { get; } = new();
} }

View File

@@ -49,6 +49,7 @@ public class Gen7Plugin : Plugin<Gen7PluginConfiguration>, IResourceProvider
registry.RegisterDamageCalculator(new Gen7DamageCalculator(Configuration)); registry.RegisterDamageCalculator(new Gen7DamageCalculator(Configuration));
registry.RegisterMiscLibrary(new Gen7MiscLibrary()); registry.RegisterMiscLibrary(new Gen7MiscLibrary());
registry.RegisterCaptureLibrary(new Gen7CaptureLibrary(Configuration)); registry.RegisterCaptureLibrary(new Gen7CaptureLibrary(Configuration));
registry.RegisterExperienceGainCalculator(new Gen7ExperienceGainCalculator());
ExplicitAIFunctionRegistration.RegisterAIFunctions(registry.ExplicitAIHandlers); ExplicitAIFunctionRegistration.RegisterAIFunctions(registry.ExplicitAIHandlers);
} }

View File

@@ -0,0 +1,40 @@
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Dynamic.Libraries;
namespace PkmnLib.Plugin.Gen7.Libraries.Battling;
/// <inheritdoc />
public class Gen7ExperienceGainCalculator : IExperienceGainCalculator
{
/// <inheritdoc />
[SuppressMessage("ReSharper", "UselessBinaryOperation")]
public uint CalculateExperienceGain(IPokemon defeatedPokemon, IPokemon victoriousPokemon)
{
var b = defeatedPokemon.Form.BaseExperience;
var levelFainted = defeatedPokemon.Level;
var levelOpponent = victoriousPokemon.Level;
// TODO: Experience share, in which case s = 2 for Pokemon that did not participate in battle
var s = 1;
var v1 = b * levelFainted / 5 * (1 / s);
var v2 = (2 * levelFainted + 10) / (levelFainted + levelOpponent + 10);
var res = v1 * Math.Pow(v2, 2.5) + 1;
// TODO: t = 1.5 if the Pokemon is traded, and 1.7 is traded and has a different language
var t = 1;
res *= t;
// TODO: script modifier, which is e.
var e = 1;
res *= e;
// TODO: v = 1.2 if the Pokemon is at or past the level where it evolves
var v = 1;
res *= v;
return res switch
{
> uint.MaxValue => uint.MaxValue,
< 0 => 0,
_ => (uint)res,
};
}
}