listening post rewrite (#1369)

* rename pirate radio map to listening post, change spawner prototype

* refactor pirate radio spawn rule into DebrisSpawner and LoadFarGrid

* remove obsolete yml

* -m make listening post rule use new stuff and antag selection

* other changes

* fixes

* more

* fixy

* fix nan

* final fix

* what

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
deltanedas 2024-06-13 16:40:41 +00:00 committed by GitHub
parent b00856ce39
commit 5e812e0f27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 281 additions and 204 deletions

View File

@ -0,0 +1,30 @@
/*
* Delta-V - This file is licensed under AGPLv3
* Copyright (c) 2024 Delta-V Contributors
* See AGPLv3.txt for details.
*/
using Content.Server.StationEvents.Events;
namespace Content.Server.StationEvents.Components;
/// <summary>
/// Spawns random debris in space around a loaded grid.
/// Requires <see cref="LoadFarGridRuleComponent"/>.
/// </summary>
[RegisterComponent, Access(typeof(DebrisSpawnerRule))]
public sealed partial class DebrisSpawnerRuleComponent : Component
{
/// <summary>
/// How many debris grids to spawn.
/// </summary>
[DataField(required: true)]
public int Count;
/// <summary>
/// Modifier for debris distance.
/// Should be between 3 and 10 generally.
/// </summary>
[DataField(required: true)]
public float DistanceModifier;
}

View File

@ -0,0 +1,37 @@
/*
* Delta-V - This file is licensed under AGPLv3
* Copyright (c) 2024 Delta-V Contributors
* See AGPLv3.txt for details.
*/
using Content.Server.StationEvents.Events;
using Robust.Shared.Utility;
namespace Content.Server.StationEvents.Components;
/// <summary>
/// Loads a grid far away from a random station.
/// Requires <see cref="RuleGridsComponent"/>.
/// </summary>
[RegisterComponent, Access(typeof(LoadFarGridRule))]
public sealed partial class LoadFarGridRuleComponent : Component
{
/// <summary>
/// Path to the grid to spawn.
/// </summary>
[DataField(required: true)]
public ResPath Path = new();
/// <summary>
/// Roughly how many AABBs away
/// </summary>
[DataField(required: true)]
public float DistanceModifier;
/// <summary>
/// "Stations of Unusual Size Constant", derived from the AABB.Width of Shoukou.
/// This Constant is used to check the size of a station relative to the reference point
/// </summary>
[DataField]
public float Sousk = 123.44f;
}

View File

@ -1,37 +0,0 @@
/*
* Delta-V - This file is licensed under AGPLv3
* Copyright (c) 2024 Delta-V Contributors
* See AGPLv3.txt for details.
*/
using Content.Server.DeltaV.StationEvents.Events;
using Content.Server.StationEvents.Events;
namespace Content.Server.StationEvents.Components;
[RegisterComponent, Access(typeof(PirateRadioSpawnRule))]
public sealed partial class PirateRadioSpawnRuleComponent : Component
{
[DataField("PirateRadioShuttlePath")]
public string PirateRadioShuttlePath = "Maps/Shuttles/DeltaV/DV-pirateradio.yml";
[DataField("additionalRule")]
public EntityUid? AdditionalRule;
[DataField("debrisCount")]
public int DebrisCount { get; set; }
[DataField("distanceModifier")]
public float DistanceModifier { get; set; }
[DataField("debrisDistanceModifier")]
public float DebrisDistanceModifier { get; set; }
/// <summary>
/// "Stations of Unusual Size Constant", derived from the AABB.Width of Shoukou.
/// This Constant is used to check the size of a station relative to the reference point
/// </summary>
[DataField("sousk")]
public float SOUSK = 123.44f;
}

View File

@ -0,0 +1,71 @@
/*
* Delta-V - This file is licensed under AGPLv3
* Copyright (c) 2024 Delta-V Contributors
* See AGPLv3.txt for details.
*/
using Content.Server.GameTicking.Rules;
using Content.Server.Station.Components;
using Content.Server.StationEvents.Components;
using Content.Shared.CCVar;
using Content.Shared.Salvage;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using System.Linq;
namespace Content.Server.StationEvents.Events;
public sealed class DebrisSpawnerRule : StationEventSystem<DebrisSpawnerRuleComponent>
{
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly MapLoaderSystem _mapLoader = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DebrisSpawnerRuleComponent, RuleLoadedGridsEvent>(OnLoadedGrids);
}
private void OnLoadedGrids(Entity<DebrisSpawnerRuleComponent> ent, ref RuleLoadedGridsEvent args)
{
if (_config.GetCVar<bool>(CCVars.WorldgenEnabled))
return;
// get world AABBs of every grid that was loaded, probably just 1 anyway
var boxes = new List<Box2>(args.Grids.Count);
foreach (var gridId in args.Grids)
{
var grid = Comp<MapGridComponent>(gridId);
var aabb = Transform(gridId).WorldMatrix.TransformBox(grid.LocalAABB);
boxes.Add(aabb);
}
// fetch all the salvage maps that can be picked
var salvageMaps = _proto.EnumeratePrototypes<SalvageMapPrototype>().ToList();
// spawn them!
for (var i = 0; i < ent.Comp.Count; i++)
{
var aabb = RobustRandom.Pick(boxes);
var dist = MathF.Max(aabb.Height / 2f, aabb.Width / 2f) * ent.Comp.DistanceModifier;
var offset = RobustRandom.NextVector2(dist, dist * 2.5f);
var randomer = RobustRandom.NextVector2(dist, dist * 5f); //Second random vector to ensure the outpost isn't perfectly centered in the debris field
var options = new MapLoadOptions
{
Offset = aabb.Center + offset + randomer,
LoadMap = false,
};
var salvage = RobustRandom.PickAndTake(salvageMaps);
_mapLoader.Load(args.Map, salvage.MapPath.ToString(), options);
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Delta-V - This file is licensed under AGPLv3
* Copyright (c) 2024 Delta-V Contributors
* See AGPLv3.txt for details.
*/
using Content.Server.GameTicking.Components;
using Content.Server.GameTicking.Rules;
using Content.Server.Station.Components;
using Content.Server.StationEvents.Components;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Random;
namespace Content.Server.StationEvents.Events;
public sealed class LoadFarGridRule : StationEventSystem<LoadFarGridRuleComponent>
{
[Dependency] private readonly MapLoaderSystem _mapLoader = default!;
protected override void Added(EntityUid uid, LoadFarGridRuleComponent comp, GameRuleComponent rule, GameRuleAddedEvent args)
{
base.Added(uid, comp, rule, args);
if (!TryGetRandomStation(out var station) || !TryComp<StationDataComponent>(station, out var data))
{
Log.Error($"{ToPrettyString(uid):rule} failed to find a station!");
ForceEndSelf(uid, rule);
return;
}
if (data.Grids.Count < 1)
{
Log.Error($"{ToPrettyString(uid):rule} picked station {station} which had no grids!");
ForceEndSelf(uid, rule);
return;
}
// get an AABB that contains all the station's grids
var aabb = new Box2();
var map = MapId.Nullspace;
foreach (var gridId in data.Grids)
{
// use the first grid's map id
if (map == MapId.Nullspace)
map = Transform(gridId).MapID;
var grid = Comp<MapGridComponent>(gridId);
var gridAabb = Transform(gridId).WorldMatrix.TransformBox(grid.LocalAABB);
aabb = aabb.Union(gridAabb);
}
var scale = comp.Sousk / aabb.Width;
var modifier = comp.DistanceModifier * scale;
var dist = MathF.Max(aabb.Height / 2f, aabb.Width / 2f) * modifier;
var offset = RobustRandom.NextVector2(dist, dist * 2.5f);
var options = new MapLoadOptions
{
Offset = aabb.Center + offset,
LoadMap = false
};
var path = comp.Path.ToString();
Log.Debug($"Loading far grid {path} at {options.Offset}");
if (!_mapLoader.TryLoad(map, path, out var grids, options))
{
Log.Error($"{ToPrettyString(uid):rule} failed to load grid {path}!");
ForceEndSelf(uid, rule);
return;
}
// let other systems do stuff
var ev = new RuleLoadedGridsEvent(map, grids);
RaiseLocalEvent(uid, ref ev);
}
}

View File

@ -1,99 +0,0 @@
/*
* Delta-V - This file is licensed under AGPLv3
* Copyright (c) 2024 Delta-V Contributors
* See AGPLv3.txt for details.
*/
using System.Linq;
using Content.Server.GameTicking.Components;
using Content.Server.Station.Components;
using Content.Server.StationEvents.Components;
using Content.Server.StationEvents.Events;
using Content.Shared.CCVar;
using Content.Shared.Salvage;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.DeltaV.StationEvents.Events;
public sealed class PirateRadioSpawnRule : StationEventSystem<PirateRadioSpawnRuleComponent>
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _confMan = default!;
protected override void Started(EntityUid uid, PirateRadioSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
//Start of Syndicate Listening Outpost spawning system
base.Started(uid, component, gameRule, args);
var xformQuery = GetEntityQuery<TransformComponent>();
var aabbs = EntityQuery<StationDataComponent>().SelectMany(x =>
x.Grids.Select(x =>
xformQuery.GetComponent(x).WorldMatrix.TransformBox(_entities.GetComponent<MapGridComponent>(x).LocalAABB)))
.ToArray();
if (aabbs.Length < 1) return;
var aabb = aabbs[0];
for (var i = 1; i < aabbs.Length; i++)
{
aabb.Union(aabbs[i]);
}
var distanceFactorCoefficient = component.SOUSK / aabb.Width;
var distanceModifier = Math.Clamp(component.DistanceModifier, 1, 25);
var distanceModifierNormalized = distanceModifier * distanceFactorCoefficient;
var a = MathF.Max(aabb.Height / 2f, aabb.Width / 2f) * distanceModifierNormalized;
var randomoffset = _random.NextVector2(a, a * 2.5f);
var outpostOptions = new MapLoadOptions
{
Offset = aabb.Center + randomoffset,
LoadMap = false,
};
if (!_map.TryLoad(GameTicker.DefaultMap, component.PirateRadioShuttlePath, out var outpostids, outpostOptions)) return;
//End of Syndicate Listening Outpost spawning system
//Start of Debris Field Generation
var debrisSpawner = _confMan.GetCVar<bool>(CCVars.WorldgenEnabled);
if (debrisSpawner == true) return;
var debrisCount = Math.Clamp(component.DebrisCount, 0, 6);
if (debrisCount == 0) return;
var debrisDistanceModifier = Math.Clamp(component.DebrisDistanceModifier, 3, 10);
foreach (var id in outpostids)
{
if (!TryComp<MapGridComponent>(id, out var grid)) return;
var outpostaabb = _entities.GetComponent<TransformComponent>(id).WorldMatrix.TransformBox(grid.LocalAABB);
var b = MathF.Max(outpostaabb.Height / 2f, aabb.Width / 2f) * debrisDistanceModifier;
var k = 1;
while (k < debrisCount + 1)
{
var debrisRandomOffset = _random.NextVector2(b, b * 2.5f);
var randomer = _random.NextVector2(b, b * 5f); //Second random vector to ensure the outpost isn't perfectly centered in the debris field
var debrisOptions = new MapLoadOptions
{
Offset = outpostaabb.Center + debrisRandomOffset + randomer,
LoadMap = false,
};
var salvageProto = _random.Pick(_prototypeManager.EnumeratePrototypes<SalvageMapPrototype>().ToList());
_map.TryLoad(GameTicker.DefaultMap, salvageProto.MapPath.ToString(), out _, debrisOptions);
k++;
}
}
//End of Debris Field generation
}
protected override void Ended(EntityUid uid, PirateRadioSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args)
{
base.Ended(uid, component, gameRule, args);
if (component.AdditionalRule != null)
GameTicker.EndGameRule(component.AdditionalRule.Value);
}
}

View File

@ -0,0 +1,9 @@
using Content.Shared.Roles;
namespace Content.Server.Roles;
/// <summary>
/// DeltaV - listening post ops have their own role
/// </summary>
[RegisterComponent, ExclusiveAntagonist]
public sealed partial class ListeningPostRoleComponent : AntagonistRoleComponent;

View File

@ -19,6 +19,7 @@ public sealed class RoleSystem : SharedRoleSystem
SubscribeAntagEvents<TraitorRoleComponent>();
SubscribeAntagEvents<ZombieRoleComponent>();
SubscribeAntagEvents<ThiefRoleComponent>();
SubscribeAntagEvents<ListeningPostRoleComponent>(); // DeltaV - listening post role
}
public string? MindGetBriefing(EntityUid? mindId)

View File

@ -0,0 +1 @@
listening-post-round-end-agent-name = Listening Post Operative

View File

@ -25,7 +25,6 @@ entities:
- type: MetaData
name: unknown
- type: Transform
parent: invalid
- type: MapGrid
chunks:
0,0:
@ -5546,7 +5545,7 @@ entities:
- type: Transform
pos: 2.5,2.5
parent: 1
- proto: SpawnPointGhostSyndicateListener
- proto: SpawnPointNukies
entities:
- uid: 813
components:

View File

@ -20,19 +20,15 @@
- sprite: Mobs/Species/Human/parts.rsi
state: full
- type: entity # Part of PirateRadioSpawn
- type: entity # Part of ListeningPost
noSpawn: true
parent: BaseAntagSpawner
id: SpawnPointGhostSyndicateListener
name: ghost role spawn point
suffix: syndicate listener
parent: MarkerBase
components:
- type: GhostRole
name: ghost-role-information-listeningop-name
description: ghost-role-information-listeningop-description
rules: ghost-role-information-listeningop-rules
raffle:
settings: default
requirements: # Worth considering these numbers for the goal of making sure someone willing to MRP takes this.
- !type:OverallPlaytimeRequirement
time: 259200 # 72 hours
@ -45,17 +41,9 @@
- !type:DepartmentTimeRequirement
department: Command
time: 40000 # 11.1 hours
- type: GhostRoleMobSpawner
prototype: MobHumanSyndicateListener
- type: Sprite
sprite: Markers/jobs.rsi
layers:
- state: green
- sprite: Structures/Wallmounts/signs.rsi
state: radiation
- type: entity
parent: MarkerBase
parent: BaseAntagSpawner
id: SpawnPointGhostParadoxAnomaly
name: paradox anomaly spawn point
components:
@ -63,10 +51,3 @@
name: ghost-role-information-paradox-anomaly-name
description: ghost-role-information-paradox-anomaly-description
rules: ghost-role-information-paradox-anomaly-rules
raffle:
settings: default
- type: GhostRoleAntagSpawner
- type: Sprite
sprite: Markers/jobs.rsi
layers:
- state: green

View File

@ -1,17 +0,0 @@
- type: entity # Delta-V : Part of a mid-round PirateRadioSpawn
noSpawn: true
parent: MobHumanSyndicateAgent
id: MobHumanSyndicateListener
name: Syndicate Listener
components:
- type: Loadout
prototypes: [SyndicateListenerGear]
- type: NpcFactionMember
factions:
- Syndicate
- type: AutoTraitor
giveUplink: false
giveObjectives: false
- type: AutoImplant
implants:
- DeathAcidifierImplant

View File

@ -1,19 +1,3 @@
- type: entity # Delta-V part of PirateRadioSpawn
id: RandomHumanoidSpawnerListener
name: Syndicate Listener
components:
- type: Sprite
sprite: Mobs/Species/Human/parts.rsi
state: full
- type: RandomHumanoidSpawner
settings: SyndicateListener
- type: randomHumanoidSettings
id: SyndicateListener
components:
- type: Loadout
prototypes: [SyndicateListenerGear]
# Mobsters
- type: entity

View File

@ -45,8 +45,8 @@
- id: MobMothroach
prob: 0.05
- type: entity # Delta-V : Midround Syndie Listening Station Spawn
id: PirateRadioSpawn
- type: entity
id: ListeningPost
parent: BaseGameRule
noSpawn: true
components:
@ -55,16 +55,37 @@
weight: 5
minimumPlayers: 25
maxOccurrences: 1
duration: 1
# TODO:
# 1. use the load grid rule to load the post itself
# 2. rename below to DebrisSpawnRule or something and have it just be debris
# 3. use AntagSelection to spawn the listeners
# the map will need spawnpoints like SpawnPointNukies added
- type: PirateRadioSpawnRule
debrisCount: 6
duration: null
- type: RuleGrids
- type: LoadFarGridRule
path: /Maps/Shuttles/DeltaV/listening_post.yml
distanceModifier: 13
debrisDistanceModifier: 3
- type: DebrisSpawnerRule
count: 6
distanceModifier: 3
# TODO: funny trolling or intel related objectives
- type: AntagLoadProfileRule
- type: AntagSelection
agentName: listening-post-round-end-agent-name
definitions:
- spawnerPrototype: SpawnPointGhostSyndicateListener
min: 2
max: 2
pickPlayer: false
startingGear: SyndicateListenerGear
components:
- type: RandomMetadata
nameSegments:
- names_death_commando
- type: AutoImplant
implants:
- DeathAcidifierImplant
- type: NpcFactionMember
factions:
- Syndicate
mindComponents:
- type: ListeningPostRole
prototype: ListeningPost
# Mid round antag spawns
- type: entity

View File

@ -0,0 +1,18 @@
- type: antag
id: ListeningPost
name: roles-antag-listening-post-name
antagonist: true
objective: roles-antag-listening-post-objective
# keep these in sync with the spawner
requirements: # Worth considering these numbers for the goal of making sure someone willing to MRP takes this.
- !type:OverallPlaytimeRequirement
time: 259200 # 72 hours
- !type:DepartmentTimeRequirement
department: Security
time: 40000 # 11.1 hours
- !type:DepartmentTimeRequirement
department: Civilian
time: 40000 # 11.1 hours
- !type:DepartmentTimeRequirement
department: Command
time: 40000 # 11.1 hours