-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: pick random equipment from equipment pool for heroes
* refactor equipment mapping from equipment pool mapper into a proper mapper for re-use * add usecase for getting a random equipment from a domain equipment pool
- Loading branch information
Showing
6 changed files
with
231 additions
and
107 deletions.
There are no files selected for viewing
72 changes: 72 additions & 0 deletions
72
Bannerlord.ExpandedTemplate.Domain.Tests/EquipmentPool/GetEquipmentShould.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
using System.Xml.Linq; | ||
using Bannerlord.ExpandedTemplate.Domain.EquipmentPool; | ||
using Bannerlord.ExpandedTemplate.Domain.EquipmentPool.Model; | ||
using Bannerlord.ExpandedTemplate.Domain.EquipmentPool.Util; | ||
using Moq; | ||
using NUnit.Framework; | ||
|
||
namespace Bannerlord.ExpandedTemplate.Domain.Tests.EquipmentPool; | ||
|
||
public class GetEquipmentShould | ||
{ | ||
private Mock<IRandom> _random; | ||
|
||
private IGetEquipment _getEquipment; | ||
|
||
[SetUp] | ||
public void SetUp() | ||
{ | ||
_random = new Mock<IRandom>(MockBehavior.Strict); | ||
_getEquipment = new GetEquipment(_random.Object); | ||
} | ||
|
||
[Test] | ||
public void GetFirstEquipmentWhenTwoEquipmentTemplatesAndRandomReturnsZero() | ||
{ | ||
var equipment = new List<Equipment> | ||
{ | ||
new(XDocument.Parse("<Equipment1/>")), | ||
new(XDocument.Parse("<Equipment2/>")) | ||
}; | ||
var equipmentPool = new Domain.EquipmentPool.Model.EquipmentPool(equipment, 0); | ||
_random.Setup(random => random.Next(0, 2)).Returns(0); | ||
|
||
var actualEquipment = _getEquipment.GetEquipmentFromEquipmentPool(equipmentPool); | ||
|
||
Assert.That(actualEquipment, Is.EqualTo(equipment[0])); | ||
} | ||
|
||
[Test] | ||
public void GetSecondEquipmentWhenTwoEquipmentTemplatesAndRandomReturnsOne() | ||
{ | ||
var equipment = new List<Equipment> | ||
{ | ||
new(XDocument.Parse("<Equipment1/>")), | ||
new(XDocument.Parse("<Equipment2/>")) | ||
}; | ||
var equipmentPool = new Domain.EquipmentPool.Model.EquipmentPool(equipment, 0); | ||
_random.Setup(random => random.Next(0, 2)).Returns(1); | ||
|
||
var actualEquipment = _getEquipment.GetEquipmentFromEquipmentPool(equipmentPool); | ||
|
||
Assert.That(actualEquipment, Is.EqualTo(equipment[1])); | ||
} | ||
|
||
[Test] | ||
public void ReturnsNullWhenNoEquipmentTemplates() | ||
{ | ||
var equipmentPool = new Domain.EquipmentPool.Model.EquipmentPool(new List<Equipment>(), 0); | ||
|
||
var actualEquipment = _getEquipment.GetEquipmentFromEquipmentPool(equipmentPool); | ||
|
||
Assert.That(actualEquipment, Is.Null); | ||
} | ||
|
||
[Test] | ||
public void ReturnsNullWhenNoEquipmentPool() | ||
{ | ||
var actualEquipment = _getEquipment.GetEquipmentFromEquipmentPool(null!); | ||
|
||
Assert.That(actualEquipment, Is.Null); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
Bannerlord.ExpandedTemplate.Domain/EquipmentPool/GetEquipment.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using Bannerlord.ExpandedTemplate.Domain.EquipmentPool.Model; | ||
using Bannerlord.ExpandedTemplate.Domain.EquipmentPool.Util; | ||
|
||
namespace Bannerlord.ExpandedTemplate.Domain.EquipmentPool; | ||
|
||
public class GetEquipment(IRandom random) : IGetEquipment | ||
{ | ||
public Equipment GetEquipmentFromEquipmentPool(Model.EquipmentPool equipmentPool) | ||
{ | ||
if (equipmentPool is null || equipmentPool.GetEquipmentLoadouts().Count == 0) return null; | ||
Check warning on line 10 in Bannerlord.ExpandedTemplate.Domain/EquipmentPool/GetEquipment.cs GitHub Actions / integration-tests
|
||
|
||
var randomIndex = random.Next(0, equipmentPool.GetEquipmentLoadouts().Count); | ||
|
||
return equipmentPool.GetEquipmentLoadouts()[randomIndex]; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
Bannerlord.ExpandedTemplate.Domain/EquipmentPool/IGetEquipment.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using Bannerlord.ExpandedTemplate.Domain.EquipmentPool.Model; | ||
|
||
namespace Bannerlord.ExpandedTemplate.Domain.EquipmentPool; | ||
|
||
public interface IGetEquipment | ||
{ | ||
Equipment? GetEquipmentFromEquipmentPool(Model.EquipmentPool equipmentPool); | ||
} |
61 changes: 61 additions & 0 deletions
61
....ExpandedTemplate.Integration/SetSpawnEquipment/EquipmentPools/Mappers/EquipmentMapper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using System; | ||
using System.Xml; | ||
using System.Xml.Linq; | ||
using Bannerlord.ExpandedTemplate.Domain.Logging.Port; | ||
using TaleWorlds.Core; | ||
using TaleWorlds.ObjectSystem; | ||
|
||
namespace Bannerlord.ExpandedTemplate.Integration.SetSpawnEquipment.EquipmentPools.Mappers; | ||
|
||
public class EquipmentMapper(MBObjectManager mbObjectManager, ILoggerFactory loggerFactory) | ||
{ | ||
private readonly ILogger _logger = loggerFactory.CreateLogger<EquipmentMapper>(); | ||
|
||
public Equipment Map(Domain.EquipmentPool.Model.Equipment equipment, MBEquipmentRoster bannerlordEquipmentPool) | ||
{ | ||
XmlNode? xmlEquipmentNode = MapEquipmentNode(equipment.GetEquipmentNode()); | ||
if (xmlEquipmentNode is null) return null; | ||
|
||
if (xmlEquipmentNode.Name.Equals("EquipmentRoster", StringComparison.InvariantCultureIgnoreCase)) | ||
return AddEquipmentNodeToEquipmentRoster(xmlEquipmentNode, bannerlordEquipmentPool); | ||
|
||
return null; | ||
} | ||
|
||
private XmlNode? MapEquipmentNode(XNode node) | ||
{ | ||
if (node is null) return null; | ||
var xmlDocument = new XmlDocument(); | ||
xmlDocument.LoadXml(node.ToString()); | ||
return xmlDocument.DocumentElement; | ||
} | ||
|
||
private Equipment AddEquipmentNodeToEquipmentRoster(XmlNode equipmentRosterNode, | ||
MBEquipmentRoster bannerlordEquipmentPool) | ||
{ | ||
var equipmentLoadout = | ||
new Equipment(bool.Parse(equipmentRosterNode.Attributes?["civilian"]?.Value ?? "false")); | ||
equipmentLoadout.Deserialize(mbObjectManager, equipmentRosterNode); | ||
|
||
var nativeEquipmentLoadout = | ||
FindMatchingDomainEquipmentInBannerlordEquipmentPool(bannerlordEquipmentPool, equipmentLoadout); | ||
|
||
if (nativeEquipmentLoadout is null) | ||
{ | ||
_logger.Error( | ||
$"Could not find {equipmentLoadout} among native '{bannerlordEquipmentPool.StringId}' equipment roster"); | ||
return null; | ||
} | ||
|
||
return nativeEquipmentLoadout; | ||
} | ||
|
||
private Equipment? FindMatchingDomainEquipmentInBannerlordEquipmentPool(MBEquipmentRoster bannerlordEquipmentPool, | ||
Equipment equipment) | ||
{ | ||
if (bannerlordEquipmentPool is null) return null; | ||
|
||
return bannerlordEquipmentPool.AllEquipments.Find(nativeEquipmentLoadout => | ||
nativeEquipmentLoadout.IsEquipmentEqualTo(equipment)); | ||
} | ||
} |
111 changes: 23 additions & 88 deletions
111
...ndedTemplate.Integration/SetSpawnEquipment/EquipmentPools/Mappers/EquipmentPoolsMapper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,105 +1,40 @@ | ||
using System; | ||
| ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Xml; | ||
using System.Xml.Linq; | ||
using Bannerlord.ExpandedTemplate.Domain.EquipmentPool.Model; | ||
using Bannerlord.ExpandedTemplate.Domain.Logging.Port; | ||
using TaleWorlds.Core; | ||
using TaleWorlds.Library; | ||
using TaleWorlds.ObjectSystem; | ||
using Equipment = TaleWorlds.Core.Equipment; | ||
|
||
namespace Bannerlord.ExpandedTemplate.Integration.SetSpawnEquipment.EquipmentPools.Mappers | ||
{ | ||
public class EquipmentPoolsMapper | ||
{ | ||
private readonly MBObjectManager _mbObjectManager; | ||
private readonly ILogger _logger; | ||
|
||
private readonly FieldInfo _mbEquipmentRosterEquipmentsField = | ||
typeof(MBEquipmentRoster).GetField("_equipments", BindingFlags.NonPublic | BindingFlags.Instance); | ||
|
||
public EquipmentPoolsMapper(MBObjectManager mbObjectManager, ILoggerFactory loggerFactory) | ||
{ | ||
_mbObjectManager = mbObjectManager; | ||
_logger = loggerFactory.CreateLogger<EquipmentPoolsMapper>(); | ||
} | ||
|
||
public MBEquipmentRoster MapEquipmentPool(EquipmentPool equipmentPool, | ||
string equipmentId) | ||
{ | ||
var mbEquipmentLoadouts = new MBEquipmentRoster(); | ||
var equipmentNodes = equipmentPool.GetEquipmentLoadouts() | ||
.Select(equipmentLoadout => equipmentLoadout.GetEquipmentNode()); | ||
|
||
foreach (var equipmentLoadoutNode in equipmentNodes) | ||
{ | ||
var node = MapNode(equipmentLoadoutNode); | ||
if (node is null) continue; | ||
|
||
if (node.Name.Equals("EquipmentRoster", StringComparison.InvariantCultureIgnoreCase)) | ||
AddEquipmentNodeToEquipmentRoster(node, mbEquipmentLoadouts, equipmentId); | ||
else if (node.Name.Equals("EquipmentSet", StringComparison.InvariantCultureIgnoreCase)) | ||
AddReferencedEquipmentsToPool(node, mbEquipmentLoadouts, equipmentId); | ||
} | ||
|
||
return mbEquipmentLoadouts; | ||
} | ||
namespace Bannerlord.ExpandedTemplate.Integration.SetSpawnEquipment.EquipmentPools.Mappers; | ||
|
||
private XmlNode? MapNode(XNode node) | ||
{ | ||
XmlDocument xmlDocument = new XmlDocument(); | ||
xmlDocument.LoadXml(node.ToString()); | ||
return xmlDocument.DocumentElement; | ||
} | ||
|
||
private void AddEquipmentNodeToEquipmentRoster(XmlNode equipmentRosterNode, MBEquipmentRoster equipmentRoster, | ||
string equipmentId) | ||
{ | ||
var equipmentLoadout = | ||
new Equipment(bool.Parse(equipmentRosterNode.Attributes?["civilian"]?.Value ?? "false")); | ||
equipmentLoadout.Deserialize(_mbObjectManager, equipmentRosterNode); | ||
|
||
var nativeEquipmentLoadout = FindMatchingEquipment(equipmentId, equipmentLoadout); | ||
|
||
if (nativeEquipmentLoadout is null) | ||
{ | ||
_logger.Error($"Could not find {equipmentLoadout} among native '{equipmentId}' equipment roster"); | ||
return; | ||
} | ||
public class EquipmentPoolsMapper | ||
{ | ||
private readonly EquipmentMapper _equipmentMapper; | ||
|
||
var equipment = (MBList<Equipment>)_mbEquipmentRosterEquipmentsField.GetValue(equipmentRoster); | ||
equipment.Add(nativeEquipmentLoadout); | ||
} | ||
private readonly FieldInfo? _mbEquipmentRosterEquipmentsField = | ||
typeof(MBEquipmentRoster).GetField("_equipments", BindingFlags.NonPublic | BindingFlags.Instance); | ||
|
||
private Equipment? FindMatchingEquipment(string equipmentId, Equipment equipment) | ||
{ | ||
var nativeEquipmentPool = _mbObjectManager.GetObject<MBEquipmentRoster>(equipmentId); | ||
public EquipmentPoolsMapper(EquipmentMapper equipmentMapper, ILoggerFactory loggerFactory) | ||
{ | ||
ILogger logger = loggerFactory.CreateLogger<EquipmentMapper>(); | ||
if (_mbEquipmentRosterEquipmentsField is null) | ||
logger.Error("Could not find the _equipment field in the MBEquipmentRoster class via reflection."); | ||
_equipmentMapper = equipmentMapper; | ||
} | ||
|
||
if (nativeEquipmentPool is null) return null; | ||
public MBEquipmentRoster MapEquipmentPool(EquipmentPool equipmentPool, | ||
MBEquipmentRoster equipmentPoolWithAllEquipment) | ||
{ | ||
if (_mbEquipmentRosterEquipmentsField is null) return new MBEquipmentRoster(); | ||
|
||
// TODO: handle use case when nativeEquipmentPool is not found | ||
return nativeEquipmentPool.AllEquipments.Find(nativeEquipmentLoadout => | ||
nativeEquipmentLoadout.IsEquipmentEqualTo(equipment)); | ||
} | ||
|
||
private void AddReferencedEquipmentsToPool(XmlNode referencedEquipmentNode, MBEquipmentRoster equipmentRoster, | ||
string equipmentId) | ||
{ | ||
var id = referencedEquipmentNode.Attributes?["id"]?.Value; | ||
if (string.IsNullOrWhiteSpace(id)) | ||
{ | ||
AddEquipmentNodeToEquipmentRoster(referencedEquipmentNode, equipmentRoster, equipmentId); | ||
return; | ||
} | ||
var mbEquipmentLoadouts = new MBEquipmentRoster(); | ||
var equipments = (MBList<Equipment>)_mbEquipmentRosterEquipmentsField.GetValue(mbEquipmentLoadouts); | ||
|
||
var referencedId = _mbObjectManager.GetObject<MBEquipmentRoster>(id); | ||
if (referencedId is null) return; | ||
equipmentPool.GetEquipmentLoadouts().ToList().ForEach(equipment => | ||
equipments.Add(_equipmentMapper.Map(equipment, equipmentPoolWithAllEquipment))); | ||
|
||
bool.TryParse(referencedEquipmentNode.Attributes?["civilian"]?.Value, out var isCivilian); | ||
// add all referenced equipments from the EquipmentSet node to the roster | ||
equipmentRoster.AddEquipmentRoster(referencedId, isCivilian); | ||
} | ||
return mbEquipmentLoadouts; | ||
} | ||
} |
Oops, something went wrong.