Skip to content

Commit

Permalink
Merge pull request #41 from 778b/mission-map-generator
Browse files Browse the repository at this point in the history
Mission map generator
  • Loading branch information
778b authored Feb 5, 2024
2 parents a85c427 + 0d81e14 commit 6c4741e
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 115 deletions.
3 changes: 2 additions & 1 deletion Content.Client/Entry/EntryPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ public override void Init()
_prototypeManager.RegisterIgnore("alertLevels");
_prototypeManager.RegisterIgnore("nukeopsRole");
_prototypeManager.RegisterIgnore("secretPool");
_prototypeManager.RegisterIgnore("missionMapGrid");
_prototypeManager.RegisterIgnore("missionGrid");
_prototypeManager.RegisterIgnore("missionMap");
_prototypeManager.RegisterIgnore("stationGoal"); // Corvax-StationGoal
_prototypeManager.RegisterIgnore("loadout"); // Corvax-Loadout

Expand Down
237 changes: 223 additions & 14 deletions Content.Server/GameTicking/Rules/TS/MasterRORuleSystem.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,247 @@

using System.Linq;
using System.Numerics;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.TS;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;


namespace Content.Server.GameTicking.Rules;

public sealed class MasterRORuleSystem : GameRuleSystem<MasterRORuleComponent>
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
[Dependency] private readonly MapLoaderSystem _mapLoader = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly ITimerManager _timerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ILocalizationManager _localizationManager = default!;

