// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.Tests; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.DataModel.Entities; using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.Bots; using MUnique.OpenMU.GameLogic.Offline; /// /// Tests the entry decisions of - which party bots may follow /// their human leader into a mini game event, and who is taken along at all. The actual entry /// goes through the regular . /// [TestFixture] public class BotMiniGameHandlerTest { /// /// The event's character level bracket is enforced in both directions. /// /// The bot's character level. /// Whether the bot qualifies. [TestCase(200, false)] [TestCase(281, true)] [TestCase(330, true)] [TestCase(331, false)] public async ValueTask EnforcesLevelBracketAsync(int level, bool expected) { var gameContext = GameContextTestHelper.CreateGameContext(); var bot = await CreateBotAsync(gameContext).ConfigureAwait(false); bot.Attributes![Stats.Level] = level; var definition = new MiniGameDefinition { MinimumCharacterLevel = 281, MaximumCharacterLevel = 330 }; var eligible = BotMiniGameHandler.IsEligible(bot, definition, out var reason); Assert.That(eligible, Is.EqualTo(expected)); Assert.That(reason, expected ? Is.Empty : Is.Not.Empty); } /// /// The special characters (Magic Gladiator, Dark Lord, Rage Fighter, Summoner) enter in their own /// level bracket, exactly like they do for a player: a qualified Magic Gladiator must not be judged /// - and kicked out of its leader's party - by the bracket of the regular classes. /// /// The bot's character level. /// Whether the bot qualifies. [TestCase(200, false)] [TestCase(221, true)] [TestCase(280, true)] [TestCase(281, false)] public async ValueTask EnforcesSpecialCharacterLevelBracketAsync(int level, bool expected) { var gameContext = GameContextTestHelper.CreateGameContext(); var bot = await CreateBotAsync(gameContext).ConfigureAwait(false); bot.Attributes![Stats.Level] = level; // That is what makes a character "special" for the entry rules (see CharacterExtensions). bot.SelectedCharacter!.CharacterClass!.LevelWarpRequirementReductionPercent = 50; var definition = new MiniGameDefinition { MinimumCharacterLevel = 281, MaximumCharacterLevel = 330, MinimumSpecialCharacterLevel = 221, MaximumSpecialCharacterLevel = 280, }; var eligible = BotMiniGameHandler.IsEligible(bot, definition, out _); Assert.That(eligible, Is.EqualTo(expected)); } /// /// An event for master classes only is not entered before the bot's master evolution. /// /// Whether the bot evolved into its master class. /// Whether the bot qualifies. [TestCase(false, false)] [TestCase(true, true)] public async ValueTask EnforcesMasterClassRequirementAsync(bool isMasterClass, bool expected) { var gameContext = GameContextTestHelper.CreateGameContext(); var bot = await CreateBotAsync(gameContext).ConfigureAwait(false); bot.Attributes![Stats.Level] = 400; bot.SelectedCharacter!.CharacterClass!.IsMasterClass = isMasterClass; var definition = new MiniGameDefinition { MinimumCharacterLevel = 0, MaximumCharacterLevel = 400, RequiresMasterClass = true }; var eligible = BotMiniGameHandler.IsEligible(bot, definition, out _); Assert.That(eligible, Is.EqualTo(expected)); } /// /// A player killer bot (should never happen, but the rule is mirrored from the player entry) /// cannot enter events which disallow player killers. /// /// Whether the event allows player killers. /// Whether the bot qualifies. [TestCase(false, false)] [TestCase(true, true)] public async ValueTask EnforcesPlayerKillerRestrictionAsync(bool killersAllowed, bool expected) { var gameContext = GameContextTestHelper.CreateGameContext(); var bot = await CreateBotAsync(gameContext).ConfigureAwait(false); bot.Attributes![Stats.Level] = 100; bot.SelectedCharacter!.State = HeroState.PlayerKiller1stStage; var definition = new MiniGameDefinition { MinimumCharacterLevel = 0, MaximumCharacterLevel = 400, ArePlayerKillersAllowedToEnter = killersAllowed }; var eligible = BotMiniGameHandler.IsEligible(bot, definition, out _); Assert.That(eligible, Is.EqualTo(expected)); } /// /// Only the bots of the party whose MASTER enters are taken along - and only the bots, not the /// human members. /// [Test] public async ValueTask SnapshotTakesOnlyBotsOfTheEnteringMasterAsync() { var gameContext = GameContextTestHelper.CreateGameContext(); var leader = await CreateHumanAsync(gameContext, "Leader").ConfigureAwait(false); var member = await CreateHumanAsync(gameContext, "Member").ConfigureAwait(false); var bot1 = await CreateBotAsync(gameContext, "BotOne").ConfigureAwait(false); var bot2 = await CreateBotAsync(gameContext, "BotTwo").ConfigureAwait(false); var party = gameContext.PartyManager.CreateParty(); await party.AddAsync(leader).ConfigureAwait(false); await party.AddAsync(member).ConfigureAwait(false); await party.AddAsync(bot1).ConfigureAwait(false); await party.AddAsync(bot2).ConfigureAwait(false); var fromLeader = BotMiniGameHandler.SnapshotPartyBots(leader); var fromMember = BotMiniGameHandler.SnapshotPartyBots(member); Assert.That(fromLeader, Is.EquivalentTo(new[] { bot1, bot2 })); Assert.That(fromMember, Is.Empty); } /// /// A player without a party (or a bot, however it would get here) takes nobody along. /// [Test] public async ValueTask SnapshotIsEmptyWithoutPartyAsync() { var gameContext = GameContextTestHelper.CreateGameContext(); var solo = await CreateHumanAsync(gameContext, "Solo").ConfigureAwait(false); var bot = await CreateBotAsync(gameContext, "Bot").ConfigureAwait(false); Assert.That(BotMiniGameHandler.SnapshotPartyBots(solo), Is.Empty); Assert.That(BotMiniGameHandler.SnapshotPartyBots(bot), Is.Empty); } private static async ValueTask CreateBotAsync(IGameContext gameContext, string name = "Bot") { var bot = await PlayerTestHelper.CreateOfflineLevelingPlayerAsync(gameContext).ConfigureAwait(false); await bot.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false); bot.SelectedCharacter!.Name = name; bot.IsAlive = true; bot.Account!.IsBot = true; return bot; } private static async ValueTask CreateHumanAsync(IGameContext gameContext, string name) { var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false); player.SelectedCharacter!.Name = name; player.IsAlive = true; return player; } }