mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 15:09:26 -07:00
214 lines
9.4 KiB
C#
214 lines
9.4 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using RadicadeServer.API.Storefront;
|
|
using RadicadeServer.DB;
|
|
using RadicadeServer.Extensions;
|
|
using RadicadeServer.RecNet.Avatar;
|
|
using RadicadeServer.RecNet.Enums.Store;
|
|
using RadicadeServer.RecNet.Progression;
|
|
|
|
namespace RadicadeServer.Utils
|
|
{
|
|
public static class GameRewardRedemption
|
|
{
|
|
public static async Task<Gift?> TakeOutstandingAsync(
|
|
RecDBContext db, long playerId, GiftContext context, string? message = null, Action<string>? trace = null)
|
|
{
|
|
var data = await db.Progression.FirstOrDefaultAsync(x => x.PlayerId == playerId);
|
|
var json = data?.GetProgressionJson();
|
|
var pending = json?.PendingGameRewards;
|
|
|
|
var outstanding = pending?.Where(r => !r.Consumed).ToList() ?? new List<RecNet.Storefront.GameReward>();
|
|
trace?.Invoke($"stage=OutstandingReward claimed={RewardTrace.Context(context)} " +
|
|
$"rewards={pending?.Count ?? 0} outstanding={outstanding.Count}");
|
|
|
|
if (data == null || json == null || outstanding.Count == 0) return null;
|
|
|
|
foreach (var candidate in outstanding)
|
|
{
|
|
var held = candidate.GiftDrop1 ?? candidate.GiftDrop2 ?? candidate.GiftDrop3;
|
|
trace?.Invoke($"stage=OutstandingCandidate selectionId={candidate.RewardSelectionId} " +
|
|
$"rewardType={candidate.RewardType} context={RewardTrace.Context(candidate.GiftContext)} " +
|
|
$"box={RewardTrace.Box(held)}");
|
|
}
|
|
|
|
var reward = outstanding.FirstOrDefault(r => r.GiftContext == context);
|
|
var matchedBy = reward == null ? null : "context";
|
|
|
|
if (reward == null && RroRewardPools.RoomForContext(context) is { } room)
|
|
{
|
|
reward = outstanding.FirstOrDefault(r => RroRewardPools.RoomForContext(r.GiftContext) == room);
|
|
matchedBy = reward == null ? null : $"room={room}";
|
|
}
|
|
|
|
if (reward == null)
|
|
{
|
|
reward = outstanding[0];
|
|
matchedBy = "oldest outstanding";
|
|
}
|
|
|
|
var drop = reward.GiftDrop1 ?? reward.GiftDrop2 ?? reward.GiftDrop3;
|
|
if (drop == null)
|
|
{
|
|
trace?.Invoke($"stage=OutstandingRewardUnusable selectionId={reward.RewardSelectionId} " +
|
|
"has no gift drop at all; nothing can be handed over from it.");
|
|
return null;
|
|
}
|
|
|
|
var gift = drop.Clone();
|
|
if (!string.IsNullOrWhiteSpace(message)) gift.Tooltip = message;
|
|
if (gift.GiftDropId == 0) gift.GiftDropId = StorefrontShared.NewGiftDropId();
|
|
|
|
reward.Consumed = true;
|
|
reward.ConsumedGiftDropId = gift.GiftDropId;
|
|
reward.ConsumedAt = DateTime.UtcNow;
|
|
|
|
data.Progress = json.ToJson();
|
|
await db.SaveChangesAsync();
|
|
|
|
trace?.Invoke($"stage=OutstandingRewardMatched by={matchedBy} selectionId={reward.RewardSelectionId} " +
|
|
$"rewardType={reward.RewardType} context={RewardTrace.Context(reward.GiftContext)} " +
|
|
$"box={RewardTrace.Box(gift)}");
|
|
|
|
return gift;
|
|
}
|
|
|
|
public static async Task<Gift?> BuildRoomRewardAsync(
|
|
RecDBContext db, long playerId, GiftContext context, string? message = null,
|
|
Action<string>? trace = null, string? catalogueJson = null)
|
|
{
|
|
var room = RroRewardPools.RoomForContext(context);
|
|
if (room == null) return null;
|
|
|
|
var owned = await LevelUpGiftService.CollectOwnedKeysAsync(db, playerId, context);
|
|
|
|
Gift? box = RroRewardPools.BuildGift(context, owned, catalogueJson: catalogueJson);
|
|
var paidFrom = context;
|
|
|
|
if (box == null)
|
|
{
|
|
foreach (var weaker in RroRewardPools.LowerRanksOf(context))
|
|
{
|
|
box = RroRewardPools.BuildGift(weaker, owned, catalogueJson: catalogueJson);
|
|
if (box == null) continue;
|
|
|
|
paidFrom = weaker;
|
|
trace?.Invoke($"stage=RoomBoxFallbackRank context={RewardTrace.Context(context)} " +
|
|
$"nothing left unowned, so the same quest's {RewardTrace.Context(weaker)} is what it pays.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (box == null)
|
|
{
|
|
box = RroRewardPools.BuildFallbackDrops(selection: false, owned: owned).FirstOrDefault();
|
|
if (box != null)
|
|
{
|
|
paidFrom = RroRewardPools.ReacquisitionContext;
|
|
trace?.Invoke($"stage=RoomBoxFallbackItem context={RewardTrace.Context(context)} " +
|
|
"every reward the activity pays is owned; paying the non-exclusive item pool.");
|
|
}
|
|
}
|
|
|
|
if (box == null) return null;
|
|
|
|
box.GiftDropId = StorefrontShared.NewGiftDropId();
|
|
if (!string.IsNullOrWhiteSpace(message)) box.Tooltip = message;
|
|
|
|
trace?.Invoke($"stage=RoomBox activity={room} context={RewardTrace.Context(context)} " +
|
|
$"paidFrom={RewardTrace.Context(paidFrom)} box={RewardTrace.Box(box)}");
|
|
|
|
await RememberHandoverAsync(db, playerId, context, box, trace);
|
|
return box;
|
|
}
|
|
|
|
public static async Task RememberHandoverAsync(
|
|
RecDBContext db, long playerId, GiftContext context, Gift box, Action<string>? trace = null)
|
|
{
|
|
var data = await db.Progression.FirstOrDefaultAsync(x => x.PlayerId == playerId);
|
|
if (data == null)
|
|
{
|
|
data = new ProgressionData { PlayerId = playerId };
|
|
db.Progression.Add(data);
|
|
}
|
|
|
|
var json = data.GetProgressionJson() ?? new ProgressionJson();
|
|
json.PendingGameRewards ??= new List<RecNet.Storefront.GameReward>();
|
|
json.PendingGameRewards.Add(new RecNet.Storefront.GameReward
|
|
{
|
|
RewardSelectionId = StorefrontShared.NewGiftDropId(),
|
|
Message = box.Tooltip ?? string.Empty,
|
|
GiftContext = context,
|
|
RewardType = RroRewardPools.IsQuestContext(context)
|
|
? RewardType.PostQuestActivity
|
|
: RewardType.PostGameActivity,
|
|
GiftDrop1 = box.Clone(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Consumed = true,
|
|
ConsumedGiftDropId = box.GiftDropId,
|
|
ConsumedAt = DateTime.UtcNow
|
|
});
|
|
|
|
data.Progress = json.ToJson();
|
|
await db.SaveChangesAsync();
|
|
|
|
trace?.Invoke($"stage=HandoverRecorded context={RewardTrace.Context(context)} " +
|
|
$"box={RewardTrace.Box(box)} the completion is recorded as delivered, so a later request " +
|
|
"for it cannot pay a second box.");
|
|
}
|
|
|
|
public static async Task<Gift?> TakePendingBoxAsync(
|
|
RecDBContext db, long playerId, GiftContext context, Action<string>? trace = null)
|
|
{
|
|
var room = RroRewardPools.RoomForContext(context);
|
|
if (room == null || !RroRewardPools.IsRewardActivity(room)) return null;
|
|
|
|
var boxes = await StorefrontShared.GetPendingGiftsAsync(db, playerId);
|
|
var box = boxes.FirstOrDefault(b => b.Context == context)
|
|
?? boxes.FirstOrDefault(b => RroRewardPools.RoomForContext(b.Context) == room);
|
|
|
|
trace?.Invoke($"stage=PendingBox claimed={RewardTrace.Context(context)} room={room} " +
|
|
$"held={boxes.Count} match={RewardTrace.Box(box)}");
|
|
|
|
if (box == null) return null;
|
|
|
|
var gift = box.Clone();
|
|
if (gift.GiftDropId == 0) gift.GiftDropId = StorefrontShared.NewGiftDropId();
|
|
return gift;
|
|
}
|
|
|
|
public static async Task<Gift?> TakeRecentlyHandedOverAsync(
|
|
RecDBContext db, long playerId, GiftContext context, TimeSpan within, Action<string>? trace = null)
|
|
{
|
|
var data = await db.Progression.FirstOrDefaultAsync(x => x.PlayerId == playerId);
|
|
var json = data?.GetProgressionJson();
|
|
var pending = json?.PendingGameRewards;
|
|
if (pending == null || pending.Count == 0) return null;
|
|
|
|
var cutoff = DateTime.UtcNow - within;
|
|
var taken = pending
|
|
.Where(r => r.Consumed && r.ConsumedAt is { } at && at >= cutoff)
|
|
.Where(r => r.GiftContext == context
|
|
|| (RroRewardPools.RoomForContext(context) is { } room
|
|
&& RroRewardPools.RoomForContext(r.GiftContext) == room))
|
|
.OrderByDescending(r => r.ConsumedAt)
|
|
.FirstOrDefault();
|
|
|
|
if (taken == null)
|
|
{
|
|
trace?.Invoke($"stage=HandedOver claimed={RewardTrace.Context(context)} none within {within}.");
|
|
return null;
|
|
}
|
|
|
|
var drop = taken.GiftDrop1 ?? taken.GiftDrop2 ?? taken.GiftDrop3;
|
|
if (drop == null) return null;
|
|
|
|
var gift = drop.Clone();
|
|
gift.GiftDropId = taken.ConsumedGiftDropId != 0 ? taken.ConsumedGiftDropId : gift.GiftDropId;
|
|
trace?.Invoke($"stage=HandedOver claimed={RewardTrace.Context(context)} " +
|
|
$"selectionId={taken.RewardSelectionId} box={RewardTrace.Box(gift)} - answering the retry " +
|
|
"with the box it was given.");
|
|
return gift;
|
|
}
|
|
}
|
|
}
|