New event: Approaching unknown shuttle (#24490)

* setup codebase

* Add first shuttle

* tak

* sync striker

* sync 2

* pipipi

* Preloaded roundstart shuttles!

* Make it abstract "PreloaderGrid" not "PreloaderShuttle"

* to do

* added china cuisin shuttle

* fixes

* add disaster evacpod

* remove enemy

* final shuttles

* weight 5 -> 10

* move data to component

* remove autotrailer touching

* doc, respath

* fix frozen positioning

* fixes + cvar

* finally

* fix evacpod

* remove blacklistrules

* remove `UnknownShuttleSpawnRule`, refactor `LoadMapRule` to support preloaded grids

* use tryload

* cleanup

* fixes

* use preloadedgrid for loneops

* weight unknown shuttles differently (preliminal)

* leftover

* cleanup and raffling

* partial review

* singleton gridpreloader no station coupling

* fix grid atmoses

* `roleLoadout` support for `LoadoutComponent`, fix missing gear

* weighting changes

* init logic fix

---------

Co-authored-by: Kara <lunarautomaton6@gmail.com>
This commit is contained in:
Ed
2024-05-10 14:35:59 +03:00
committed by GitHub
parent d061aa437e
commit e522bbf90d
30 changed files with 8252 additions and 47 deletions

View File

@@ -1,4 +1,6 @@
using Content.Server.Maps;
using Content.Shared.GridPreloader.Prototypes;
using Content.Shared.Storage;
using Content.Shared.Whitelist;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
@@ -16,11 +18,14 @@ public sealed partial class LoadMapRuleComponent : Component
public MapId? Map;
[DataField]
public ProtoId<GameMapPrototype>? GameMap ;
public ProtoId<GameMapPrototype>? GameMap;
[DataField]
public ResPath? MapPath;
[DataField]
public ProtoId<PreloadedGridPrototype>? PreloadedGrid;
[DataField]
public List<EntityUid> MapGrids = new();

View File

@@ -1,9 +1,11 @@
using Content.Server.Antag;
using Content.Server.GameTicking.Components;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.GridPreloader;
using Content.Server.Spawners.Components;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Server.GameTicking.Rules;
@@ -11,9 +13,12 @@ namespace Content.Server.GameTicking.Rules;
public sealed class LoadMapRuleSystem : GameRuleSystem<LoadMapRuleComponent>
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly MapSystem _map = default!;
[Dependency] private readonly MapLoaderSystem _mapLoader = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly GridPreloaderSystem _gridPreloader = default!;
public override void Initialize()
{
@@ -41,7 +46,9 @@ public sealed class LoadMapRuleSystem : GameRuleSystem<LoadMapRuleComponent>
if (comp.Map != null)
return;
_map.CreateMap(out var mapId);
// grid preloading needs map to init after moving it
var mapUid = comp.PreloadedGrid != null ? _map.CreateMap(out var mapId, false) : _map.CreateMap(out mapId);
_metaData.SetEntityName(mapUid, $"LoadMapRule destination for rule {ToPrettyString(uid)}");
comp.Map = mapId;
if (comp.GameMap != null)
@@ -51,8 +58,29 @@ public sealed class LoadMapRuleSystem : GameRuleSystem<LoadMapRuleComponent>
}
else if (comp.MapPath != null)
{
if (_mapLoader.TryLoad(comp.Map.Value, comp.MapPath.Value.ToString(), out var roots, new MapLoadOptions { LoadMap = true }))
comp.MapGrids.AddRange(roots);
if (!_mapLoader.TryLoad(comp.Map.Value,
comp.MapPath.Value.ToString(),
out var roots,
new MapLoadOptions { LoadMap = true }))
{
_mapManager.DeleteMap(mapId);
return;
}
comp.MapGrids.AddRange(roots);
}
else if (comp.PreloadedGrid != null)
{
// TODO: If there are no preloaded grids left, any rule announcements will still go off!
if (!_gridPreloader.TryGetPreloadedGrid(comp.PreloadedGrid.Value, out var loadedShuttle))
{
_mapManager.DeleteMap(mapId);
return;
}
_transform.SetParent(loadedShuttle.Value, mapUid);
comp.MapGrids.Add(loadedShuttle.Value);
_map.InitializeMap(mapId);
}
else
{

View File

@@ -0,0 +1,16 @@
using Content.Shared.GridPreloader.Prototypes;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Server.GridPreloader;
/// <summary>
/// Component storing data about preloaded grids and their location
/// Goes on the map entity
/// </summary>
[RegisterComponent, Access(typeof(GridPreloaderSystem))]
public sealed partial class GridPreloaderComponent : Component
{
[DataField]
public Dictionary<ProtoId<PreloadedGridPrototype>, List<EntityUid>> PreloadedGrids = new();
}

View File

@@ -0,0 +1,147 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.CCVar;
using Content.Shared.GridPreloader.Prototypes;
using Content.Shared.GridPreloader.Systems;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes;
using System.Numerics;
using Content.Server.GameTicking;
using Content.Shared.GameTicking;
using JetBrains.Annotations;
namespace Content.Server.GridPreloader;
public sealed class GridPreloaderSystem : SharedGridPreloaderSystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly MapSystem _map = default!;
[Dependency] private readonly MapLoaderSystem _mapLoader = default!;
[Dependency] private readonly MetaDataSystem _meta = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
SubscribeLocalEvent<PostGameMapLoad>(OnPostGameMapLoad);
}
private void OnRoundRestart(RoundRestartCleanupEvent ev)
{
var ent = GetPreloaderEntity();
if (ent == null)
return;
Del(ent.Value.Owner);
}
private void OnPostGameMapLoad(PostGameMapLoad ev)
{
EnsurePreloadedGridMap();
}
private void EnsurePreloadedGridMap()
{
// Already have a preloader?
if (GetPreloaderEntity() != null)
return;
if (!_cfg.GetCVar(CCVars.PreloadGrids))
return;
var mapUid = _map.CreateMap(out var mapId, false);
var preloader = EnsureComp<GridPreloaderComponent>(mapUid);
_meta.SetEntityName(mapUid, "GridPreloader Map");
_map.SetPaused(mapId, true);
var globalXOffset = 0f;
foreach (var proto in _prototype.EnumeratePrototypes<PreloadedGridPrototype>())
{
for (var i = 0; i < proto.Copies; i++)
{
var options = new MapLoadOptions
{
LoadMap = false,
};
if (!_mapLoader.TryLoad(mapId, proto.Path.ToString(), out var roots, options))
continue;
// only supports loading maps with one grid.
if (roots.Count != 1)
continue;
var gridUid = roots[0];
// gets grid + also confirms that the root we loaded is actually a grid
if (!TryComp<MapGridComponent>(gridUid, out var mapGrid))
continue;
if (!TryComp<PhysicsComponent>(gridUid, out var physics))
continue;
// Position Calculating
globalXOffset += mapGrid.LocalAABB.Width / 2;
var coords = new Vector2(-physics.LocalCenter.X + globalXOffset, -physics.LocalCenter.Y);
_transform.SetCoordinates(gridUid, new EntityCoordinates(mapUid, coords));
globalXOffset += (mapGrid.LocalAABB.Width / 2) + 1;
// Add to list
if (!preloader.PreloadedGrids.ContainsKey(proto.ID))
preloader.PreloadedGrids[proto.ID] = new();
preloader.PreloadedGrids[proto.ID].Add(gridUid);
}
}
}
/// <summary>
/// Should be a singleton no matter station count, so we can assume 1
/// (better support for singleton component in engine at some point i guess)
/// </summary>
/// <returns></returns>
public Entity<GridPreloaderComponent>? GetPreloaderEntity()
{
var query = AllEntityQuery<GridPreloaderComponent>();
while (query.MoveNext(out var uid, out var comp))
{
return (uid, comp);
}
return null;
}
/// <summary>
/// An attempt to get a certain preloaded shuttle. If there are no more such shuttles left, returns null
/// </summary>
[PublicAPI]
public bool TryGetPreloadedGrid(ProtoId<PreloadedGridPrototype> proto, [NotNullWhen(true)] out EntityUid? preloadedGrid, GridPreloaderComponent? preloader = null)
{
preloadedGrid = null;
if (preloader == null)
{
preloader = GetPreloaderEntity();
if (preloader == null)
return false;
}
if (!preloader.PreloadedGrids.TryGetValue(proto, out var list) || list.Count <= 0)
return false;
preloadedGrid = list[0];
list.RemoveAt(0);
if (list.Count == 0)
preloader.PreloadedGrids.Remove(proto);
return true;
}
}

