174 lines
6.0 KiB
C#
174 lines
6.0 KiB
C#
|
|
using System.Linq;
|
|||
|
|
using System.Numerics;
|
|||
|
|
using Content.Server._CP14.Demiplane.Components;
|
|||
|
|
using Content.Server.Station.Systems;
|
|||
|
|
using Content.Shared._CP14.Demiplane;
|
|||
|
|
using Content.Shared._CP14.Demiplane.Components;
|
|||
|
|
using Content.Shared._CP14.Procedural.Prototypes;
|
|||
|
|
using Content.Shared.UserInterface;
|
|||
|
|
using Robust.Server.GameObjects;
|
|||
|
|
using Robust.Shared.Prototypes;
|
|||
|
|
using Robust.Shared.Random;
|
|||
|
|
|
|||
|
|
namespace Content.Server._CP14.Demiplane;
|
|||
|
|
|
|||
|
|
public sealed partial class CP14DemiplaneSystem
|
|||
|
|
{
|
|||
|
|
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
|||
|
|
[Dependency] private readonly StationSystem _station = default!;
|
|||
|
|
|
|||
|
|
private readonly ProtoId<CP14ProceduralModifierPrototype> _firstPointProto = "DemiplaneArcSingle";
|
|||
|
|
private readonly ProtoId<CP14ProceduralModifierPrototype> _secondPointProto = "CP14DemiplanEnterRoom";
|
|||
|
|
|
|||
|
|
private void InitializeStation()
|
|||
|
|
{
|
|||
|
|
SubscribeLocalEvent<CP14StationDemiplaneMapComponent, MapInitEvent>(OnMapInit);
|
|||
|
|
SubscribeLocalEvent<CP14DemiplaneNavigationMapComponent, BeforeActivatableUIOpenEvent>(
|
|||
|
|
OnBeforeActivatableUiOpen);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnMapInit(Entity<CP14StationDemiplaneMapComponent> ent, ref MapInitEvent args)
|
|||
|
|
{
|
|||
|
|
GenerateDemiplaneMap(ent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnBeforeActivatableUiOpen(Entity<CP14DemiplaneNavigationMapComponent> ent,
|
|||
|
|
ref BeforeActivatableUIOpenEvent args)
|
|||
|
|
{
|
|||
|
|
var station = _station.GetOwningStation(ent, Transform(ent));
|
|||
|
|
|
|||
|
|
if (!TryComp<CP14StationDemiplaneMapComponent>(station, out var stationMap))
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
UpdateNodesStatus((station.Value, stationMap));
|
|||
|
|
|
|||
|
|
_userInterface.SetUiState(ent.Owner,
|
|||
|
|
CP14DemiplaneMapUiKey.Key,
|
|||
|
|
new CP14DemiplaneMapUiState(stationMap.Nodes, stationMap.Edges));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void UpdateNodesStatus(Entity<CP14StationDemiplaneMapComponent> ent)
|
|||
|
|
{
|
|||
|
|
var openedMaps = new List<Vector2i>();
|
|||
|
|
|
|||
|
|
var query = EntityQueryEnumerator<CP14DemiplaneMapComponent>();
|
|||
|
|
while (query.MoveNext(out var uid, out var demiplane))
|
|||
|
|
{
|
|||
|
|
openedMaps.Add(demiplane.Position);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
foreach (var node in ent.Comp.Nodes)
|
|||
|
|
{
|
|||
|
|
node.Value.Opened = openedMaps.Contains(node.Key);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void GenerateDemiplaneMap(Entity<CP14StationDemiplaneMapComponent> ent)
|
|||
|
|
{
|
|||
|
|
ent.Comp.Nodes.Clear();
|
|||
|
|
ent.Comp.Edges.Clear();
|
|||
|
|
|
|||
|
|
var directions = new[] { new Vector2i(1, 0), new Vector2i(-1, 0), new Vector2i(0, 1), new Vector2i(0, -1) };
|
|||
|
|
|
|||
|
|
// Generate village node
|
|||
|
|
var villageNode = new CP14DemiplaneMapNode(Vector2.Zero, null, null)
|
|||
|
|
{
|
|||
|
|
Start = true,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
ent.Comp.Nodes.Add(Vector2i.Zero, villageNode);
|
|||
|
|
|
|||
|
|
// Generate first node
|
|||
|
|
var firstNodePosition = _random.Pick(directions);
|
|||
|
|
var location = SelectLocation(0);
|
|||
|
|
var modifiers = SelectModifiers(0, location, GetLimits(0));
|
|||
|
|
var firstNode = new CP14DemiplaneMapNode(firstNodePosition, location, modifiers)
|
|||
|
|
{
|
|||
|
|
Opened = true
|
|||
|
|
};
|
|||
|
|
firstNode.Modifiers.Add(_secondPointProto);
|
|||
|
|
|
|||
|
|
ent.Comp.Nodes.Add(firstNodePosition, firstNode);
|
|||
|
|
ent.Comp.Edges.Add((Vector2i.Zero, firstNodePosition));
|
|||
|
|
|
|||
|
|
//In a loop, take a random existing node, find an empty spot next to it on the grid, add a new room there, and connect them.
|
|||
|
|
while (ent.Comp.Nodes.Count < ent.Comp.TotalCount)
|
|||
|
|
{
|
|||
|
|
//Get random existing node
|
|||
|
|
var randomNode = _random.Pick(ent.Comp.Nodes);
|
|||
|
|
|
|||
|
|
if (randomNode.Value.Start)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
var randomNodePosition = randomNode.Key;
|
|||
|
|
|
|||
|
|
// Find a random empty adjacent position
|
|||
|
|
var emptyPositions = directions
|
|||
|
|
.Select(dir => randomNodePosition + dir)
|
|||
|
|
.Where(pos => !ent.Comp.Nodes.ContainsKey(pos))
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
if (emptyPositions.Count == 0)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
var newPosition = emptyPositions[_random.Next(emptyPositions.Count)];
|
|||
|
|
|
|||
|
|
// Add the new node and connect it with an edge
|
|||
|
|
var lvl = randomNode.Value.Level + 1;
|
|||
|
|
location = SelectLocation(lvl);
|
|||
|
|
modifiers = SelectModifiers(lvl, location, GetLimits(lvl));
|
|||
|
|
var newNode = new CP14DemiplaneMapNode(newPosition, location, modifiers)
|
|||
|
|
{
|
|||
|
|
Level = lvl,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
ent.Comp.Nodes[newPosition] = newNode;
|
|||
|
|
ent.Comp.Edges.Add((randomNodePosition, newPosition));
|
|||
|
|
|
|||
|
|
randomNode.Value.Modifiers.Add(_firstPointProto);
|
|||
|
|
newNode.Modifiers.Add(_secondPointProto);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
foreach (var (position, node) in ent.Comp.Nodes)
|
|||
|
|
{
|
|||
|
|
//Random minor UI offset
|
|||
|
|
node.UiPosition += new Vector2(
|
|||
|
|
_random.NextFloat(-0.2f, 0.2f),
|
|||
|
|
_random.NextFloat(-0.2f, 0.2f));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Dictionary<ProtoId<CP14ProceduralModifierCategoryPrototype>, float> GetLimits(int level)
|
|||
|
|
{
|
|||
|
|
return new Dictionary<ProtoId<CP14ProceduralModifierCategoryPrototype>, float>
|
|||
|
|
{
|
|||
|
|
{ "Danger", Math.Max(level * 0.2f, 0.5f) },
|
|||
|
|
{ "GhostRoleDanger", 1f },
|
|||
|
|
{ "Reward", Math.Max(level * 0.3f, 0.5f) },
|
|||
|
|
{ "Ore", Math.Max(level * 0.5f, 1f) },
|
|||
|
|
{ "Fun", 1f },
|
|||
|
|
{ "Weather", 1f },
|
|||
|
|
{ "MapLight", 1f },
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Vector2i? GetRandomNeighbourNotGeneratedMap(Entity<CP14StationDemiplaneMapComponent> ent, Vector2i origin)
|
|||
|
|
{
|
|||
|
|
if (!ent.Comp.Nodes.ContainsKey(origin))
|
|||
|
|
return null;
|
|||
|
|
|
|||
|
|
//Проверить, есть ли у этой ноды исходящие ребра
|
|||
|
|
var paths = new List<Vector2i>();
|
|||
|
|
foreach (var edge in ent.Comp.Edges)
|
|||
|
|
{
|
|||
|
|
if (edge.Item1 == origin && !ent.Comp.GeneratedNodes.Contains(edge.Item2))
|
|||
|
|
paths.Add(edge.Item2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (paths.Count == 0)
|
|||
|
|
return null;
|
|||
|
|
|
|||
|
|
return _random.Pick(paths);
|
|||
|
|
}
|
|||
|
|
}
|