296 lines
10 KiB
C#
296 lines
10 KiB
C#
using System.Collections;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using GameFramework.DataTable;
|
|
using GeometryTD.CustomComponent;
|
|
using GeometryTD.DataTable;
|
|
using GeometryTD.Definition;
|
|
using GeometryTD.UI;
|
|
using NUnit.Framework;
|
|
|
|
namespace GeometryTD.Tests.EditMode
|
|
{
|
|
public sealed class ShopPricingRuleTests
|
|
{
|
|
[Test]
|
|
public void BuildGoods_Uses_Price_Row_Range_Matching_Item_Rarity()
|
|
{
|
|
ShopGoodsBuilder builder = CreateBuilder(
|
|
CreateShopPriceRow(1, RarityType.Blue, 30, 35),
|
|
CreateShopPriceRow(2, RarityType.Purple, 60, 70));
|
|
|
|
List<GoodsItemRawData> goods = builder.BuildGoods(goodsCount: 12, runSeed: 3001, sequenceIndex: 4);
|
|
|
|
Assert.That(goods, Has.Count.EqualTo(12));
|
|
for (int i = 0; i < goods.Count; i++)
|
|
{
|
|
GoodsItemRawData item = goods[i];
|
|
Assert.That(item, Is.Not.Null);
|
|
Assert.That(item.SourceItem, Is.Not.Null);
|
|
|
|
switch (item.SourceItem.Rarity)
|
|
{
|
|
case RarityType.Blue:
|
|
Assert.That(item.Price, Is.InRange(30, 35));
|
|
break;
|
|
case RarityType.Purple:
|
|
Assert.That(item.Price, Is.InRange(60, 70));
|
|
break;
|
|
default:
|
|
Assert.Fail($"Unexpected rarity generated for goods item {i}: {item.SourceItem.Rarity}");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void BuildGoods_Is_Reproducible_For_Same_RunSeed_And_SequenceIndex()
|
|
{
|
|
ShopGoodsBuilder builder = CreateBuilder(
|
|
CreateShopPriceRow(1, RarityType.Blue, 30, 35),
|
|
CreateShopPriceRow(2, RarityType.Purple, 60, 70));
|
|
|
|
string first = BuildGoodsSignature(builder.BuildGoods(goodsCount: 4, runSeed: 901, sequenceIndex: 7));
|
|
string second = BuildGoodsSignature(builder.BuildGoods(goodsCount: 4, runSeed: 901, sequenceIndex: 7));
|
|
|
|
Assert.That(second, Is.EqualTo(first));
|
|
}
|
|
|
|
[Test]
|
|
public void ResolveRandomPrice_Returns_Zero_When_Rarity_Has_No_Price_Row()
|
|
{
|
|
ShopGoodsBuilder builder = CreateBuilder(CreateShopPriceRow(1, RarityType.Blue, 30, 35));
|
|
MethodInfo method = typeof(ShopGoodsBuilder).GetMethod(
|
|
"ResolveRandomPrice",
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
|
|
Assert.That(method, Is.Not.Null);
|
|
|
|
int resolvedPrice = (int)method.Invoke(builder, new object[] { RarityType.Red, new Random(7) });
|
|
|
|
Assert.That(resolvedPrice, Is.EqualTo(0));
|
|
}
|
|
|
|
private static ShopGoodsBuilder CreateBuilder(params DRShopPrice[] shopPriceRows)
|
|
{
|
|
return new ShopGoodsBuilder(
|
|
shopPriceRows,
|
|
new FakeDataTable<DRMuzzleComp>(CreateMuzzleRow(1, "火焰枪口")),
|
|
new FakeDataTable<DRBearingComp>(CreateBearingRow(1, "寒冰轴承")),
|
|
new FakeDataTable<DRBaseComp>(CreateBaseRow(1, "迅捷底座")));
|
|
}
|
|
|
|
private static string BuildGoodsSignature(IReadOnlyList<GoodsItemRawData> goods)
|
|
{
|
|
List<string> parts = new List<string>(goods.Count);
|
|
for (int i = 0; i < goods.Count; i++)
|
|
{
|
|
GoodsItemRawData item = goods[i];
|
|
parts.Add($"{item.GoodsIndex}:{item.Price}:{item.SourceItem.SlotType}:{item.SourceItem.Rarity}:{item.SourceItem.ConfigId}");
|
|
}
|
|
|
|
return string.Join("|", parts);
|
|
}
|
|
|
|
private static DRShopPrice CreateShopPriceRow(int id, RarityType rarity, int minPrice, int maxPrice)
|
|
{
|
|
DRShopPrice row = new DRShopPrice();
|
|
Assert.That(row.ParseDataRow($"\t{id}\t\t{rarity}\t{minPrice}\t{maxPrice}", null), Is.True);
|
|
return row;
|
|
}
|
|
|
|
private static DRMuzzleComp CreateMuzzleRow(int id, string name)
|
|
{
|
|
DRMuzzleComp row = new DRMuzzleComp();
|
|
Assert.That(
|
|
row.ParseDataRow($"\t{id}\t\t{name}\t[10,20,30,40,50]\t3\t0.15\tNormalBullet\t\t[Fire,Crit]", null),
|
|
Is.True);
|
|
return row;
|
|
}
|
|
|
|
private static DRBearingComp CreateBearingRow(int id, string name)
|
|
{
|
|
DRBearingComp row = new DRBearingComp();
|
|
Assert.That(
|
|
row.ParseDataRow($"\t{id}\t\t{name}\t[1,2,3,4,5]\t0.5\t[10,20,30,40,50]\t1\t\t[Ice,Shatter]", null),
|
|
Is.True);
|
|
return row;
|
|
}
|
|
|
|
private static DRBaseComp CreateBaseRow(int id, string name)
|
|
{
|
|
DRBaseComp row = new DRBaseComp();
|
|
Assert.That(
|
|
row.ParseDataRow($"\t{id}\t\t{name}\t[2,4,6,8,10]\t-0.25\tFire\t\t[Fire,Crit]", null),
|
|
Is.True);
|
|
return row;
|
|
}
|
|
|
|
private sealed class FakeDataTable<TRow> : IDataTable<TRow> where TRow : class, IDataRow
|
|
{
|
|
private readonly Dictionary<int, TRow> _rowsById = new();
|
|
|
|
public FakeDataTable(params TRow[] rows)
|
|
{
|
|
if (rows == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < rows.Length; i++)
|
|
{
|
|
TRow row = rows[i];
|
|
if (row != null)
|
|
{
|
|
_rowsById[row.Id] = row;
|
|
}
|
|
}
|
|
}
|
|
|
|
public string Name => typeof(TRow).Name;
|
|
public string FullName => typeof(TRow).FullName;
|
|
public Type Type => typeof(TRow);
|
|
public int Count => _rowsById.Count;
|
|
public TRow this[int id] => GetDataRow(id);
|
|
public TRow MinIdDataRow => _rowsById.Count <= 0 ? null : GetDataRow(GetOrderedIds()[0]);
|
|
public TRow MaxIdDataRow => _rowsById.Count <= 0 ? null : GetDataRow(GetOrderedIds()[^1]);
|
|
|
|
public bool HasDataRow(int id) => _rowsById.ContainsKey(id);
|
|
public bool HasDataRow(Predicate<TRow> condition) => GetDataRow(condition) != null;
|
|
public TRow GetDataRow(int id) => _rowsById.TryGetValue(id, out TRow row) ? row : null;
|
|
|
|
public TRow GetDataRow(Predicate<TRow> condition)
|
|
{
|
|
if (condition == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
foreach (TRow row in _rowsById.Values)
|
|
{
|
|
if (row != null && condition(row))
|
|
{
|
|
return row;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public TRow[] GetDataRows(Predicate<TRow> condition)
|
|
{
|
|
List<TRow> results = new();
|
|
GetDataRows(condition, results);
|
|
return results.ToArray();
|
|
}
|
|
|
|
public void GetDataRows(Predicate<TRow> condition, List<TRow> results)
|
|
{
|
|
results?.Clear();
|
|
if (condition == null || results == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (TRow row in _rowsById.Values)
|
|
{
|
|
if (row != null && condition(row))
|
|
{
|
|
results.Add(row);
|
|
}
|
|
}
|
|
}
|
|
|
|
public TRow[] GetDataRows(Comparison<TRow> comparison)
|
|
{
|
|
List<TRow> results = new();
|
|
GetDataRows(comparison, results);
|
|
return results.ToArray();
|
|
}
|
|
|
|
public void GetDataRows(Comparison<TRow> comparison, List<TRow> results)
|
|
{
|
|
results?.Clear();
|
|
if (results == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
results.AddRange(_rowsById.Values);
|
|
if (comparison != null)
|
|
{
|
|
results.Sort(comparison);
|
|
}
|
|
}
|
|
|
|
public TRow[] GetDataRows(Predicate<TRow> condition, Comparison<TRow> comparison)
|
|
{
|
|
List<TRow> results = new();
|
|
GetDataRows(condition, comparison, results);
|
|
return results.ToArray();
|
|
}
|
|
|
|
public void GetDataRows(Predicate<TRow> condition, Comparison<TRow> comparison, List<TRow> results)
|
|
{
|
|
GetDataRows(condition, results);
|
|
if (results != null && comparison != null)
|
|
{
|
|
results.Sort(comparison);
|
|
}
|
|
}
|
|
|
|
public TRow[] GetAllDataRows()
|
|
{
|
|
List<TRow> results = new();
|
|
GetAllDataRows(results);
|
|
return results.ToArray();
|
|
}
|
|
|
|
public void GetAllDataRows(List<TRow> results)
|
|
{
|
|
results?.Clear();
|
|
if (results == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (int id in GetOrderedIds())
|
|
{
|
|
results.Add(_rowsById[id]);
|
|
}
|
|
}
|
|
|
|
public bool AddDataRow(string dataRowString, object userData) => throw new NotSupportedException();
|
|
public bool AddDataRow(byte[] dataRowBytes, int startIndex, int length, object userData) => throw new NotSupportedException();
|
|
public bool RemoveDataRow(int id) => _rowsById.Remove(id);
|
|
|
|
public void RemoveAllDataRows()
|
|
{
|
|
_rowsById.Clear();
|
|
}
|
|
|
|
public IEnumerator<TRow> GetEnumerator()
|
|
{
|
|
foreach (int id in GetOrderedIds())
|
|
{
|
|
yield return _rowsById[id];
|
|
}
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
private int[] GetOrderedIds()
|
|
{
|
|
int[] ids = new int[_rowsById.Count];
|
|
_rowsById.Keys.CopyTo(ids, 0);
|
|
Array.Sort(ids);
|
|
return ids;
|
|
}
|
|
}
|
|
}
|
|
}
|