Files
Fishdice/FishDice/Assets/FishDice/Runtime/LuckyDice/LuckySelection/LuckyDiceFlowService.cs
2026-06-23 14:13:08 +08:00

77 lines
2.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace FishDice.LuckyDice
{
public sealed class LuckyDiceFlowService
{
private readonly IRandomSource _random;
public LuckyDiceFlowService(IRandomSource random)
{
_random = random ?? throw new ArgumentNullException(nameof(random));
}
public LuckyDiceSelectionResult Select(
IReadOnlyList<LuckyDiceCandidateConfig> candidates,
RollFlowRequest request,
DiceSetConfig luckyDiceSetConfig)
{
var source = candidates ?? Array.Empty<LuckyDiceCandidateConfig>();
var filtered = source
.Where(candidate => candidate.Enabled)
.Where(candidate => request.PlayerLevel >= candidate.MinLevel)
.Where(candidate => candidate.SourceFilter == null ||
candidate.SourceFilter.Count == 0 ||
candidate.SourceFilter.Contains(request.TriggerSource))
.ToArray();
var fallbackType = "None";
int? randomValue = null;
var selected = PickWeighted(filtered, out randomValue);
if (selected == null)
{
selected = filtered.FirstOrDefault(candidate => candidate.IsDefault);
fallbackType = selected == null ? "NormalRewardFallback" : "DefaultResult";
}
var slotCount = selected == null ? 0 : selected.ResultSlotCount ?? luckyDiceSetConfig.DefaultResultSlotCount ?? 3;
return new LuckyDiceSelectionResult(
selected,
source.Count,
filtered.Length,
randomValue,
fallbackType,
slotCount);
}
private LuckyDiceCandidateConfig PickWeighted(IReadOnlyList<LuckyDiceCandidateConfig> candidates, out int? randomValue)
{
var weighted = candidates.Where(candidate => candidate.Weight > 0).ToArray();
var totalWeight = weighted.Sum(candidate => candidate.Weight);
if (totalWeight <= 0)
{
randomValue = null;
return null;
}
var roll = _random.Next(0, totalWeight);
randomValue = roll;
var cursor = 0;
foreach (var candidate in weighted)
{
cursor += candidate.Weight;
if (roll < cursor)
{
return candidate;
}
}
return weighted[weighted.Length - 1];
}
}
}