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> MatchComplete([FromBody] RoyaleMatchCompleteStats? stats) { var userId = User.GetUserId(); if (userId == null) return Unauthorized(); if (stats == null) return BadRequest(); var awardStrings = new List(); 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 { progress } }; return Ok(response); } private async Task CurrentXp(long playerId) { var data = await db.Progression.AsNoTracking().FirstOrDefaultAsync(x => x.PlayerId == playerId); return data?.XP ?? 0; } } }