mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
using RadicadeServer.RecNet.Enums.Store;
|
||||
|
||||
namespace RadicadeServer.RecNet.Royale
|
||||
{
|
||||
public class RoyaleProgressDTO
|
||||
{
|
||||
public long TotalXP { get; set; }
|
||||
public int Level { get; set; }
|
||||
public int RankIdx { get; set; }
|
||||
public string RankName { get; set; } = string.Empty;
|
||||
public long CurrentLevelXPThreshold { get; set; }
|
||||
public long NextLevelXPThreshold { get; set; }
|
||||
public int NextLevelAcornReward { get; set; }
|
||||
}
|
||||
|
||||
public class RoyaleMatchCompleteStats
|
||||
{
|
||||
public int Rank { get; set; }
|
||||
public int NumEliminations { get; set; }
|
||||
public int SecondsAlive { get; set; }
|
||||
public bool WalkGame { get; set; }
|
||||
public bool CustomGame { get; set; }
|
||||
public int ChestsOpened { get; set; }
|
||||
public int ShieldPotionsConsumed { get; set; }
|
||||
public int HealthPotionsConsumed { get; set; }
|
||||
public int SecondsInAir { get; set; }
|
||||
}
|
||||
|
||||
public class RoyaleMatchCompleteResponse
|
||||
{
|
||||
public List<string> XPAwardStrings { get; set; } = new();
|
||||
public long TotalXPAwarded { get; set; }
|
||||
public List<RoyaleProgressDTO> NewProgress { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.RecNet.Leveling;
|
||||
using RadicadeServer.RecNet.Progression;
|
||||
using RadicadeServer.RecNet.Royale;
|
||||
using RadicadeServer.Utils;
|
||||
|
||||
namespace RadicadeServer.API.Royale
|
||||
{
|
||||
public static class RoyaleShared
|
||||
{
|
||||
public static readonly string[] RankNames =
|
||||
{
|
||||
"Recruit", "Cadet", "Private", "Corporal", "Sergeant",
|
||||
"Lieutenant", "Captain", "Major", "Colonel", "General"
|
||||
};
|
||||
|
||||
public static int RankIdxForLevel(int level) =>
|
||||
Math.Clamp((level - 1) / 5, 0, RankNames.Length - 1);
|
||||
|
||||
public static (long current, long next) XpThresholds(List<LevelProgressionMap> map, int level)
|
||||
{
|
||||
long cumulative = 0;
|
||||
long current = 0;
|
||||
long next = 0;
|
||||
foreach (var entry in map.OrderBy(x => x.Level))
|
||||
{
|
||||
if (entry.Level <= 0) continue;
|
||||
if (entry.Level <= level)
|
||||
{
|
||||
cumulative += entry.RequiredXp;
|
||||
current = cumulative;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (next == 0) next = cumulative + entry.RequiredXp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (next == 0) next = current;
|
||||
return (current, next);
|
||||
}
|
||||
|
||||
public static RoyaleProgressDTO BuildProgress(ProgressionData data)
|
||||
{
|
||||
var map = LevelUpGiftService.ParseMap();
|
||||
int level = LevelUpGiftService.DisplayLevel(data.Level);
|
||||
var (current, next) = XpThresholds(map, level);
|
||||
int rankIdx = RankIdxForLevel(level);
|
||||
long totalXp = level >= LevelUpGiftService.MaxLevel ? next : data.XP;
|
||||
return new RoyaleProgressDTO
|
||||
{
|
||||
TotalXP = totalXp,
|
||||
Level = level,
|
||||
RankIdx = rankIdx,
|
||||
RankName = RankNames[rankIdx],
|
||||
CurrentLevelXPThreshold = current,
|
||||
NextLevelXPThreshold = next,
|
||||
NextLevelAcornReward = Math.Max(10, level * 5)
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task<RoyaleProgressDTO> AwardXpAsync(
|
||||
RecDBContext db, long playerId, int xp, string description, List<string> awardStrings)
|
||||
{
|
||||
var progression = await db.Progression.FirstOrDefaultAsync(x => x.PlayerId == playerId);
|
||||
if (progression == null)
|
||||
{
|
||||
progression = new ProgressionData { PlayerId = playerId };
|
||||
db.Progression.Add(progression);
|
||||
}
|
||||
|
||||
if (xp <= 0)
|
||||
{
|
||||
await db.SaveChangesAsync();
|
||||
return BuildProgress(progression);
|
||||
}
|
||||
|
||||
int granted = (int)Math.Min(xp, int.MaxValue);
|
||||
int oldLevel = progression.Level;
|
||||
progression.XP = (int)Math.Min(progression.XP + granted, int.MaxValue);
|
||||
progression.RecordXpGrant(granted);
|
||||
|
||||
await LevelUpGiftService.DeliverLevelUpGiftsAsync(db, playerId,
|
||||
recomputeFromXp: true, fromLevel: oldLevel, notify: true);
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
if (granted > 0 && awardStrings.Count == 0)
|
||||
awardStrings.Add($"{description}: +{granted} XP");
|
||||
|
||||
if (granted > 0)
|
||||
await RoomProgressionService.SendProgressionLevelUpdateAsync(db, playerId, progression);
|
||||
|
||||
return BuildProgress(progression);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.Extensions;
|
||||
using RadicadeServer.RecNet.Progression;
|
||||
using RadicadeServer.RecNet.Royale;
|
||||
|
||||
namespace RadicadeServer.API.Royale.V1
|
||||
{
|
||||
[Route("api/royale/v1")]
|
||||
public class RoyaleV1(RecDBContext db) : Controller
|
||||
{
|
||||
[HttpGet("current"), Authorize]
|
||||
public async Task<ActionResult<RoyaleProgressDTO>> GetCurrentProgress()
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
if (userId == null) return Unauthorized();
|
||||
|
||||
var data = await db.Progression.FirstOrDefaultAsync(x => x.PlayerId == userId.Value);
|
||||
data ??= new ProgressionData { PlayerId = userId.Value };
|
||||
|
||||
return Ok(RoyaleShared.BuildProgress(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.Extensions;
|
||||
using RadicadeServer.RecNet.Royale;
|
||||
|
||||
namespace RadicadeServer.API.Royale.V2
|
||||
{
|
||||
[Route("api/royale/v2")]
|
||||
public class RoyaleV2(RecDBContext db) : Controller
|
||||
{
|
||||
private static int PlacementXp(int rank)
|
||||
{
|
||||
if (rank <= 0) return 10;
|
||||
if (rank == 1) return 50;
|
||||
if (rank <= 3) return 30;
|
||||
if (rank <= 8) return 15;
|
||||
return 10;
|
||||
}
|
||||
|
||||
[HttpPost("matchcomplete"), Authorize]
|
||||
public async Task<ActionResult<RoyaleMatchCompleteResponse>> MatchComplete([FromBody] RoyaleMatchCompleteStats? stats)
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
if (userId == null) return Unauthorized();
|
||||
if (stats == null) return BadRequest();
|
||||
|
||||
var awardStrings = new List<string>();
|
||||
int totalXp = 0;
|
||||
|
||||
if (!stats.CustomGame)
|
||||
{
|
||||
int placement = PlacementXp(stats.Rank);
|
||||
totalXp += placement;
|
||||
awardStrings.Add($"Match finished: +{placement} XP");
|
||||
|
||||
if (stats.NumEliminations > 0)
|
||||
{
|
||||
int elimXp = Math.Min(stats.NumEliminations, 5) * 10;
|
||||
totalXp += elimXp;
|
||||
awardStrings.Add($"Eliminations: +{elimXp} XP");
|
||||
}
|
||||
|
||||
if (stats.ChestsOpened > 0)
|
||||
{
|
||||
int chestXp = Math.Min(stats.ChestsOpened, 10) * 2;
|
||||
totalXp += chestXp;
|
||||
awardStrings.Add($"Chests opened: +{chestXp} XP");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
awardStrings.Add("Custom game: no XP awarded");
|
||||
}
|
||||
|
||||
long xpBefore = await CurrentXp(userId.Value);
|
||||
var progress = await RoyaleShared.AwardXpAsync(db, userId.Value, totalXp, "Rec Royale match", awardStrings);
|
||||
|
||||
var response = new RoyaleMatchCompleteResponse
|
||||
{
|
||||
XPAwardStrings = awardStrings,
|
||||
TotalXPAwarded = progress.TotalXP - xpBefore,
|
||||
NewProgress = new List<RoyaleProgressDTO> { progress }
|
||||
};
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
private async Task<long> CurrentXp(long playerId)
|
||||
{
|
||||
var data = await db.Progression.AsNoTracking().FirstOrDefaultAsync(x => x.PlayerId == playerId);
|
||||
return data?.XP ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user