protected override void Added(EntityUid uid, MasterRORuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
private static MapId _currentMissionMapId;
private static Vector2 _currentCenterMissionMap;
protected override void Started(EntityUid uid, MasterRORuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
_chat.DispatchGlobalAnnouncement("Added gamerule", "SpecialForces", true, null, Color.BetterViolet);
var smallestValue = 1;
for (; smallestValue < 25; ++smallestValue) // 25 is magic number
{
if (_mapManager.MapExists(new MapId(smallestValue)))
continue;
break;
}

if (smallestValue == 25)
{
_logManager.GetSawmill("RORule").Error("Event cant spawn map, because maps already more than 24");
return;
}
_currentMissionMapId = new MapId(smallestValue);

var allFoundMaps = _prototypeManager.EnumeratePrototypes<MissionMapPrototype>().ToList();

if (!allFoundMaps.Any())
{
_logManager.GetSawmill("RORule").Error("Event cant spawn map, because cant find any mission map prototype");
return;
}

var allFoundGrids = _prototypeManager.EnumeratePrototypes<MissionGridPrototype>().ToList();
var indexMap = _random.Next(allFoundMaps.Count() - 1);

if (!_mapLoader.TryLoad(_currentMissionMapId, allFoundMaps[indexMap].MapPath.ToString(), out var entityList))
{
return;
}

var grids = _mapManager.GetAllMapGrids(_currentMissionMapId);
var mainGrid = grids.First();

if (!grids.Any())
{
_logManager.GetSawmill("RORule").Error("Event cant found main grid!");
return;
}
var entityMap = _mapManager.GetMapEntityId(_currentMissionMapId);

_currentCenterMissionMap = _mapSystem.LocalToWorld(entityMap, mainGrid, Vector2.Zero);

int objectCount = _random.Next(5, 9);
_logManager.GetSawmill("RORule").Info("Side grid count = {0}", objectCount);

float angleDelta = 360 / objectCount;
var xDelta = _random.NextFloat(0.5f, 1);
var startVector = new Vector2(xDelta, 1 - (xDelta * xDelta)); // hard math of point on normalized circle, Y found by Pifagor's theorem
for (var i = 0; i < objectCount; ++i)
{
var indexGrid = _random.Next(allFoundGrids.Count() - 1);
float currentAngle = angleDelta * i;
var tempOptions = new MapLoadOptions();
tempOptions.Offset = new Vector2(
(startVector.X * Single.Cos(currentAngle) - startVector.Y * Single.Sin(currentAngle)),
(startVector.X * Single.Sin(currentAngle) + startVector.Y * Single.Cos(currentAngle))
) * _random.NextFloat(140f, 220f) + _currentCenterMissionMap;
tempOptions.Rotation = _random.NextAngle(0, 360);

_mapLoader.TryLoad(_currentMissionMapId, allFoundGrids[indexGrid].GridPath.ToString(), out _, tempOptions);
}
_mapManager.DoMapInitialize(_currentMissionMapId);

var allSecretPools = _prototypeManager.EnumeratePrototypes<SecretPoolPrototype>().ToList();

if (!allSecretPools.Any())
{
_logManager.GetSawmill("RORule").Error("No one secretPoolPrototype found!");
return;
}

var missionItemSpawners = new List<EntityUid>();
var tempEntities = _entMan.GetEntities();

foreach (var tempEnt in tempEntities) // there is no other way to get entity by ID in current map
{
if (_entMan.TryGetComponent<TransformComponent>(tempEnt, out var tempTransform))
{
if (tempTransform.MapID != _currentMissionMapId) continue;
}

if (_entMan.TryGetComponent<MetaDataComponent>(tempEnt, out var tempMeta))
{
if (tempMeta.EntityPrototype == null)
continue;

if (tempMeta.EntityPrototype.ID == "MissionItemSpawn")
{
missionItemSpawners.Add(tempEnt);
}
}
}

if (!missionItemSpawners.Any())
{
_logManager.GetSawmill("RORule").Error("No one MissionItemPrototype found!");
return;
}
else
_logManager.GetSawmill("RORule").Info("Found {0} mission item's spawners on RO map", missionItemSpawners.Count());

// @todo antag player system balance
var guaranteedPointsCount = 3;
var sidePointsCount = 2;
int debugCountItems = 0;

var tempPoolIndex = _random.Next(allSecretPools.Count() - 1);

var tempTupleSpawnerList = new List<Tuple<EntityUid, bool>>();
foreach (var tempMissionItem in missionItemSpawners)
{
tempTupleSpawnerList.Add(new Tuple<EntityUid, bool>(tempMissionItem, false));
}

if (missionItemSpawners.Count() <= guaranteedPointsCount)
{
foreach (var tempMissionItem in missionItemSpawners)
{
if (spawnMissionItem(allSecretPools[tempPoolIndex], tempMissionItem, entityMap, mainGrid))
++debugCountItems;
}
}
else
{
for (var i = 0; i < guaranteedPointsCount; ++i)
{
var tempRandMissionIndex = _random.Next(missionItemSpawners.Count());

if (spawnMissionItem(allSecretPools[tempPoolIndex], missionItemSpawners[tempRandMissionIndex],
entityMap, mainGrid))
{
missionItemSpawners.Remove(missionItemSpawners[tempRandMissionIndex]);
++debugCountItems;
}
}

if (missionItemSpawners.Any())
{
if (missionItemSpawners.Count() <= sidePointsCount)
{
foreach (var tempMissionItem in missionItemSpawners)
{
if (_random.Next(100) > 60)
continue; // 40% chance

if (spawnMissionItem(allSecretPools[tempPoolIndex], tempMissionItem, entityMap, mainGrid))
++debugCountItems;
}
}
else
{
for (var i = 0; i < sidePointsCount; ++i)
{
var tempRandMissionIndex = _random.Next(missionItemSpawners.Count());
if (spawnMissionItem(allSecretPools[tempPoolIndex], missionItemSpawners[tempRandMissionIndex],
entityMap, mainGrid))
{
missionItemSpawners.Remove(missionItemSpawners[tempRandMissionIndex]);
++debugCountItems;
}
}
}
}
}

_logManager.GetSawmill("RORule").Info("Spawned {0} mission items on RO map", debugCountItems);

var randDelay = 60000; // 1 min //_random.Next(400000, 600000); // 5-10 min
_timerManager.AddTimer(new Timer(randDelay, false, startEvent));
}


protected override void Started(EntityUid uid, MasterRORuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
protected override void ActiveTick(EntityUid uid, MasterRORuleComponent component, GameRuleComponent gameRule, float frameTime)
{
_chat.DispatchGlobalAnnouncement("Started gamerule", "SpecialForces", true, null, Color.BetterViolet);
//_chat.DispatchGlobalAnnouncement("Added gamerule", "SpecialForces", true, null, Color.BetterViolet);
}

/// <summary>
/// Called when the gamerule ends
/// </summary>
protected override void Ended(EntityUid uid, MasterRORuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args)

private void startEvent()
{
_chat.DispatchGlobalAnnouncement("Ended gamerule", "SpecialForces", true, null, Color.BetterViolet);
var ftlPoint = _entMan.SpawnEntity("FTLPointUnknown", new MapCoordinates(_currentCenterMissionMap, _currentMissionMapId));

var senderLocale = _localizationManager.GetString("research-mission-sender");
var messageLocale = _localizationManager.GetString("research-mission-message");

_chat.DispatchGlobalAnnouncement(messageLocale, senderLocale, true, null, Color.GreenYellow);
}

/// <summary>
/// Called on an active gamerule entity in the Update function
/// </summary>
protected override void ActiveTick(EntityUid uid, MasterRORuleComponent component, GameRuleComponent gameRule, float frameTime)

private bool spawnMissionItem(SecretPoolPrototype secretPool, EntityUid missionItemSpawner, EntityUid entityMap, MapGridComponent mainGrid)
{
//_chat.DispatchGlobalAnnouncement("Added gamerule", "SpecialForces", true, null, Color.BetterViolet);
if (!TryComp(missionItemSpawner, out TransformComponent? xComp))
{
_logManager.GetSawmill("RORule")
.Error(
"Item {0} has no transform component, cant spawn random guaranteed mission item!", missionItemSpawner.Id);
return false;
}

var tempItemPoolIndex = _random.Next(secretPool.PoolItems.Count() - 1);
_entMan.Spawn(
secretPool.PoolItems[tempItemPoolIndex],
new MapCoordinates(_mapSystem.LocalToWorld(entityMap, mainGrid, xComp.LocalPosition),
_currentMissionMapId)
);
return true;
}




}
20 changes: 20 additions & 0 deletions Content.Server/TS/MissionGridPrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
using Robust.Shared.Utility;

namespace Content.Server.TS;

/// <summary>
/// Stores data for generic queries.
/// Each query is run in turn to get the final available results.
/// These results are then run through the considerations.
/// </summary>
[Prototype]
public sealed partial class MissionGridPrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;

[DataField(required: true)]
public ResPath GridPath;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Content.Server.TS;
/// These results are then run through the considerations.
/// </summary>
[Prototype]
public sealed partial class MissionMapGridPrototype : IPrototype
public sealed partial class MissionMapPrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;
Expand Down
2 changes: 2 additions & 0 deletions Resources/Locale/ru-RU/TS/research-mission.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
research-mission-sender = Центральный военный штаб
research-mission-message = Внимание! Одна из наших станций послала сигнал SOS, по последним данным станционного ИИ, на борту живых членов экипажа нет. Приказываем собрать группу, разведать ситуацию, эвакуировать важные документы, устранить имеющиеся угрозы.
Loading

0 comments on commit 6c4741e

Please sign in to comment.