diff --git a/Server/Components/Pages/GamePage.razor b/Server/Components/Pages/GamePage.razor index b2ff905..3a861e2 100644 --- a/Server/Components/Pages/GamePage.razor +++ b/Server/Components/Pages/GamePage.razor @@ -12,11 +12,9 @@ @using Fracture.Server.Modules.MapGenerator.UI.Models @using Fracture.Server.Modules.Pathfinding.Models @using Fracture.Server.Modules.Shared -@using Fracture.Server.Modules.Shared.ImageChangers @using Fracture.Server.Modules.Users @using Fracture.Server.Modules.Users.Services @using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage -@implements IDisposable @page "/game" @rendermode @(new InteractiveServerRenderMode(false)) @@ -26,8 +24,9 @@ @inject NavigationManager NavigationManager @inject IItemsRepository ItemsRepository @inject VersionInfoProvider VersionInfoProvider -@inject BackgroundImageChanger BackgroundImageChanger @inject UserService UserService +@inject MovementService MovementService +@inject ILogger Logger @@ -68,8 +67,8 @@
-
+ style="background: url(@BackgroundImage);background-repeat: no-repeat; background-size: cover; "> +
Fracture @VersionInfoProvider.InformationalVersion
tutaj log mojej gry
@@ -132,7 +131,7 @@
- +
diff --git a/Server/Components/Pages/GamePage.razor.cs b/Server/Components/Pages/GamePage.razor.cs index 542dba3..7e08fd6 100644 --- a/Server/Components/Pages/GamePage.razor.cs +++ b/Server/Components/Pages/GamePage.razor.cs @@ -2,21 +2,20 @@ using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.UI.Models; using Fracture.Server.Modules.Pathfinding.Models; -using Fracture.Server.Modules.Shared.ImageChangers; using Fracture.Server.Modules.Users.Models; +using Fracture.Server.Modules.Users.Services; namespace Fracture.Server.Components.Pages; public partial class GamePage { private Dictionary _mapPopupParameters = null!; - public static Map Map { get; set; } private PopupContainer _popup = null!; - public static BackgroundImage BackgroundImage { get; set; } = new(string.Empty); + public string BackgroundImage { get; set; } = string.Empty; - private readonly MapDisplayData _mapDisplayData = new(); + private readonly MapDisplayOptions _mapDisplayOptions = new(); private List? Path { get; set; } @@ -28,18 +27,29 @@ protected override async Task OnInitializedAsync() NavigationManager.NavigateTo("/"); } - Map = MapManagerService.GetWorldMap() ?? throw new InvalidOperationException(); - _mapDisplayData.ShowColorMap = true; - _mapPopupParameters = new Dictionary + if (MovementService.CurrentMap is null) + { + MovementService.Initialize(); + BackgroundImage = GetBackgroundImagePath(); + } + + MovementService.OnMapEntered += async (sender, args) => { - { "Map", Map }, - { "MapDisplayData", _mapDisplayData }, + BackgroundImage = GetBackgroundImagePath(); + StateHasChanged(); }; - BackgroundImageChanger.BackgroundImage = BackgroundImage; - await GetBacgroundAsync(); + MovementService.OnMoved += async (sender, args) => + { + BackgroundImage = GetBackgroundImagePath(); + StateHasChanged(); + }; - BackgroundImageChanger.BackgroundImageChanged += OnBgChanged!; + _mapDisplayOptions.ShowColorMap = true; + _mapPopupParameters = new Dictionary + { + { "MapDisplayData", _mapDisplayOptions }, + }; await base.OnInitializedAsync(); } @@ -74,19 +84,90 @@ private void Logout() ProtectedSessionStore.DeleteAsync("username"); } - //this piece of codes refreshes GamePage after changing background image. It is necessary to show new background. - private async Task GetBacgroundAsync() + private string GetBackgroundImagePath() { - await BackgroundImageChanger.ChangeBackgroundImageAsync(); - } + if (MovementService.CurrentMap is null) + { + Logger.LogError("Current map is null"); + return string.Empty; + } - private void OnBgChanged(object sender, EventArgs e) - { - this.InvokeAsync(this.StateHasChanged); - } + if ( + MovementService.CurrentX < 0 + || MovementService.CurrentY < 0 + || MovementService.CurrentX >= MovementService.CurrentMap.Width + || MovementService.CurrentY >= MovementService.CurrentMap.Height + ) + { + Logger.LogError("Character is out of map"); + return string.Empty; + } + var cell = MovementService.CurrentMap.Grid[ + MovementService.CurrentX, + MovementService.CurrentY + ]; + var biome = cell.Biome; - public void Dispose() - { - BackgroundImageChanger.BackgroundImageChanged -= OnBgChanged!; + if (biome is null) + { + Logger.LogError("Biome is null"); + return string.Empty; + } + + string? imagePath = null; + if (cell.LocationType != LocationType.None) + { + var currentLocationName = cell.LocationType.ToString(); + var location = biome.Locations.FirstOrDefault(l => + string.Equals(l.Name, currentLocationName, StringComparison.OrdinalIgnoreCase) + ); + + if (location is null) + { + Logger.LogWarning( + "No matching location config for LocationType {LocationType} at ({X},{Y}) in biome {BiomeName}. Available: {Locations}", + cell.LocationType, + MovementService.CurrentX, + MovementService.CurrentY, + biome.Name, + string.Join( + ", ", + biome + .Locations.Where(l => !string.IsNullOrWhiteSpace(l.Name)) + .Select(l => l.Name) + ) + ); + } + else + { + imagePath = location.BackgroundImage; + Logger.LogDebug( + "Using location background image '{ImagePath}' for LocationType {LocationType} at ({X},{Y})", + imagePath, + cell.LocationType, + MovementService.CurrentX, + MovementService.CurrentY + ); + } + } + if (string.IsNullOrWhiteSpace(imagePath)) + { + imagePath = biome.BackgroundImage; + Logger.LogDebug( + "Using biome background image '{ImagePath}' for biome {BiomeName} at ({X},{Y})", + imagePath, + biome.Name, + MovementService.CurrentX, + MovementService.CurrentY + ); + } + + if (string.IsNullOrWhiteSpace(imagePath)) + { + Logger.LogError("Background image path is null or empty"); + return string.Empty; + } + + return imagePath; } } diff --git a/Server/Components/Popups/MapPopup.razor b/Server/Components/Popups/MapPopup.razor index b0b37c6..28223d8 100644 --- a/Server/Components/Popups/MapPopup.razor +++ b/Server/Components/Popups/MapPopup.razor @@ -1,18 +1,17 @@ @inject NavigationManager NavigationManager +@inject MovementService MovementService @page "/map" @using Fracture.Server.Modules.MapGenerator.Models.Map @using Fracture.Server.Modules.MapGenerator.UI @using Fracture.Server.Modules.MapGenerator.UI.Models +@using Fracture.Server.Modules.Users.Services +
- +
-@code { - [Parameter] public required Map Map { get; set; } - [Parameter] public required MapDisplayData MapDisplayData { get; set; } - readonly int x = MapView.CharacterXX; - readonly int y = MapView.CharacterYY; +@code { + [Parameter] public required MapDisplayOptions MapDisplayData { get; set; } } \ No newline at end of file diff --git a/Server/Modules/Items/Models/ItemStatistics.cs b/Server/Modules/Items/Models/ItemStatistics.cs index f830f9a..1875278 100644 --- a/Server/Modules/Items/Models/ItemStatistics.cs +++ b/Server/Modules/Items/Models/ItemStatistics.cs @@ -20,7 +20,7 @@ public class ItemStatistics [InverseProperty(nameof(Item.Statistics))] [JsonIgnore] - public virtual Item Item { get; set; } + public virtual required Item Item { get; set; } public int GetStatFromItemStat(ItemStat stat) { diff --git a/Server/Modules/MapGenerator/Models/Map/Biome/Biome.cs b/Server/Modules/MapGenerator/Models/Map/Biome/Biome.cs index be9f13e..e985062 100644 --- a/Server/Modules/MapGenerator/Models/Map/Biome/Biome.cs +++ b/Server/Modules/MapGenerator/Models/Map/Biome/Biome.cs @@ -9,6 +9,6 @@ public class Biome public float MinTemperature { get; set; } public float MaxTemperature { get; set; } public bool Walkable { get; set; } = true; - public string BackgroundImage { get; set; } + public string? BackgroundImage { get; set; } public List Locations { get; set; } = new(); } diff --git a/Server/Modules/MapGenerator/Models/Map/Biome/BiomeCategory.cs b/Server/Modules/MapGenerator/Models/Map/Biome/BiomeCategory.cs index 8d8f0af..b60dd19 100644 --- a/Server/Modules/MapGenerator/Models/Map/Biome/BiomeCategory.cs +++ b/Server/Modules/MapGenerator/Models/Map/Biome/BiomeCategory.cs @@ -4,14 +4,13 @@ namespace Fracture.Server.Modules.MapGenerator.Models.Map.Biome; public class BiomeCategory { - public string BiomeType { get; set; } - public string TerrainType { get; set; } + public required string BiomeType { get; set; } + public required string TerrainType { get; set; } - public string BackgroundImage { get; set; } + public string? BackgroundImage { get; set; } public float MaxHeight { get; set; } public float MinHeight { get; set; } - [Required] - public List Biomes { get; set; } + public required List Biomes { get; set; } = []; } diff --git a/Server/Modules/MapGenerator/Models/Map/Biome/Location.cs b/Server/Modules/MapGenerator/Models/Map/Biome/Location.cs index 70c1e10..f113142 100644 --- a/Server/Modules/MapGenerator/Models/Map/Biome/Location.cs +++ b/Server/Modules/MapGenerator/Models/Map/Biome/Location.cs @@ -2,8 +2,8 @@ public class Location { - public string? Name { get; set; } // name of the location what you want to generate for example town + public string? Name { get; set; } // name of the location what you want to generate, for example: town public int Weight { get; set; } public float Mult { get; set; } - public string BackgroundImage { get; set; } + public string? BackgroundImage { get; set; } } diff --git a/Server/Modules/MapGenerator/Models/Map/Map.cs b/Server/Modules/MapGenerator/Models/Map/Map.cs index 81d5f87..e2bf9c7 100644 --- a/Server/Modules/MapGenerator/Models/Map/Map.cs +++ b/Server/Modules/MapGenerator/Models/Map/Map.cs @@ -4,11 +4,24 @@ public class Map { public string? Name { get; set; } - public LocationType LocationType { get; set; } + public LocationType LocationType { get; init; } - public List LocationGroups { get; set; } = new(); + public List LocationGroups { get; init; } = []; - public int Width { get; set; } - public int Height { get; set; } - public Node[,] Grid { get; set; } + public int Width { get; init; } + public int Height { get; init; } + public required Node[,] Grid { get; init; } + + public Position GetRandomWalkableNode() + { + var rnd = new Random(); + + Position node; + do + { + node = new Position() { X = rnd.Next(0, Width), Y = rnd.Next(0, Height) }; + } while (!Grid[node.X, node.Y].Walkable); + + return node; + } } diff --git a/Server/Modules/MapGenerator/Models/Map/MapObjects/IMapObject.cs b/Server/Modules/MapGenerator/Models/Map/MapObjects/IMapObject.cs index 5dd7e1a..590351a 100644 --- a/Server/Modules/MapGenerator/Models/Map/MapObjects/IMapObject.cs +++ b/Server/Modules/MapGenerator/Models/Map/MapObjects/IMapObject.cs @@ -2,6 +2,6 @@ public interface IMapObject { - string Type { get; set; } + string Id { get; set; } bool IsInteractive { get; set; } } diff --git a/Server/Modules/MapGenerator/Models/Map/MapObjects/LocationMapObject.cs b/Server/Modules/MapGenerator/Models/Map/MapObjects/LocationMapObject.cs index f1ea1e7..6687084 100644 --- a/Server/Modules/MapGenerator/Models/Map/MapObjects/LocationMapObject.cs +++ b/Server/Modules/MapGenerator/Models/Map/MapObjects/LocationMapObject.cs @@ -3,7 +3,8 @@ public class LocationMapObject : IMapObject { public Map? SubMap { get; set; } - public string Type { get; set; } = "Location"; + + public string Id { get; set; } = "Location"; public bool IsInteractive { get; set; } } diff --git a/Server/Modules/MapGenerator/Models/Map/MapObjects/NPCMapObject.cs b/Server/Modules/MapGenerator/Models/Map/MapObjects/NPCMapObject.cs index c240296..ac42be8 100644 --- a/Server/Modules/MapGenerator/Models/Map/MapObjects/NPCMapObject.cs +++ b/Server/Modules/MapGenerator/Models/Map/MapObjects/NPCMapObject.cs @@ -2,6 +2,6 @@ public class NpcMapObject : IMapObject { - public string Type { get; set; } + public required string Id { get; set; } public bool IsInteractive { get; set; } } diff --git a/Server/Modules/MapGenerator/Models/Map/Node.cs b/Server/Modules/MapGenerator/Models/Map/Node.cs index 87168e4..45638b1 100644 --- a/Server/Modules/MapGenerator/Models/Map/Node.cs +++ b/Server/Modules/MapGenerator/Models/Map/Node.cs @@ -1,38 +1,24 @@ -using System.ComponentModel.DataAnnotations; -using Fracture.Server.Modules.MapGenerator.Models.Map.Biome; -using Fracture.Server.Modules.MapGenerator.Models.Map.MapObjects; +using Fracture.Server.Modules.MapGenerator.Models.Map.MapObjects; using Fracture.Server.Modules.Pathfinding.Models; namespace Fracture.Server.Modules.MapGenerator.Models.Map; public class Node : IPathfindingNode { - public Node(int xId, int yId, Biome.Biome biome) - { - XId = xId; - YId = yId; - Biome = biome; - } + public required Biome.Biome Biome { get; set; } - [Required] - public Biome.Biome Biome { get; set; } + public required string TerrainType { get; set; } - [Required] - public string TerrainType { get; set; } - - [Required] public float NoiseValue { get; set; } - public string GroupName { get; set; } + public string? GroupName { get; set; } public LocationType LocationType { get; set; } = LocationType.None; - public IMapObject MapObject { get; set; } + public IMapObject? MapObject { get; set; } - [Required] - public int XId { get; set; } + public required int XId { get; set; } - [Required] - public int YId { get; set; } + public required int YId { get; set; } public int GCost { get; set; } public int HCost { get; set; } diff --git a/Server/Modules/MapGenerator/Models/Map/Position.cs b/Server/Modules/MapGenerator/Models/Map/Position.cs new file mode 100644 index 0000000..88ad463 --- /dev/null +++ b/Server/Modules/MapGenerator/Models/Map/Position.cs @@ -0,0 +1,7 @@ +namespace Fracture.Server.Modules.MapGenerator.Models.Map; + +public record struct Position(int X, int Y) +{ + public int X { get; init; } = X; + public int Y { get; init; } = Y; +} diff --git a/Server/Modules/MapGenerator/Models/Map/Town/LocationParameters.cs b/Server/Modules/MapGenerator/Models/Map/Town/LocationParameters.cs index d2ff07b..adb3e68 100644 --- a/Server/Modules/MapGenerator/Models/Map/Town/LocationParameters.cs +++ b/Server/Modules/MapGenerator/Models/Map/Town/LocationParameters.cs @@ -1,7 +1,4 @@ -using System.Text.Json; -using Fracture.Server.Modules.MapGenerator.Models.Map.Biome; - -namespace Fracture.Server.Modules.MapGenerator.Models.Map.Town; +namespace Fracture.Server.Modules.MapGenerator.Models.Map.Town; public class LocationParameters { diff --git a/Server/Modules/MapGenerator/Services/LocationGroupGeneratorService.cs b/Server/Modules/MapGenerator/Services/LocationGroupGeneratorService.cs index 74a61b9..907999c 100644 --- a/Server/Modules/MapGenerator/Services/LocationGroupGeneratorService.cs +++ b/Server/Modules/MapGenerator/Services/LocationGroupGeneratorService.cs @@ -1,27 +1,18 @@ using Fracture.Server.Modules.FloodFill; using Fracture.Server.Modules.MapGenerator.Models.Map; using Fracture.Server.Modules.MapGenerator.Services.TownGen; +using Fracture.Server.Modules.Shared; namespace Fracture.Server.Modules.MapGenerator.Services; -public class LocationGroupGeneratorService : ILocationGroupGeneratorService +public class LocationGroupGeneratorService( + ILocationWeightGeneratorService weightGen, + ILocationGeneratorService locationGen, + IFloodFillService floodFill +) : ILocationGroupGeneratorService { - private readonly ILocationWeightGeneratorService _weightGen; - private readonly ILocationGeneratorService _locationGen; - private readonly IFloodFillService _floodFill; private readonly Random _rnd = new(); - public LocationGroupGeneratorService( - ILocationWeightGeneratorService weightGen, - ILocationGeneratorService locationGen, - IFloodFillService floodFill - ) - { - _weightGen = weightGen; - _locationGen = locationGen; - _floodFill = floodFill; - } - public List GenerateGroups( Node[,] grid, int width, @@ -30,10 +21,10 @@ LocationType type ) { var count = _rnd.Next(5, 9); - var weights = _weightGen.GenerateWeights(grid, height, width); - var updated = _locationGen.Generate(grid, weights, height, width, _rnd, count, type); + var weights = weightGen.GenerateWeights(grid, height, width); + var updated = locationGen.Generate(grid, weights, height, width, _rnd, count, type); - var groups = _floodFill.FindGroups( + var groups = floodFill.FindGroups( updated, n => n.LocationType == type, (a, b) => a.LocationType == b.LocationType, diff --git a/Server/Modules/MapGenerator/Services/MapGeneratorService.cs b/Server/Modules/MapGenerator/Services/MapGeneratorService.cs index 687ecc3..bb3fcde 100644 --- a/Server/Modules/MapGenerator/Services/MapGeneratorService.cs +++ b/Server/Modules/MapGenerator/Services/MapGeneratorService.cs @@ -19,9 +19,9 @@ public async Task GetMap(MapParameters? mapParameters) return await Task.FromResult(GenerateMap(mapParameters)); } - private Node[,] GenerateGrid(MapParameters? mapParameters) + private Node[,] GenerateGrid(MapParameters mapParameters) { - var noiseParameters = mapParameters?.NoiseParameters; + var noiseParameters = mapParameters.NoiseParameters; noiseParameters.Seed = noiseParameters.UseRandomSeed ? _rnd.Next(-100000, 100000) : noiseParameters.Seed; @@ -111,27 +111,42 @@ public async Task GetMap(MapParameters? mapParameters) ) ); - Biome biome = null; + Biome? biome = null; if (biomeCategory != null) { biome = biomeCategory.Biomes.FirstOrDefault(sb => temperatureMap[x, y] >= sb.MinTemperature && temperatureMap[x, y] < sb.MaxTemperature )!; + if (biome == null) + { _logger.LogError( string.Format( - "No biome found for temperature {0} at ({1}, {2}) within category {3}", + "No biome found for temperature {0} at ({1}, {2}) within category {3}: using default", temperatureMap[x, y], x, y, biomeCategory.TerrainType ) ); + + biome = biomeCategory.Biomes.FirstOrDefault()!; + ArgumentNullException.ThrowIfNull( + biome, + string.Format( + "Biome category {0} has no biomes defined.", + biomeCategory.TerrainType + ) + ); + } } - grid[x, y] = new Node(x, y, biome) + grid[x, y] = new Node() { + XId = x, + YId = y, + Biome = biome!, NoiseValue = heightMap[x, y], Walkable = biome?.Walkable == true, TerrainType = biomeCategory!.TerrainType, diff --git a/Server/Modules/MapGenerator/Services/MapManagerService.cs b/Server/Modules/MapGenerator/Services/MapManagerService.cs index 83c5549..3282291 100644 --- a/Server/Modules/MapGenerator/Services/MapManagerService.cs +++ b/Server/Modules/MapGenerator/Services/MapManagerService.cs @@ -3,26 +3,13 @@ namespace Fracture.Server.Modules.MapGenerator.Services; -public class MapManagerService +public class MapManagerService( + IMapRepository mapRepository, + ILogger logger, + IWorldGenerationService worldGenerationService +) { - private readonly IRecurringJobManager _jobManager; - private readonly IServiceScopeFactory _scopeFactory; - private readonly object _lock = new(); - private readonly ILogger _logger; - private readonly IMapRepository _mapRepository; - - public MapManagerService( - IServiceScopeFactory scopeFactory, - IRecurringJobManager jobManager, - IMapRepository mapRepository, - ILogger logger - ) - { - _scopeFactory = scopeFactory; - _jobManager = jobManager; - _mapRepository = mapRepository; - _logger = logger; - } + private readonly Lock _lock = new(); public Map CurrentMap { get; private set; } = default!; @@ -42,40 +29,24 @@ public void SetWorldMap(Map map) } } - public async Task InitializeAndScheduleMapsAsync() + public async Task GenerateWorldAsync() { - using var scope = _scopeFactory.CreateScope(); - var worldGenerationService = - scope.ServiceProvider.GetRequiredService(); - - _mapRepository.ClearMaps(); + mapRepository.ClearMaps(); var map = await worldGenerationService.GenerateWorldMapAsync(); - _mapRepository.SaveMap(map); + mapRepository.SaveMap(map); SetWorldMap(map); - _jobManager.AddOrUpdate( - "cykliczny-import-map", - service => service.RefreshMapsAndSetNewAsync(), - Cron.Minutely - ); - - _logger.LogInformation( - "Mapy zostały zainicjalizowane, a zadanie cykliczne zarejestrowane." - ); + logger.LogInformation("Maps initialized."); } public async Task RefreshMapsAndSetNewAsync() { - using var scope = _scopeFactory.CreateScope(); - var worldGenerationService = - scope.ServiceProvider.GetRequiredService(); - - _logger.LogInformation("Rozpoczynanie generowania nowej mapy..."); + logger.LogInformation("Generating new world..."); var newMap = await worldGenerationService.GenerateWorldMapAsync(); - _mapRepository.SaveMap(newMap); + mapRepository.SaveMap(newMap); SetWorldMap(newMap); - _logger.LogInformation($"Nowa mapa ustawiona: {newMap.Name}"); + logger.LogInformation($"New map set: {newMap.Name}"); } } diff --git a/Server/Modules/MapGenerator/Services/TownGen/LocationBiomeWeightGenService.cs b/Server/Modules/MapGenerator/Services/TownGen/LocationBiomeWeightGenService.cs index 541882f..bbba266 100644 --- a/Server/Modules/MapGenerator/Services/TownGen/LocationBiomeWeightGenService.cs +++ b/Server/Modules/MapGenerator/Services/TownGen/LocationBiomeWeightGenService.cs @@ -6,7 +6,7 @@ namespace Fracture.Server.Modules.MapGenerator.Services.TownGen; public class LocationBiomeWeightGenService : ILocationWeightGeneratorService { private readonly ILogger _logger; - private LocationParameters _locationParameters; + private LocationParameters? _locationParameters; public LocationBiomeWeightGenService(ILogger logger) { @@ -64,14 +64,14 @@ private int CalculateWeight(Node[,] nodes, int height, int width, int x, int y) private int GetWeight(Node node) { - var biomeParams = _locationParameters.Get(node.Biome.Name); + var biomeParams = _locationParameters!.Get(node.Biome.Name); return biomeParams.Weight; } // Self multipliers to make a mountain city surrounded mountains or hills less likely private float GetMultiplier(Node node) { - var biomeParams = _locationParameters.Get(node.Biome.Name); + var biomeParams = _locationParameters!.Get(node.Biome.Name); return biomeParams.Mult; } } diff --git a/Server/Modules/MapGenerator/Services/WorldGenerationService.cs b/Server/Modules/MapGenerator/Services/WorldGenerationService.cs index 5e4bd4d..6f22b7e 100644 --- a/Server/Modules/MapGenerator/Services/WorldGenerationService.cs +++ b/Server/Modules/MapGenerator/Services/WorldGenerationService.cs @@ -1,39 +1,44 @@ -using Fracture.Server.Modules.FloodFill; -using Fracture.Server.Modules.MapGenerator.Models.Map; -using Fracture.Server.Modules.MapGenerator.Models.Map.Biome; -using Fracture.Server.Modules.MapGenerator.Models.Map.MapObjects; -using Fracture.Server.Modules.MapGenerator.Models.Map.Town; -using Fracture.Server.Modules.MapGenerator.Services.TownGen; +using Fracture.Server.Modules.MapGenerator.Models.Map; +using Fracture.Server.Modules.Shared; namespace Fracture.Server.Modules.MapGenerator.Services; -public class WorldGenerationService : IWorldGenerationService +public class WorldGenerationService( + IMapParameterSelectorService parameterSelector, + IMapGeneratorService mapGenerator, + ISubMapAssignmentService subMapAssigner, + INameGenerator nameGenerator, + ILogger logger +) : IWorldGenerationService { - private readonly IMapParameterSelectorService _parameterSelector; - private readonly IMapGeneratorService _mapGenerator; - private readonly ISubMapAssignmentService _subMapAssigner; - private readonly ILogger _logger; - - public WorldGenerationService( - IMapParameterSelectorService parameterSelector, - IMapGeneratorService mapGenerator, - ISubMapAssignmentService subMapAssigner, - ILogger logger - ) - { - _parameterSelector = parameterSelector; - _mapGenerator = mapGenerator; - _subMapAssigner = subMapAssigner; - _logger = logger; - } - public async Task GenerateWorldMapAsync() { - var parameters = await _parameterSelector.GetParametersAsync(); - var mainParameter = _parameterSelector.SelectRandom(parameters, LocationType.MainLocation); - var map = await _mapGenerator.GetMap(mainParameter); + logger.LogInformation("Generating main world..."); + var parameters = await parameterSelector.GetParametersAsync(); + + // generate main map + var mainParameter = parameterSelector.SelectRandom(parameters, LocationType.MainLocation); + var map = await mapGenerator.GetMap(mainParameter); + + // generate submaps + await subMapAssigner.AssignSubMapsAsync(map, parameters); + + // rename locations + foreach (var item in map.LocationGroups) + { + item.GroupName = item.LocationType switch + { + LocationType.MainLocation => "The World", + LocationType.Town => "Town " + await nameGenerator.GenerateNameAsync(), + LocationType.Cave => "Cave " + await nameGenerator.GenerateNameAsync(), + _ => item.GroupName, + }; - await _subMapAssigner.AssignSubMapsAsync(map, parameters); + foreach (var node in item.Nodes) + { + node.GroupName = item.GroupName; + } + } return map; } diff --git a/Server/Modules/MapGenerator/UI/MapView.razor b/Server/Modules/MapGenerator/UI/MapView.razor index 3a646b2..3107414 100644 --- a/Server/Modules/MapGenerator/UI/MapView.razor +++ b/Server/Modules/MapGenerator/UI/MapView.razor @@ -2,181 +2,190 @@ @using Fracture.Server.Modules.MapGenerator.Models.Map @using Fracture.Server.Modules.MapGenerator.UI.Models @using Fracture.Server.Modules.Pathfinding.Models -@using Fracture.Server.Modules.Shared.ImageChangers +@using Fracture.Server.Modules.Users.Services @using Microsoft.AspNetCore.Components.Web -@inject BackgroundImageChanger BackgroundImageChanger + +@inject MovementService MovementService + +
- @if (Map != null && MapDisplayData != null) { - - @for (var y = 0; y < Map.Grid.GetLength(1); y++) { - - @for (var x = 0; x < Map.Grid.GetLength(0); x++) { - var node = Map.Grid[x, y]; - - @if (x == CharacterXX && y == CharacterYY) { - - } - else if (MapDisplayData.TileInformationDisplay == TileInformationDisplay.Position) { - - x:@x
y:@y -
- } - else if (MapDisplayData.TileInformationDisplay == TileInformationDisplay.Noise) { - - @Math.Round(node.NoiseValue, 2) - - } - else if (MapDisplayData.TileInformationDisplay == TileInformationDisplay.None) { - - @RenderLocation(node) - - } - else if (MapDisplayData.TileInformationDisplay == TileInformationDisplay.Path) { - - @GetPathForTile(node) - - } - else { - - @RenderLocation(node) - - } - } - - } -
- 😐 -
- } + @if (Map != null && MapDisplayOptions != null) + { + + @for (var y = 0; y < Map.Grid.GetLength(1); y++) + { + + @for (var x = 0; x < Map.Grid.GetLength(0); x++) + { + var node = Map.Grid[x, y]; + + @if (MapDisplayOptions.ShowPlayer && x == CharacterX && y == CharacterY) + { + + } + else if (MapDisplayOptions.TileInformationDisplay == TileInformationDisplay.Position) + { + + x:@x
y:@y +
+ } + else if (MapDisplayOptions.TileInformationDisplay == TileInformationDisplay.Noise) + { + + @Math.Round(node.NoiseValue, 2) + + } + else if (MapDisplayOptions.TileInformationDisplay == TileInformationDisplay.None) + { + + @RenderLocation(node) + + } + else if (MapDisplayOptions.TileInformationDisplay == TileInformationDisplay.Path) + { + + @GetPathForTile(node) + + } + else + { + + @RenderLocation(node) + + } + } + + } +
+ @SpecialChars.Icons.Player +
+ }
@code { - [Parameter] public Map? Map { get; set; } - - [Parameter] public MapDisplayData? MapDisplayData { get; set; } - - [Parameter] public List? Path { get; set; } - - [Parameter] public bool IsMiniMap { get; set; } = true; - - - private bool isMoving = false; - public static int CharacterXX { get; set; } = 16; - public static int CharacterYY { get; set; } = 16; - [Parameter] public int CharacterX { get; set; } = CharacterXX; - [Parameter] public int CharacterY { get; set; } = CharacterXX; - - private readonly HashSet _blockingColors = new() { - "#21618C", "#2E86C1" - }; - - private string GetTileColor(Node node) { - if (MapDisplayData!.ShowColorMap) { - var color = node.Biome.Color; - var hex = $"#{color.R:X2}{color.G:X2}{color.B:X2}"; // rgb -> hex - return hex ?? string.Empty; - } - - var hexValue = string.Format("{0:x}", (int)(node.NoiseValue * 255)); - if (hexValue.Length == 1) { - hexValue = "0" + hexValue; - } - - return $"#{hexValue}{hexValue}{hexValue}"; - } - - private string GetPathForTile(Node node) { - if (Path == null) { - return ""; - } - - var pathNodeIndex = Path.FindIndex(n => n.XId == node.XId && n.YId == node.YId); - if (pathNodeIndex != -1 && pathNodeIndex + 1 < Path.Count) { - var xDirection = Path[pathNodeIndex + 1].XId - Path[pathNodeIndex].XId; - var yDirection = Path[pathNodeIndex].YId - Path[pathNodeIndex + 1].YId; - return GetArrowForDirection(xDirection, yDirection); - } - - return ""; - } - - protected override void OnInitialized() { - BackgroundImageChanger.BackgroundImage = GamePage.BackgroundImage; - } - - private string GetArrowForDirection(int xDirection, int yDirection) { - return yDirection switch { - 1 when xDirection == 0 => SpecialChars.Arrows.Up, - 1 when xDirection == 1 => SpecialChars.Arrows.UpRight, - 0 when xDirection == 1 => SpecialChars.Arrows.Right, - -1 when xDirection == 1 => SpecialChars.Arrows.DownRight, - -1 when xDirection == 0 => SpecialChars.Arrows.Down, - -1 when xDirection == -1 => SpecialChars.Arrows.DownLeft, - 0 when xDirection == -1 => SpecialChars.Arrows.Left, - 1 when xDirection == -1 => SpecialChars.Arrows.UpLeft, - _ => "" - }; - } - - private string RenderLocation(Node node) { - return node.LocationType switch { - LocationType.None => string.Empty, - LocationType.Town => "T", - LocationType.Cave => "C", - _ => string.Empty - }; - } - - private async void HandleKeyDown(KeyboardEventArgs e) { - if (IsMiniMap == false) { - if (isMoving) return; - isMoving = true; - var newX = CharacterXX; - var newY = CharacterYY; - switch (e.Key) { - case "ArrowUp": - if (CharacterYY > 0) { - newY--; - } - - break; - case "ArrowDown": - if (CharacterYY < Map.Grid.GetLength(1) - 1) { - newY++; - } - - break; - case "ArrowLeft": - if (CharacterXX > 0) { - newX--; - } - - break; - case "ArrowRight": - if (CharacterXX < Map.Grid.GetLength(0) - 1) { - newX++; - } - - break; - } - - if (CanMoveToTile(newX, newY)) { - CharacterXX = newX; - CharacterYY = newY; - await BackgroundImageChanger.ChangeBackgroundImageAsync(); - StateHasChanged(); - } - - Task.Delay(100).ContinueWith(_ => { isMoving = false; }); - } - } - - private bool CanMoveToTile(int x, int y) { - if (Map != null && Map.Grid[x, y].Walkable) { - return true; - } - - return false; - } + [Parameter] public Map? Map { get; set; } + + [Parameter] public MapDisplayOptions? MapDisplayOptions { get; set; } + + [Parameter] public List? Path { get; set; } + + [Parameter] public bool IsMiniMap { get; set; } = true; + + [Parameter] public int CharacterX { get; set; } + [Parameter] public int CharacterY { get; set; } + + + private bool isMoving = false; + + private readonly HashSet _blockingColors = new() { + "#21618C", "#2E86C1" + }; + + private string GetTileColor(Node node) + { + if (MapDisplayOptions!.ShowColorMap) + { + var color = node.Biome.Color; + var hex = $"#{color.R:X2}{color.G:X2}{color.B:X2}"; // rgb -> hex + return hex ?? string.Empty; + } + + var hexValue = string.Format("{0:x}", (int)(node.NoiseValue * 255)); + if (hexValue.Length == 1) + { + hexValue = "0" + hexValue; + } + + return $"#{hexValue}{hexValue}{hexValue}"; + } + + private string GetPathForTile(Node node) + { + if (Path == null) + { + return ""; + } + + var pathNodeIndex = Path.FindIndex(n => n.XId == node.XId && n.YId == node.YId); + if (pathNodeIndex != -1 && pathNodeIndex + 1 < Path.Count) + { + var xDirection = Path[pathNodeIndex + 1].XId - Path[pathNodeIndex].XId; + var yDirection = Path[pathNodeIndex].YId - Path[pathNodeIndex + 1].YId; + return GetArrowForDirection(xDirection, yDirection); + } + + return ""; + } + + protected override void OnInitialized() + { + CharacterX = MovementService.CurrentX; + CharacterY = MovementService.CurrentY; + } + + private string GetArrowForDirection(int xDirection, int yDirection) + { + return yDirection switch + { + 1 when xDirection == 0 => SpecialChars.Arrows.Up, + 1 when xDirection == 1 => SpecialChars.Arrows.UpRight, + 0 when xDirection == 1 => SpecialChars.Arrows.Right, + -1 when xDirection == 1 => SpecialChars.Arrows.DownRight, + -1 when xDirection == 0 => SpecialChars.Arrows.Down, + -1 when xDirection == -1 => SpecialChars.Arrows.DownLeft, + 0 when xDirection == -1 => SpecialChars.Arrows.Left, + 1 when xDirection == -1 => SpecialChars.Arrows.UpLeft, + _ => "" + }; + } + + private string RenderLocation(Node node) + { + return node.LocationType switch + { + LocationType.None => string.Empty, + LocationType.Town => "T", + LocationType.Cave => "C", + _ => string.Empty + }; + } + + private async void HandleKeyDown(KeyboardEventArgs e) + { + if (IsMiniMap == false) + { + if (isMoving) return; + isMoving = true; + + var newX = MovementService.CurrentX; + var newY = MovementService.CurrentY; + switch (e.Key) + { + case "ArrowUp": + newY--; + break; + case "ArrowDown": + newY++; + break; + case "ArrowLeft": + newX--; + break; + case "ArrowRight": + newX++; + break; + } + + if (MovementService.CanMove(newX, newY)) + { + await MovementService.MoveAsync(newX, newY); + CharacterX = newX; + CharacterY = newY; + + StateHasChanged(); + } + + await Task.Delay(100).ContinueWith(_ => { isMoving = false; }); + } + } } diff --git a/Server/Modules/MapGenerator/UI/Models/MapDisplayData.cs b/Server/Modules/MapGenerator/UI/Models/MapDisplayOptions.cs similarity index 70% rename from Server/Modules/MapGenerator/UI/Models/MapDisplayData.cs rename to Server/Modules/MapGenerator/UI/Models/MapDisplayOptions.cs index c5ab102..10a185b 100644 --- a/Server/Modules/MapGenerator/UI/Models/MapDisplayData.cs +++ b/Server/Modules/MapGenerator/UI/Models/MapDisplayOptions.cs @@ -1,7 +1,8 @@ namespace Fracture.Server.Modules.MapGenerator.UI.Models; -public class MapDisplayData +public class MapDisplayOptions { public bool ShowColorMap { get; set; } = true; + public bool ShowPlayer { get; set; } = true; public TileInformationDisplay TileInformationDisplay { get; set; } } diff --git a/Server/Modules/MapGenerator/UI/Models/SpecialChars.cs b/Server/Modules/MapGenerator/UI/Models/SpecialChars.cs index 2be6fa9..0e22eaa 100644 --- a/Server/Modules/MapGenerator/UI/Models/SpecialChars.cs +++ b/Server/Modules/MapGenerator/UI/Models/SpecialChars.cs @@ -13,4 +13,9 @@ public static class Arrows public const string Left = "\U00002190"; public const string UpLeft = "\U00002196"; } + + public static class Icons + { + public const string Player = "😐"; + } } diff --git a/Server/Modules/MapGenerator/UI/NodeInfo.razor b/Server/Modules/MapGenerator/UI/NodeInfo.razor index 4aa57ab..45f8fe9 100644 --- a/Server/Modules/MapGenerator/UI/NodeInfo.razor +++ b/Server/Modules/MapGenerator/UI/NodeInfo.razor @@ -1,12 +1,17 @@ @using Fracture.Server.Modules.MapGenerator.Models.Map +
- Biome: @Node?.Biome.Name - Location: @(string.IsNullOrWhiteSpace(Node?.GroupName) - ? Node.LocationType - : Node.GroupName) + Biome: @Node.Biome.Name + @if (!string.IsNullOrWhiteSpace(Node.GroupName)) + { + Location: @Node.GroupName + } + @if (!Node.Walkable) + { + Not walkable! + }
@code { - [Parameter] public Node? Node { get; set; } - + [Parameter] public required Node Node { get; set; } } \ No newline at end of file diff --git a/Server/Modules/MapGenerator/UI/NoiseParametersView.razor b/Server/Modules/MapGenerator/UI/NoiseParametersView.razor index f7a7696..d5551ac 100644 --- a/Server/Modules/MapGenerator/UI/NoiseParametersView.razor +++ b/Server/Modules/MapGenerator/UI/NoiseParametersView.razor @@ -13,7 +13,7 @@
@code { - [Parameter] public NoiseParameters NoiseParameters { get; set; } + [Parameter] public required NoiseParameters NoiseParameters { get; set; } [Parameter] public EventCallback NoiseParametersChanged { get; set; } private Task OnNoiseParametersChanged(ChangeEventArgs e) { diff --git a/Server/Modules/Pathfinding/Services/PathfindingService.cs b/Server/Modules/Pathfinding/Services/PathfindingService.cs index 3655ea4..b55a367 100644 --- a/Server/Modules/Pathfinding/Services/PathfindingService.cs +++ b/Server/Modules/Pathfinding/Services/PathfindingService.cs @@ -80,7 +80,7 @@ private List GetNeighbors(IPathfindingNode node) return neighbors; } - private List ReconstructPath(IPathfindingNode node) + private List ReconstructPath(IPathfindingNode? node) { List path = new List(); diff --git a/Server/Modules/Shared/ImageChangers/BackgroundImage.cs b/Server/Modules/Shared/ImageChangers/BackgroundImage.cs deleted file mode 100644 index 85f7835..0000000 --- a/Server/Modules/Shared/ImageChangers/BackgroundImage.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Fracture.Server.Modules.Shared.ImageChangers; - -public class BackgroundImage -{ - public BackgroundImage(string path) - { - ImagePath = path; - } - - [Required] - public string ImagePath { get; set; } -} diff --git a/Server/Modules/Shared/ImageChangers/BackgroundImageChanger.cs b/Server/Modules/Shared/ImageChangers/BackgroundImageChanger.cs deleted file mode 100644 index 6c41b21..0000000 --- a/Server/Modules/Shared/ImageChangers/BackgroundImageChanger.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Fracture.Server.Modules.MapGenerator.Models.Map; -using Fracture.Server.Modules.MapGenerator.Services; -using Fracture.Server.Modules.MapGenerator.UI; - -namespace Fracture.Server.Modules.Shared.ImageChangers; - -public class BackgroundImageChanger( - ILogger logger, - MapManagerService mapManagerService -) -{ - public required BackgroundImage? BackgroundImage { get; set; } - public required MapManagerService MapManagerService { get; set; } = mapManagerService; - - public Task ChangeBackgroundImageAsync() - { - if (BackgroundImage is null) - { - logger.LogError("BackgroundImage is null"); - return Task.CompletedTask; - } - var currentMap = MapManagerService.CurrentMap; - if (currentMap.Grid is null) - { - logger.LogError("Grid is null"); - return Task.CompletedTask; - } - - var x = MapView.CharacterXX; - var y = MapView.CharacterYY; - if (x < 0 || y < 0 || x >= currentMap.Width || y >= currentMap.Height) - { - logger.LogError("Character is out of map"); - return Task.CompletedTask; - } - var cell = currentMap.Grid[x, y]; - var biome = cell.Biome; - - if (biome is null) - { - logger.LogError("Biome is null"); - return Task.CompletedTask; - } - - string? imagePath = null; - if (cell.LocationType != LocationType.None) - { - var currentLocationName = cell.LocationType.ToString(); - var location = biome.Locations.FirstOrDefault(l => - string.Equals(l.Name, currentLocationName, StringComparison.OrdinalIgnoreCase) - ); - - if (location is null) - { - logger.LogWarning( - "No matching location config for LocationType {LocationType} at ({X},{Y}) in biome {BiomeName}. Available: {Locations}", - cell.LocationType, - x, - y, - biome.Name, - string.Join( - ", ", - biome - .Locations.Where(l => !string.IsNullOrWhiteSpace(l.Name)) - .Select(l => l.Name) - ) - ); - } - else - { - imagePath = location.BackgroundImage; - logger.LogInformation( - "Using location background image '{ImagePath}' for LocationType {LocationType} at ({X},{Y})", - imagePath, - cell.LocationType, - x, - y - ); - } - } - if (string.IsNullOrWhiteSpace(imagePath)) - { - imagePath = biome.BackgroundImage; - logger.LogInformation( - "Using biome background image '{ImagePath}' for biome {BiomeName} at ({X},{Y})", - imagePath, - biome.Name, - x, - y - ); - } - - if (string.IsNullOrWhiteSpace(imagePath)) - { - logger.LogError("Background image path is null or empty"); - return Task.CompletedTask; - } - - BackgroundImage.ImagePath = imagePath; - NotifyListChanged(this, EventArgs.Empty); - - return Task.CompletedTask; - } - - public event EventHandler? BackgroundImageChanged; - - public void NotifyListChanged(object? sender, EventArgs e) - { - BackgroundImageChanged?.Invoke(sender, e); - } -} diff --git a/Server/Modules/Shared/ImageChangers/IBackgroundImageChanger.cs b/Server/Modules/Shared/ImageChangers/IBackgroundImageChanger.cs deleted file mode 100644 index 94d737f..0000000 --- a/Server/Modules/Shared/ImageChangers/IBackgroundImageChanger.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Components; - -namespace Fracture.Server.Modules.Shared.ImageChangers; - -public interface IBackgroundImageChanger -{ - [Parameter] - BackgroundImage BackgroundImage { get; set; } - - void Change(); -} diff --git a/Server/Modules/Users/Services/MovementService.cs b/Server/Modules/Users/Services/MovementService.cs new file mode 100644 index 0000000..ccbecaf --- /dev/null +++ b/Server/Modules/Users/Services/MovementService.cs @@ -0,0 +1,46 @@ +using Fracture.Server.Modules.MapGenerator.Models.Map; +using Fracture.Server.Modules.MapGenerator.Services; + +namespace Fracture.Server.Modules.Users.Services; + +public class MovementService(MapManagerService _mapManagerService) +{ + public Map? CurrentMap { get; private set; } + + public int CurrentX { get; private set; } + public int CurrentY { get; private set; } + + public event EventHandler? OnMoved; + public event EventHandler<(Map, Position)>? OnMapEntered; + + public void Initialize() + { + CurrentMap = + _mapManagerService.GetWorldMap() + ?? throw new InvalidOperationException("Map cannot be loaded, critical error"); + + var start = CurrentMap.GetRandomWalkableNode(); + CurrentX = start.X; + CurrentY = start.Y; + + OnMapEntered?.Invoke(this, new(CurrentMap, new Position(CurrentX, CurrentY))); + } + + public bool CanMove(int x, int y) + { + return CurrentMap != null + && x >= 0 + && y >= 0 + && x < CurrentMap!.Width + && y < CurrentMap.Height + && CurrentMap.Grid[x, y].Walkable; + } + + public async Task MoveAsync(int x, int y) + { + CurrentX = x; + CurrentY = y; + + OnMoved?.Invoke(this, new Position(CurrentX, CurrentY)); + } +} diff --git a/Server/Program.cs b/Server/Program.cs index 5e61fe1..11b874d 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -11,7 +11,6 @@ using Fracture.Server.Modules.MapGenerator.Services.TownGen; using Fracture.Server.Modules.Shared; using Fracture.Server.Modules.Shared.Configuration; -using Fracture.Server.Modules.Shared.ImageChangers; using Fracture.Server.Modules.Shared.NameGenerators; using Fracture.Server.Modules.Users.Services; using Hangfire; @@ -40,23 +39,22 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); -builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); + builder.Services.AddTransient(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); -builder.Services.AddScoped(); +builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); -builder.Services.AddHangfire(config => config.UseInMemoryStorage()); // lub .UseSqlServerStorage(connectionString) builder.Services.AddSingleton(typeof(IFloodFillService<>), typeof(FloodFillService<>)); -builder.Services.AddHangfireServer(); builder.Services.AddFeatureManagement(); builder.Services.AddSingleton(); @@ -108,9 +106,6 @@ app.UseExceptionHandler("/Error", true); } -app.UseHangfireDashboard(); -app.UseHangfireServer(); - app.UseStaticFiles(); app.UseAuthorization(); @@ -125,7 +120,8 @@ using (var scope = app.Services.CreateScope()) { var mapManagerService = scope.ServiceProvider.GetRequiredService(); - await mapManagerService.InitializeAndScheduleMapsAsync(); + await mapManagerService.GenerateWorldAsync(); + var db = scope.ServiceProvider.GetRequiredService(); db.Database.Migrate(); }