150 lines
5.3 KiB
C#
150 lines
5.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace ET.Server
|
|
{
|
|
[FriendOf(typeof(GachaComponent))]
|
|
public static class GachaComponentSystem
|
|
{
|
|
public static List<RewardEntry> Draw(this GachaComponent self, int poolId, GachaDrawType drawType, BagComponent bag)
|
|
{
|
|
List<GachaPoolWeight> weights = GetPoolWeights(poolId);
|
|
if (weights.Count == 0)
|
|
{
|
|
return new List<RewardEntry>();
|
|
}
|
|
|
|
int times = drawType == GachaDrawType.Single ? 1 : 10;
|
|
List<CostEntry> costs = GetDrawCost(poolId, drawType);
|
|
if (!bag.HasItems(costs))
|
|
{
|
|
throw new RpcException(ErrorCode.ERR_GACHA_COST_NOT_ENOUGH, "Gacha cost not enough");
|
|
}
|
|
|
|
bool removed = bag.RemoveItems(costs);
|
|
if (!removed)
|
|
{
|
|
throw new RpcException(ErrorCode.ERR_GACHA_COST_NOT_ENOUGH, "Gacha cost remove failed");
|
|
}
|
|
|
|
List<RewardEntry> rewards = new(times);
|
|
for (int i = 0; i < times; i++)
|
|
{
|
|
GachaPoolWeight pick = PickByWeight(weights);
|
|
rewards.Add(new RewardEntry { ItemId = pick.ItemId, Count = pick.Count });
|
|
self.UpdateGuarantee(poolId, pick.Rare, 90);
|
|
}
|
|
|
|
if (drawType == GachaDrawType.Multi10 && !rewards.Any(r => GetRareByItemId(r.ItemId) >= 4))
|
|
{
|
|
self.ReplaceWithGuaranteed(rewards, weights, 4);
|
|
}
|
|
|
|
bag.AddItems(rewards);
|
|
LogExtensions.Audit(
|
|
"Gacha",
|
|
"Draw",
|
|
$"{{\"poolId\":{poolId},\"drawType\":{(int)drawType},\"rewardsCount\":{rewards.Count},\"guaranteeRemain\":{self.GetGuaranteeRemain(poolId)}}}");
|
|
return rewards;
|
|
}
|
|
|
|
public static void UpdateGuarantee(this GachaComponent self, int poolId, int rare, int guaranteeCount)
|
|
{
|
|
if (!self.PoolStats.TryGetValue(poolId, out GachaPoolState state))
|
|
{
|
|
state = new GachaPoolState { GuaranteeRemain = guaranteeCount };
|
|
self.PoolStats[poolId] = state;
|
|
}
|
|
|
|
state.TotalCount += 1;
|
|
state.LastDrawMs = TimeInfo.Instance.ServerNow();
|
|
if (rare >= 5)
|
|
{
|
|
state.GuaranteeRemain = guaranteeCount;
|
|
return;
|
|
}
|
|
|
|
state.GuaranteeRemain = Math.Max(0, state.GuaranteeRemain - 1);
|
|
if (state.GuaranteeRemain == 0)
|
|
{
|
|
state.GuaranteeRemain = guaranteeCount;
|
|
}
|
|
}
|
|
|
|
public static void ReplaceWithGuaranteed(this GachaComponent self, List<RewardEntry> rewards, List<GachaPoolWeight> weights, int minRare)
|
|
{
|
|
GachaPoolWeight guaranteed = weights.FirstOrDefault(x => x.Rare >= minRare);
|
|
if (guaranteed == null || rewards.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int index = rewards.Count - 1;
|
|
rewards[index] = new RewardEntry { ItemId = guaranteed.ItemId, Count = guaranteed.Count };
|
|
}
|
|
|
|
public static int GetGuaranteeRemain(this GachaComponent self, int poolId)
|
|
{
|
|
if (!self.PoolStats.TryGetValue(poolId, out GachaPoolState state))
|
|
{
|
|
return 90;
|
|
}
|
|
return state.GuaranteeRemain;
|
|
}
|
|
|
|
private static GachaPoolWeight PickByWeight(List<GachaPoolWeight> weights)
|
|
{
|
|
int total = weights.Sum(x => x.Weight);
|
|
int point = CryptoRandomGenerator.Next(0, Math.Max(total, 1));
|
|
int sum = 0;
|
|
foreach (GachaPoolWeight w in weights)
|
|
{
|
|
sum += w.Weight;
|
|
if (point < sum)
|
|
{
|
|
return w;
|
|
}
|
|
}
|
|
|
|
return weights[weights.Count - 1];
|
|
}
|
|
|
|
private static List<CostEntry> GetDrawCost(int poolId, GachaDrawType drawType)
|
|
{
|
|
// TODO: 由 Luban TbGachaPool 驱动。当前使用固定测试配置。
|
|
return drawType == GachaDrawType.Single
|
|
? new List<CostEntry> { new() { ItemId = poolId == 1 ? 1 : 2, Count = 1 } }
|
|
: new List<CostEntry> { new() { ItemId = poolId == 1 ? 1 : 2, Count = 9 } };
|
|
}
|
|
|
|
private static int GetRareByItemId(int itemId)
|
|
{
|
|
if (itemId >= 10000) return 5;
|
|
if (itemId >= 1000) return 4;
|
|
return 3;
|
|
}
|
|
|
|
private static List<GachaPoolWeight> GetPoolWeights(int poolId)
|
|
{
|
|
// TODO: 由 Luban TbGachaWeight 读取。当前提供固定测试池。
|
|
if (poolId == 2)
|
|
{
|
|
return new List<GachaPoolWeight>
|
|
{
|
|
new() { ItemId = 2, Count = 10, Weight = 7000, Rare = 3 },
|
|
new() { ItemId = 1001, Count = 5, Weight = 2500, Rare = 4 },
|
|
new() { ItemId = 10001, Count = 1, Weight = 500, Rare = 5 },
|
|
};
|
|
}
|
|
|
|
return new List<GachaPoolWeight>
|
|
{
|
|
new() { ItemId = 1, Count = 100, Weight = 7000, Rare = 3 },
|
|
new() { ItemId = 1001, Count = 1, Weight = 2500, Rare = 4 },
|
|
new() { ItemId = 10001, Count = 1, Weight = 500, Rare = 5 },
|
|
};
|
|
}
|
|
}
|
|
}
|