Radicade Public Code

This commit is contained in:
JogmansonReal
2026-10-16 23:33:13 -07:00
commit 55175acdf6
832 changed files with 363261 additions and 0 deletions
@@ -0,0 +1,290 @@
using Microsoft.EntityFrameworkCore;
using RadicadeServer.API.Storefront;
using RadicadeServer.DB;
using RadicadeServer.Extensions;
using RadicadeServer.RecNet.Avatar;
using RadicadeServer.RecNet.Enums.Players.Avatar;
using RadicadeServer.RecNet.Enums.Store;
using RadicadeServer.RecNet.Leveling;
using RadicadeServer.RecNet.Progression;
using RadicadeServer.RecNet.Store;
namespace RadicadeServer.Utils
{
public static class LevelUpGiftService
{
public static List<LevelProgressionMap> ParseMap()
{
var map = FirstTimeUtils.LevelProgressionMap.FromJson<List<LevelProgressionMap>>() ?? new List<LevelProgressionMap>();
return map.OrderBy(x => x.Level).ToList();
}
public const int MaxLevel = 50;
public static int ComputeLevel(List<LevelProgressionMap> map, int xp)
{
int cumulative = 0;
int level = 1;
foreach (var entry in map.OrderBy(x => x.Level))
{
if (entry.Level <= 0) continue;
cumulative += entry.RequiredXp;
if (xp < cumulative) break;
level = entry.Level;
}
return level > MaxLevel ? MaxLevel : level;
}
public static int ThresholdXpForLevel(List<LevelProgressionMap> map, int level)
{
int threshold = 0;
foreach (var entry in map.OrderBy(x => x.Level))
{
if (entry.Level <= 0 || entry.Level > level) continue;
threshold += entry.RequiredXp;
}
return threshold;
}
public static int RequiredXpForLevel(List<LevelProgressionMap> map, int level) =>
ThresholdXpForLevel(map, level) - ThresholdXpForLevel(map, level - 1);
public static int DisplayLevel(int level) => level > MaxLevel ? MaxLevel : level;
public static int XpIntoCurrentLevel(List<LevelProgressionMap> map, int level, int totalXp)
{
if (level >= MaxLevel) return RequiredXpForLevel(map, MaxLevel);
int reached = ComputeLevel(map, totalXp);
int from = level < reached ? level : reached;
int into = totalXp - ThresholdXpForLevel(map, from);
return into < 0 ? 0 : into;
}
public static int XpIntoCurrentLevel(ProgressionData data) =>
XpIntoCurrentLevel(ParseMap(), data.Level, data.XP);
public const bool LevelUpGiftDropsAreSeeded = false;
private static List<AvatarItem>? _avatarItems;
private static List<ConsumableItem>? _consumables;
internal static List<AvatarItem> ClothingCatalogue(string? json = null)
{
if (!string.IsNullOrWhiteSpace(json)) return ParseClothingCatalogue(json);
return _avatarItems ??= ParseClothingCatalogue(DBConfig.DefaultAvatarCatalog);
}
private static List<AvatarItem> ParseClothingCatalogue(string json) =>
(json.FromJson<List<AvatarItem>>() ?? new List<AvatarItem>())
.Where(x => x != null && !string.IsNullOrWhiteSpace(x.AvatarItemDesc) && !x.IsBaseAvatarItem)
.OrderBy(x => x.AvatarItemId)
.ThenBy(x => x.AvatarItemDesc, StringComparer.Ordinal)
.ToList();
private static List<ConsumableItem> ConsumableCatalogue(string? json = null)
{
if (!string.IsNullOrWhiteSpace(json)) return ParseConsumableCatalogue(json);
return _consumables ??= ParseConsumableCatalogue(DBConfig.DefaultConsumableItems);
}
private static List<ConsumableItem> ParseConsumableCatalogue(string json) =>
(json.FromJson<List<ConsumableItem>>() ?? new List<ConsumableItem>())
.Where(x => x != null && !string.IsNullOrWhiteSpace(x.ConsumableItemDesc))
.OrderBy(x => x.ConsumableItemDesc, StringComparer.Ordinal)
.ToList();
public static Gift? BuildItemReward(
int level,
string? avatarCatalogJson = null,
string? consumableCatalogJson = null,
ISet<string>? owned = null,
Random? random = null,
LevelUpRewardPool.Pool? pool = null)
{
if (!LevelRewardTable.IsItemLevel(level)) return null;
var ownedKeys = owned ?? new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var rng = random ?? Random.Shared;
return LevelRewardTable.KindForLevel(level) == LevelRewardTable.RewardKind.Consumable
? BuildConsumableReward(level, consumableCatalogJson, ownedKeys, rng, pool)
: BuildClothingReward(level, avatarCatalogJson, consumableCatalogJson, ownedKeys, rng, pool);
}
private static Gift? BuildClothingReward(
int level,
string? avatarCatalogJson,
string? consumableCatalogJson,
ISet<string> owned,
Random random,
LevelUpRewardPool.Pool? pool)
{
var rarity = LevelRewardTable.RarityForLevel(level);
var pick = LevelUpRewardPool.PickAvatarItem(ClothingCatalogue(avatarCatalogJson), rarity, owned, random, pool);
if (pick == null)
{
return BuildConsumableReward(level, consumableCatalogJson, owned, random, pool);
}
var label = LevelRewardTable.StarLabel(rarity);
return new Gift
{
FriendlyName = $"Level {level} Reward - {label} {pick.FriendlyName}",
Tooltip = $"Congrats on getting to Level {level}!",
AvatarItemDesc = pick.AvatarItemDesc,
AvatarItemType = AvatarItemType.Outfit,
Rarity = rarity,
Currency = 0,
Xp = 0
};
}
private static Gift? BuildConsumableReward(
int level,
string? consumableCatalogJson,
ISet<string> owned,
Random random,
LevelUpRewardPool.Pool? pool)
{
var pick = LevelUpRewardPool.PickConsumable(ConsumableCatalogue(consumableCatalogJson), owned, random, pool);
if (pick == null)
{
return null;
}
var token = ConsumableCatalog.ToClientDesc(pick.ConsumableItemDesc);
if (string.IsNullOrWhiteSpace(token)) return null;
return new Gift
{
FriendlyName = $"Level {level} Reward - {pick.FriendlyName}",
Tooltip = $"Congrats on getting to Level {level}!",
ConsumableItemDesc = token,
Rarity = pick.Rarity,
Currency = 0,
Xp = 0
};
}
public static Gift? BuildConsumableGift(
string? consumableCatalogJson, ISet<string> owned, Random random, LevelUpRewardPool.Pool? pool = null)
{
var pick = LevelUpRewardPool.PickConsumable(ConsumableCatalogue(consumableCatalogJson), owned, random, pool);
if (pick == null) return null;
var token = ConsumableCatalog.ToClientDesc(pick.ConsumableItemDesc);
if (string.IsNullOrWhiteSpace(token)) return null;
return new Gift
{
FriendlyName = pick.FriendlyName,
Tooltip = pick.FriendlyName,
ConsumableItemDesc = token,
Rarity = pick.Rarity,
Currency = 0,
Xp = 0
};
}
public static async Task<HashSet<string>> CollectOwnedKeysAsync(
RecDBContext db, long playerId, GiftContext? pendingContext = GiftContext.LevelUp)
{
var owned = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var unlocks = await db.Unlocks.FirstOrDefaultAsync(x => x.AccountId == playerId);
if (unlocks != null)
{
foreach (var item in unlocks.GetAvatarItems())
if (!string.IsNullOrWhiteSpace(item?.AvatarItemDesc))
owned.Add(LevelUpRewardPool.OwnershipKey(item.AvatarItemDesc));
foreach (var item in unlocks.GetConsumables())
if (!string.IsNullOrWhiteSpace(item?.ConsumableItemDesc))
owned.Add(LevelUpRewardPool.OwnershipKey(item.ConsumableItemDesc));
}
var gifts = await db.Gifts.FirstOrDefaultAsync(x => x.AccountId == playerId);
foreach (var pending in StorefrontShared.ParseGiftList(gifts?.Data))
{
if (pending == null) continue;
if (pendingContext.HasValue && pending.Context != pendingContext.Value) continue;
Remember(owned, pending);
}
return owned;
}
private static void Remember(HashSet<string> owned, Gift gift)
{
if (!string.IsNullOrWhiteSpace(gift.AvatarItemDesc))
owned.Add(LevelUpRewardPool.OwnershipKey(gift.AvatarItemDesc));
if (!string.IsNullOrWhiteSpace(gift.ConsumableItemDesc))
owned.Add(LevelUpRewardPool.OwnershipKey(gift.ConsumableItemDesc));
}
public static async Task<List<Gift>> DeliverLevelUpGiftsAsync(RecDBContext db, long playerId, bool recomputeFromXp, int? fromLevel = null, bool notify = true)
{
var progression = await db.Progression.FirstOrDefaultAsync(x => x.PlayerId == playerId);
if (progression == null) return new List<Gift>();
var map = ParseMap();
int oldLevel = fromLevel ?? progression.Level;
int target = progression.Level;
if (recomputeFromXp)
{
int computed = ComputeLevel(map, progression.XP);
if (computed > target) target = computed;
}
if (target <= oldLevel) return new List<Gift>();
var json = progression.GetProgressionJson() ?? new ProgressionJson();
var delivered = json.DeliveredLevelUpGiftLevels ?? new List<int>();
var owned = await CollectOwnedKeysAsync(db, playerId);
var random = Random.Shared;
var toPersist = new List<Gift>();
foreach (var entry in map.OrderBy(e => e.Level))
{
if (entry.Level <= oldLevel) continue;
if (entry.Level > target) break;
if (delivered.Contains(entry.Level)) continue;
var gift = LevelRewardTable.IsItemLevel(entry.Level)
? BuildItemReward(entry.Level, owned: owned, random: random)
: null;
if (gift == null) continue;
Remember(owned, gift);
gift.GiftDropId = StorefrontShared.NewGiftDropId();
gift.Context = GiftContext.LevelUp;
gift.FromPlayerId = 0;
toPersist.Add(gift);
delivered.Add(entry.Level);
}
if (toPersist.Count == 0) return new List<Gift>();
json.DeliveredLevelUpGiftLevels = delivered;
progression.Progress = json.ToJson();
progression.Level = target;
foreach (var gift in toPersist)
{
await StorefrontShared.PersistGiftBoxAsync(db, playerId, gift, notify: false);
}
await db.SaveChangesAsync();
if (notify)
{
foreach (var gift in toPersist)
await StorefrontShared.SendGiftPackageNotificationAsync(playerId, gift);
}
return toPersist;
}
}
}