View File

@@ -200,27 +200,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
loadout.SetDefault(_prototypeManager);
}
// Order loadout selections by the order they appear on the prototype.
foreach (var group in loadout.SelectedLoadouts.OrderBy(x => roleProto.Groups.FindIndex(e => e == x.Key)))
{
foreach (var items in group.Value)
{
if (!_prototypeManager.TryIndex(items.Prototype, out var loadoutProto))
{
Log.Error($"Unable to find loadout prototype for {items.Prototype}");
continue;
}
if (!_prototypeManager.TryIndex(loadoutProto.Equipment, out var startingGear))
{
Log.Error($"Unable to find starting gear {loadoutProto.Equipment} for loadout {loadoutProto}");
continue;
}
// Handle any extra data here.
EquipStartingGear(entity.Value, startingGear, raiseEvent: false);
}
}
EquipRoleLoadout(entity.Value, loadout, roleProto);
}
var gearEquippedEv = new StartingGearEquippedEvent(entity.Value);

View File

@@ -1,4 +1,4 @@
using Robust.Shared.Audio;
using Robust.Shared.Audio;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.StationEvents.Components;
@@ -15,49 +15,49 @@ public sealed partial class StationEventComponent : Component
public const float WeightHigh = 15.0f;
public const float WeightVeryHigh = 20.0f;
[DataField("weight")]
[DataField]
public float Weight = WeightNormal;
[DataField("startAnnouncement")]
[DataField]
public string? StartAnnouncement;
[DataField("endAnnouncement")]
[DataField]
public string? EndAnnouncement;
[DataField("startAudio")]
[DataField]
public SoundSpecifier? StartAudio;
[DataField("endAudio")]
[DataField]
public SoundSpecifier? EndAudio;
/// <summary>
/// In minutes, when is the first round time this event can start
/// </summary>
[DataField("earliestStart")]
[DataField]
public int EarliestStart = 5;
/// <summary>
/// In minutes, the amount of time before the same event can occur again
/// </summary>
[DataField("reoccurrenceDelay")]
[DataField]
public int ReoccurrenceDelay = 30;
/// <summary>
/// How long after being added does the event start
/// </summary>
[DataField("startDelay")]
[DataField]
public TimeSpan StartDelay = TimeSpan.Zero;
/// <summary>
/// How long the event lasts.
/// </summary>
[DataField("duration")]
[DataField]
public TimeSpan? Duration = TimeSpan.FromSeconds(1);
/// <summary>
/// The max amount of time the event lasts.
/// </summary>
[DataField("maxDuration")]
[DataField]
public TimeSpan? MaxDuration;
/// <summary>
@@ -66,13 +66,13 @@ public sealed partial class StationEventComponent : Component
/// <remarks>
/// To avoid running deadly events with low-pop
/// </remarks>
[DataField("minimumPlayers")]
[DataField]
public int MinimumPlayers;
/// <summary>
/// How many times this even can occur in a single round
/// </summary>
[DataField("maxOccurrences")]
[DataField]
public int? MaxOccurrences;
/// <summary>

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Content.Server.Administration.Logs;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking.Components;
@@ -42,6 +43,7 @@ public abstract class StationEventSystem<T> : GameRuleSystem<T> where T : ICompo
if (!TryComp<StationEventComponent>(uid, out var stationEvent))
return;
AdminLogManager.Add(LogType.EventAnnounced, $"Event added / announced: {ToPrettyString(uid)}");
if (stationEvent.StartAnnouncement != null)