diff --git a/Controllers/PlayerController.cs b/Controllers/PlayerController.cs index 0ac3f66..1b5bc33 100644 --- a/Controllers/PlayerController.cs +++ b/Controllers/PlayerController.cs @@ -275,6 +275,53 @@ public class PlayerController : ControllerBase return Ok($"Player '{player.Name}' moved {direction} to '{targetLocation.X}, {targetLocation.Y}'."); } + [HttpPost("action")] + [Authorize(Roles = "USER,SUPER")] + public async Task 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) + return Ok(outcome.Result); + } + + return Ok("Nothing happens."); + } + + return Ok("Nothing happens."); + } + private static readonly Dictionary DirectionData = new() { { "N", (0, 1, "S") }, { "NE", (1, 1, "SW") }, { "E", (1, 0, "W") }, { "SE", (1, -1, "NW") }, diff --git a/GameAuthApi.Tests/PlayerControllerTests.cs b/GameAuthApi.Tests/PlayerControllerTests.cs index d8e8798..9fc342c 100644 --- a/GameAuthApi.Tests/PlayerControllerTests.cs +++ b/GameAuthApi.Tests/PlayerControllerTests.cs @@ -90,4 +90,49 @@ public class PlayerControllerTests Assert.IsType(result); } + + [Fact] + public async Task PerformAction_PlayerNotFound() + { + var svc = new Mock(cfg); + svc.Setup(p => p.GetByUserIdAsync("u")).ReturnsAsync((Player?)null); + var controller = new PlayerController(svc.Object, new Mock(cfg).Object, new Mock(cfg).Object, new Mock(cfg).Object) + { + ControllerContext = ContextWithUser("u") + }; + + var result = await controller.PerformAction(new PerformActionRequest { Action = "gather" }); + + Assert.IsType(result); + } + + [Fact] + public async Task PerformAction_ReturnsOutcome() + { + var playerSvc = new Mock(cfg); + var player = new Player { Id = "p", UserId = "u", LocationId = "loc", InventoryLocationId = "inv" }; + playerSvc.Setup(p => p.GetByUserIdAsync("u")).ReturnsAsync(player); + var locSvc = new Mock(cfg); + var loc = new Location { Id = "loc", Content = new List { "i" } }; + locSvc.Setup(l => l.GetByIdAsync("loc")).ReturnsAsync(loc); + var itemSvc = new Mock(cfg); + itemSvc.Setup(i => i.GetByIdAsync("i")).ReturnsAsync(new Item { Id = "i", Name = "wood" }); + var spawnSvc = new Mock(cfg); + spawnSvc.Setup(s => s.GetByNameAsync("wood")).ReturnsAsync(new Spawnable + { + Name = "wood", + CollectAction = "chop", + CollectResults = new List { new() { Result = "log", SuccessRate = 100 } } + }); + + var controller = new PlayerController(playerSvc.Object, locSvc.Object, spawnSvc.Object, itemSvc.Object) + { + ControllerContext = ContextWithUser("u") + }; + + var result = await controller.PerformAction(new PerformActionRequest { Action = "chop" }); + + var ok = Assert.IsType(result); + Assert.Equal("log", ok.Value); + } } diff --git a/Models/DTOs/PerformActionRequest.cs b/Models/DTOs/PerformActionRequest.cs new file mode 100644 index 0000000..dd35cd7 --- /dev/null +++ b/Models/DTOs/PerformActionRequest.cs @@ -0,0 +1,6 @@ +namespace GameAuthApi.Models; + +public class PerformActionRequest +{ + public string Action { get; set; } +} diff --git a/Services/SpawnableService.cs b/Services/SpawnableService.cs index 8cc4d6f..9d05c17 100644 --- a/Services/SpawnableService.cs +++ b/Services/SpawnableService.cs @@ -36,6 +36,11 @@ public class SpawnableService return await _spawnables.Find(s => s.Id == id).FirstOrDefaultAsync(); } + public virtual async Task GetByNameAsync(string name) + { + return await _spawnables.Find(s => s.Name == name).FirstOrDefaultAsync(); + } + public virtual async Task DeleteAsync(string id) { await _spawnables.DeleteOneAsync(s => s.Id == id);