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:
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
16
Content.Server/GridPreloader/GridPreloaderComponent.cs
Normal file
16
Content.Server/GridPreloader/GridPreloaderComponent.cs
Normal 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();
|
||||
}
|
||||
147
Content.Server/GridPreloader/GridPreloaderSystem.cs
Normal file
147
Content.Server/GridPreloader/GridPreloaderSystem.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user