using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using RR3CommunityServer.Data; using RR3CommunityServer.Models; using static RR3CommunityServer.Data.RR3DbContext; namespace RR3CommunityServer.Controllers; [ApiController] [Route("synergy/[controller]")] public class ProgressionController : ControllerBase { private readonly RR3DbContext _context; private readonly ILogger _logger; public ProgressionController(RR3DbContext context, ILogger logger) { _context = context; _logger = logger; } /// /// Get player progression data (career, owned cars, upgrades, etc.) /// [HttpGet("player/{synergyId}")] public async Task GetPlayerProgression(string synergyId) { _logger.LogInformation("Getting progression for {SynergyId}", synergyId); var user = await _context.Users .Include(u => u.OwnedCars) .Include(u => u.CareerProgress) .FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return NotFound(new { error = "User not found" }); } return Ok(new { playerId = user.SynergyId, level = user.Level ?? 1, experience = user.Experience ?? 0, gold = user.Gold ?? 0, cash = user.Cash ?? 0, reputation = user.Reputation ?? 0, ownedCars = user.OwnedCars.Select(c => new { id = c.CarId, name = c.CarName, manufacturer = c.Manufacturer, class_type = c.ClassType, performance_rating = c.PerformanceRating, upgrade_level = c.UpgradeLevel, purchased_upgrades = c.PurchasedUpgrades }), careerProgress = user.CareerProgress.Select(cp => new { series = cp.SeriesName, eventName = cp.EventName, completed = cp.Completed, stars = cp.StarsEarned, best_time = cp.BestTime }) }); } /// /// Update player progression (complete event, earn XP, etc.) /// [HttpPost("player/{synergyId}/update")] public async TaskUpdateProgression(string synergyId, [FromBody] ProgressionUpdate update) { _logger.LogInformation("Updating progression for {SynergyId}", synergyId); var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) { return NotFound(new { error = "User not found" }); } // Update currency if (update.GoldEarned.HasValue) { user.Gold = (user.Gold ?? 0) + update.GoldEarned.Value; } if (update.CashEarned.HasValue) { user.Cash = (user.Cash ?? 0) + update.CashEarned.Value; } // Update XP and level if (update.ExperienceEarned.HasValue) { user.Experience = (user.Experience ?? 0) + update.ExperienceEarned.Value; // Level up calculation (every 1000 XP = 1 level) int newLevel = (user.Experience.Value / 1000) + 1; if (newLevel > (user.Level ?? 1)) { user.Level = newLevel; // Level up rewards user.Gold = (user.Gold ?? 0) + (newLevel * 10); // 10 gold per level user.Cash = (user.Cash ?? 0) + (newLevel * 5000); // 5k cash per level } } // Update reputation if (update.ReputationEarned.HasValue) { user.Reputation = (user.Reputation ?? 0) + update.ReputationEarned.Value; } await _context.SaveChangesAsync(); return Ok(new { success = true, newGold = user.Gold, newCash = user.Cash, newExperience = user.Experience, newLevel = user.Level, newReputation = user.Reputation, leveledUp = update.ExperienceEarned.HasValue && (user.Experience.Value / 1000) + 1 > (user.Level ?? 1) - 1 }); } /// /// Purchase/unlock a car /// [HttpPost("car/purchase")] public async Task PurchaseCar([FromBody] CarPurchaseRequest request) { _logger.LogInformation("Purchasing car {CarId} for {SynergyId}", request.CarId, request.SynergyId); var user = await _context.Users .Include(u => u.OwnedCars) .FirstOrDefaultAsync(u => u.SynergyId == request.SynergyId); if (user == null) { return NotFound(new { error = "User not found" }); } // Check if already owned if (user.OwnedCars.Any(c => c.CarId == request.CarId)) { return BadRequest(new { error = "Car already owned" }); } // Get car data from catalog var carData = await _context.Cars.FirstOrDefaultAsync(c => c.CarId == request.CarId); if (carData == null) { return NotFound(new { error = "Car not found in catalog" }); } // Check currency (in community server, can make it free or use price) int costGold = request.UseGold ? carData.GoldPrice : 0; int costCash = !request.UseGold ? carData.CashPrice : 0; if (request.UseGold && (user.Gold ?? 0) < costGold) { return BadRequest(new { error = "Insufficient gold" }); } if (!request.UseGold && (user.Cash ?? 0) < costCash) { return BadRequest(new { error = "Insufficient cash" }); } // Deduct currency if (request.UseGold) { user.Gold -= costGold; } else { user.Cash -= costCash; } // Add car to garage var ownedCar = new OwnedCar { UserId = user.Id, CarId = carData.CarId, CarName = carData.Name, Manufacturer = carData.Manufacturer, ClassType = carData.ClassType, PerformanceRating = carData.BasePerformanceRating, UpgradeLevel = 0, PurchasedUpgrades = "", PurchasedAt = DateTime.UtcNow }; _context.OwnedCars.Add(ownedCar); await _context.SaveChangesAsync(); return Ok(new { success = true, carId = ownedCar.CarId, carName = ownedCar.CarName, goldSpent = costGold, cashSpent = costCash, remainingGold = user.Gold, remainingCash = user.Cash }); } /// /// Upgrade a car /// [HttpPost("car/upgrade")] public async Task UpgradeCar([FromBody] CarUpgradeRequest request) { _logger.LogInformation("Upgrading car {CarId} for {SynergyId}: {Upgrade}", request.CarId, request.SynergyId, request.UpgradeType); var user = await _context.Users .Include(u => u.OwnedCars) .FirstOrDefaultAsync(u => u.SynergyId == request.SynergyId); if (user == null) { return NotFound(new { error = "User not found" }); } var ownedCar = user.OwnedCars.FirstOrDefault(c => c.CarId == request.CarId); if (ownedCar == null) { return BadRequest(new { error = "Car not owned" }); } // Get upgrade cost var upgrade = await _context.CarUpgrades .FirstOrDefaultAsync(u => u.CarId == request.CarId && u.UpgradeType == request.UpgradeType); if (upgrade == null) { return NotFound(new { error = "Upgrade not found" }); } // Check if already purchased var purchasedUpgrades = ownedCar.PurchasedUpgrades?.Split(',') ?? Array.Empty(); if (purchasedUpgrades.Contains(request.UpgradeType)) { return BadRequest(new { error = "Upgrade already purchased" }); } // Check currency if ((user.Cash ?? 0) < upgrade.CashCost) { return BadRequest(new { error = "Insufficient cash" }); } // Apply upgrade user.Cash -= upgrade.CashCost; ownedCar.UpgradeLevel++; ownedCar.PerformanceRating += upgrade.PerformanceIncrease; var newUpgrades = purchasedUpgrades.Append(request.UpgradeType).ToArray(); ownedCar.PurchasedUpgrades = string.Join(",", newUpgrades); await _context.SaveChangesAsync(); return Ok(new { success = true, upgradeType = request.UpgradeType, cashSpent = upgrade.CashCost, newPerformanceRating = ownedCar.PerformanceRating, newUpgradeLevel = ownedCar.UpgradeLevel, remainingCash = user.Cash }); } /// /// Complete a career event /// [HttpPost("career/complete")] public async Task CompleteCareerEvent([FromBody] CareerEventCompletion completion) { _logger.LogInformation("Completing career event {Event} for {SynergyId}", completion.EventName, completion.SynergyId); var user = await _context.Users .Include(u => u.CareerProgress) .FirstOrDefaultAsync(u => u.SynergyId == completion.SynergyId); if (user == null) { return NotFound(new { error = "User not found" }); } // Find or create progress entry var progress = user.CareerProgress.FirstOrDefault(cp => cp.SeriesName == completion.SeriesName && cp.EventName == completion.EventName); if (progress == null) { progress = new CareerProgress { UserId = user.Id, SeriesName = completion.SeriesName, EventName = completion.EventName, Completed = false, StarsEarned = 0 }; _context.CareerProgress.Add(progress); } // Update progress progress.Completed = true; progress.StarsEarned = Math.Max(progress.StarsEarned, completion.StarsEarned); progress.BestTime = progress.BestTime == 0 ? completion.RaceTime : Math.Min(progress.BestTime, completion.RaceTime); progress.CompletedAt = DateTime.UtcNow; // Award rewards int goldReward = completion.StarsEarned * 10; // 10 gold per star int cashReward = completion.StarsEarned * 2000; // 2000 cash per star int xpReward = completion.StarsEarned * 100; // 100 XP per star user.Gold = (user.Gold ?? 0) + goldReward; user.Cash = (user.Cash ?? 0) + cashReward; user.Experience = (user.Experience ?? 0) + xpReward; await _context.SaveChangesAsync(); return Ok(new { success = true, stars = completion.StarsEarned, goldEarned = goldReward, cashEarned = cashReward, xpEarned = xpReward, bestTime = progress.BestTime, totalGold = user.Gold, totalCash = user.Cash, totalExperience = user.Experience }); } }