//
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//
namespace MUnique.OpenMU.Tests;
using Microsoft.Extensions.Logging.Abstractions;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.Pathfinding;
using NUnit.Framework;
///
/// Tests for the .
///
[TestFixture]
public class DroppedMoneyTest
{
private const uint DroppedAmount = 1000;
///
/// Tests that a failed pick up doesn't consume the drop, so it's still available for the next player.
/// A player at the maximum inventory money can't take it, and used to claim it nevertheless - which left
/// it lying on the map, unpickable for everyone until it expired.
///
[Test]
public async Task FailedPickUpKeepsMoneyAvailableAsync()
{
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
player.GameContext.Configuration.MaximumInventoryMoney = int.MaxValue;
var maximumMoney = player.GameContext.Configuration.MaximumInventoryMoney;
player.Money = maximumMoney;
var money = new DroppedMoney(DroppedAmount, new Point(100, 100), player.CurrentMap!);
Assert.That(await money.TryPickUpByAsync(player).ConfigureAwait(false), Is.False);
Assert.That(player.Money, Is.EqualTo(maximumMoney));
var otherPlayer = await PlayerTestHelper.CreatePlayerAsync(player.GameContext).ConfigureAwait(false);
otherPlayer.Money = 0;
Assert.That(await money.TryPickUpByAsync(otherPlayer).ConfigureAwait(false), Is.True);
Assert.That(otherPlayer.Money, Is.EqualTo((int)DroppedAmount));
}
///
/// Tests that a successful pick up still consumes the drop, so it can't be picked up twice.
///
[Test]
public async Task SuccessfulPickUpConsumesMoneyAsync()
{
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
player.GameContext.Configuration.MaximumInventoryMoney = int.MaxValue;
player.Money = 0;
var money = new DroppedMoney(DroppedAmount, new Point(100, 100), player.CurrentMap!);
Assert.That(await money.TryPickUpByAsync(player).ConfigureAwait(false), Is.True);
Assert.That(player.Money, Is.EqualTo((int)DroppedAmount));
var otherPlayer = await PlayerTestHelper.CreatePlayerAsync(player.GameContext).ConfigureAwait(false);
otherPlayer.Money = 0;
Assert.That(await money.TryPickUpByAsync(otherPlayer).ConfigureAwait(false), Is.False);
Assert.That(otherPlayer.Money, Is.EqualTo(0));
}
///
/// Tests that money earned by one party can still be picked up by a stranger's party - money has no
/// owner - and is then split equally between the picking party, because there is no experience of theirs
/// to follow. The earner, who is not in the picking party, receives nothing.
///
[Test]
public async Task StrangerPartySplitsPickedUpMoneyEquallyAsync()
{
var earner = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var gameContext = earner.GameContext;
gameContext.Configuration.MaximumInventoryMoney = int.MaxValue;
var strangerParty = CreateParty();
var stranger1 = await AddPartyMemberAsync(gameContext, strangerParty).ConfigureAwait(false);
var stranger2 = await AddPartyMemberAsync(gameContext, strangerParty).ConfigureAwait(false);
// The money was earned by 'earner', who is in neither of the picking party's members.
var shares = new[] { new MoneyShare(earner, DroppedAmount) };
var money = new DroppedMoney(DroppedAmount, new Point(100, 100), stranger1.CurrentMap!, shares);
Assert.That(await money.TryPickUpByAsync(stranger1).ConfigureAwait(false), Is.True);
Assert.That(earner.Money, Is.EqualTo(0));
Assert.That(stranger1.Money + stranger2.Money, Is.EqualTo((int)DroppedAmount));
Assert.That(stranger1.Money, Is.EqualTo((int)DroppedAmount / 2));
Assert.That(stranger2.Money, Is.EqualTo((int)DroppedAmount / 2));
}
///
/// Tests that when the earning party picks its own money up, it follows the recorded shares
/// (proportional to the experience each member gained) instead of being split equally.
///
[Test]
public async Task EarningPartyReceivesMoneyByExperienceShareAsync()
{
var big = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
var gameContext = big.GameContext;
gameContext.Configuration.MaximumInventoryMoney = int.MaxValue;
var party = CreateParty();
await party.AddAsync(big).ConfigureAwait(false);
var small = await AddPartyMemberAsync(gameContext, party).ConfigureAwait(false);
var shares = new[]
{
new MoneyShare(big, 800),
new MoneyShare(small, 200),
};
var money = new DroppedMoney(DroppedAmount, new Point(100, 100), big.CurrentMap!, shares);
Assert.That(await money.TryPickUpByAsync(big).ConfigureAwait(false), Is.True);
Assert.That(big.Money, Is.EqualTo(800));
Assert.That(small.Money, Is.EqualTo(200));
}
private static Party CreateParty()
{
// The party manager of the lightweight test context has a maximum size of zero, which would
// reject every member. A stand-alone party with a real size is enough for the money distribution.
var partyManager = new PartyManager(5, new NullLogger());
return new Party(partyManager, 5, new NullLogger());
}
private static async ValueTask AddPartyMemberAsync(IGameContext gameContext, Party party)
{
// The test helper already enters the player into the world, so it shares the live map of the
// context with the other members created from the same context - which is what the money
// distribution's eligibility check compares.
var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false);
await party.AddAsync(player).ConfigureAwait(false);
return player;
}
}