Files
2025-06-09 23:29:43 -05:00

380 lines
14 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Controllers/PlayerController.cs
using GameAuthApi.Models;
using GameAuthApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace GameAuthApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class PlayerController : ControllerBase
{
private readonly PlayerService _playerService;
private readonly LocationService _locationService;
private readonly SpawnableService _spawnableService;
private readonly ItemService _itemService;
public PlayerController(PlayerService playerService, LocationService locationService, SpawnableService spawnableService, ItemService itemService)
{
_playerService = playerService;
_locationService = locationService;
_spawnableService = spawnableService;
_itemService = itemService;
}
[HttpPost]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> Create([FromBody] CreatePlayerRequest req)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (string.IsNullOrEmpty(userId))
return Unauthorized("User ID not found in token.");
var existing = await _playerService.GetByUserIdAsync(userId);
if (existing != null)
return BadRequest("Player already exists for this user.");
Location spawnLocation = null;
if (!string.IsNullOrWhiteSpace(req.LocationId))
{
spawnLocation = await _locationService.GetByIdAsync(req.LocationId);
if (spawnLocation == null)
return BadRequest("Specified location does not exist.");
}
else
{
spawnLocation = await _locationService.GetSpawnLocationAsync();
// If not found, spawnLocation remains null
}
// create the inventory location
var inventoryLocation = new Location
{
LocationType = "Inventory",
X = null,
Y = null,
Z = null,
N = null,
NE = null,
E = null,
SE = null,
S = null,
SW = null,
W = null,
NW = null,
Players = new List<string>() // optional
};
await _locationService.CreateAsync(inventoryLocation);
var player = new Player
{
UserId = userId,
Name = req.Name,
LocationId = spawnLocation?.Id,
InventoryLocationId = inventoryLocation.Id,
Climbing = 3
};
await _playerService.CreateAsync(player);
if (spawnLocation != null)
{
await _locationService.AddPlayerToLocationAsync(spawnLocation.Id, player.Id);
}
return Ok(player);
}
[HttpPut]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> Update([FromBody] UpdatePlayerRequest req)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var player = await _playerService.GetByUserIdAsync(userId);
if (player == null)
return NotFound("Player not found.");
player.Name = req.Name;
player.Climbing = req.Climbing;
await _playerService.UpdateAsync(player);
return Ok(player);
}
[HttpDelete]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> Delete()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var role = User.FindFirstValue(ClaimTypes.Role);
var player = await _playerService.GetByUserIdAsync(userId);
if (player == null)
return NotFound("No player found for this user.");
// Remove from current location if set
if (!string.IsNullOrEmpty(player.LocationId))
{
await _locationService.RemovePlayerFromLocationAsync(player.LocationId, player.Id);
}
await _playerService.DeleteAsync(player.Id);
return Ok($"Player '{player.Name}' deleted.");
}
[HttpPost("teleport")]
[Authorize(Roles = "SUPER")]
public async Task<IActionResult> AdminTeleport([FromBody] TeleportRequest req)
{
var player = await _playerService.GetByIdAsync(req.PlayerId);
if (player == null)
return NotFound("Player not found.");
var newLocation = await _locationService.GetByIdAsync(req.LocationId);
if (newLocation == null)
return BadRequest("Target location does not exist.");
if (!string.IsNullOrEmpty(player.LocationId))
{
await _locationService.RemovePlayerFromLocationAsync(player.LocationId, player.Id);
}
player.LocationId = newLocation.Id;
await _playerService.UpdateAsync(player);
await _locationService.AddPlayerToLocationAsync(newLocation.Id, player.Id);
return Ok($"Player '{player.Name}' teleported to '{newLocation.X}, {newLocation.Y}'.");
}
[HttpPost("move")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> Move([FromBody] MoveRequest req)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var player = await _playerService.GetByUserIdAsync(userId);
if (player == null)
return NotFound("Player not found.");
if (string.IsNullOrWhiteSpace(player.LocationId))
return BadRequest("Player is not currently in a location.");
var currentLocation = await _locationService.GetByIdAsync(player.LocationId);
if (currentLocation == null)
return BadRequest("Current location not found.");
if (string.IsNullOrWhiteSpace(req.Direction))
return BadRequest("Direction is required.");
var direction = req.Direction.ToUpper();
var directionMap = new Dictionary<string, (int dx, int dy, string reverse)>
{
{ "N", (0, 1, "S") }, { "NE", (1, 1, "SW") }, { "E", (1, 0, "W") }, { "SE", (1, -1, "NW") },
{ "S", (0, -1, "N") }, { "SW", (-1, -1, "NE") }, { "W", (-1, 0, "E") }, { "NW", (-1, 1, "SE") }
};
if (!directionMap.ContainsKey(direction))
return BadRequest("Invalid direction. Use N, NE, E, SE, S, SW, W, or NW.");
var (dx, dy, reverseDir) = directionMap[direction];
var targetId = typeof(Location).GetProperty(direction)?.GetValue(currentLocation)?.ToString();
Location targetLocation;
if (!string.IsNullOrWhiteSpace(targetId))
{
targetLocation = await _locationService.GetByIdAsync(targetId);
if (targetLocation == null)
return BadRequest("Linked location ID is invalid.");
var z1 = currentLocation.Z ?? 0;
var z2 = targetLocation.Z ?? 0;
var zDiff = Math.Abs(z1 - z2);
if (zDiff > player.Climbing)
{
return BadRequest($"Cannot move {req.Direction.ToUpper()}: elevation difference ({zDiff}) exceeds your climbing ability ({player.Climbing}).");
}
}
else
{
// Generate new location
var newX = currentLocation.X + dx;
var newY = currentLocation.Y + dy;
var newZ = currentLocation.Z + Random.Shared.Next(-1, 2); // -1, 0, or +1
string itemId = null;
var spawnables = await _spawnableService.GetAllAsync();
if (spawnables.Any())
{
var roll = Random.Shared.Next(0, 100); // 0–99
foreach (var s in spawnables.OrderBy(s => s.SpawnRate))
{
if (roll < s.SpawnRate)
{
var tempItem = new Item
{
Name = s.Name,
LocationId = null,
UsesRemaining = s.Uses
};
await _itemService.CreateAsync(tempItem);
itemId = tempItem.Id;
break;
}
}
}
targetLocation = new Location
{
LocationType = "World",
X = newX,
Y = newY,
Z = newZ,
Content = itemId != null ? [itemId] : []
};
await _locationService.CreateAsync(targetLocation);
if (itemId != null)
{
var createdItem = await _itemService.GetByIdAsync(itemId);
createdItem.LocationId = targetLocation.Id;
await _itemService.UpdateAsync(createdItem);
}
// Link current → new
typeof(Location).GetProperty(direction)?.SetValue(currentLocation, targetLocation.Id);
// Link new → current
typeof(Location).GetProperty(reverseDir)?.SetValue(targetLocation, currentLocation.Id);
await _locationService.UpdateAsync(currentLocation);
await _locationService.UpdateAsync(targetLocation);
// Link additional neighbors
foreach (var entry in directionMap)
{
var dir = entry.Key;
var (nx, ny, rev) = entry.Value;
var neighbor = await _locationService.GetByCoordinatesAsync(
(targetLocation.X ?? 0) + nx,
(targetLocation.Y ?? 0) + ny,
targetLocation.Z ?? 0);
if (neighbor != null)
{
typeof(Location).GetProperty(dir)?.SetValue(targetLocation, neighbor.Id);
typeof(Location).GetProperty(rev)?.SetValue(neighbor, targetLocation.Id);
await _locationService.UpdateAsync(neighbor);
}
}
await _locationService.UpdateAsync(targetLocation);
}
// Move the player
await _locationService.RemovePlayerFromLocationAsync(currentLocation.Id, player.Id);
await _locationService.AddPlayerToLocationAsync(targetLocation.Id, player.Id);
player.LocationId = targetLocation.Id;
await _playerService.UpdateAsync(player);
return Ok($"Player '{player.Name}' moved {direction} to '{targetLocation.X}, {targetLocation.Y}'.");
}
[HttpPost("action")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> PerformAction([FromBody] PerformActionRequest req)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var player = await _playerService.GetByUserIdAsync(userId);
if (player == null)
return NotFound("Player not found.");
if (string.IsNullOrWhiteSpace(player.LocationId))
return BadRequest("Player is not currently in a location.");
if (string.IsNullOrWhiteSpace(req.Action))
return BadRequest("Action is required.");
var location = await _locationService.GetByIdAsync(player.LocationId);
if (location == null)
return BadRequest("Current location not found.");
foreach (var itemId in location.Content)
{
var item = await _itemService.GetByIdAsync(itemId);
if (item == null)
continue;
var spawnable = await _spawnableService.GetByNameAsync(item.Name.ToLower());
if (spawnable == null)
continue;
if (!string.Equals(spawnable.CollectAction, req.Action, StringComparison.OrdinalIgnoreCase))
continue;
var roll = Random.Shared.Next(0, 100);
var cumulative = 0;
foreach (var outcome in spawnable.CollectResults)
{
cumulative += outcome.SuccessRate;
if (roll < cumulative)
{
if (!string.IsNullOrEmpty(player.InventoryLocationId))
{
var newItem = new Item
{
Name = outcome.Result,
LocationId = player.InventoryLocationId
};
await _itemService.CreateAsync(newItem);
await _locationService.AddItemToLocationAsync(player.InventoryLocationId, newItem.Id);
}
if (spawnable.ConsumeOnSuccess)
{
await _locationService.RemoveItemFromLocationAsync(location.Id, itemId);
await _itemService.DeleteAsync(itemId);
}
else if (item.UsesRemaining > 0)
{
item.UsesRemaining--;
if (item.UsesRemaining <= 0)
{
await _locationService.RemoveItemFromLocationAsync(location.Id, itemId);
await _itemService.DeleteAsync(itemId);
}
else
{
await _itemService.UpdateAsync(item);
}
}
return Ok(outcome.Result);
}
}
return Ok("Nothing happens.");
}
return Ok("Nothing happens.");
}
private static readonly Dictionary<string, (int dx, int dy, string reverse)> DirectionData = new()
{
{ "N", (0, 1, "S") }, { "NE", (1, 1, "SW") }, { "E", (1, 0, "W") }, { "SE", (1, -1, "NW") },
{ "S", (0, -1, "N") }, { "SW", (-1, -1, "NE") }, { "W", (-1, 0, "E") }, { "NW", (-1, 1, "SE") }
};
}