diff --git a/Content.Server/DeltaV/StationEvents/Components/DebrisSpawnerRuleComponent.cs b/Content.Server/DeltaV/StationEvents/Components/DebrisSpawnerRuleComponent.cs new file mode 100644 index 00000000000..0f89e161fc8 --- /dev/null +++ b/Content.Server/DeltaV/StationEvents/Components/DebrisSpawnerRuleComponent.cs @@ -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; + +/// +/// Spawns random debris in space around a loaded grid. +/// Requires . +/// +[RegisterComponent, Access(typeof(DebrisSpawnerRule))] +public sealed partial class DebrisSpawnerRuleComponent : Component +{ + /// + /// How many debris grids to spawn. + /// + [DataField(required: true)] + public int Count; + + /// + /// Modifier for debris distance. + /// Should be between 3 and 10 generally. + /// + [DataField(required: true)] + public float DistanceModifier; +} diff --git a/Content.Server/DeltaV/StationEvents/Components/LoadFarGridRuleComponent.cs b/Content.Server/DeltaV/StationEvents/Components/LoadFarGridRuleComponent.cs new file mode 100644 index 00000000000..ca241fb071f --- /dev/null +++ b/Content.Server/DeltaV/StationEvents/Components/LoadFarGridRuleComponent.cs @@ -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; + +/// +/// Loads a grid far away from a random station. +/// Requires . +/// +[RegisterComponent, Access(typeof(LoadFarGridRule))] +public sealed partial class LoadFarGridRuleComponent : Component +{ + /// + /// Path to the grid to spawn. + /// + [DataField(required: true)] + public ResPath Path = new(); + + /// + /// Roughly how many AABBs away + /// + [DataField(required: true)] + public float DistanceModifier; + + /// + /// "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 + /// + [DataField] + public float Sousk = 123.44f; +} diff --git a/Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs b/Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs deleted file mode 100644 index 07ea5425cf1..00000000000 --- a/Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs +++ /dev/null @@ -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; } - - /// - /// "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 - /// - [DataField("sousk")] - public float SOUSK = 123.44f; - -} diff --git a/Content.Server/DeltaV/StationEvents/Events/DebrisSpawnerRule.cs b/Content.Server/DeltaV/StationEvents/Events/DebrisSpawnerRule.cs new file mode 100644 index 00000000000..41e0742fd3d --- /dev/null +++ b/Content.Server/DeltaV/StationEvents/Events/DebrisSpawnerRule.cs @@ -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 +{ + [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(OnLoadedGrids); + } + + private void OnLoadedGrids(Entity ent, ref RuleLoadedGridsEvent args) + { + if (_config.GetCVar(CCVars.WorldgenEnabled)) + return; + + // get world AABBs of every grid that was loaded, probably just 1 anyway + var boxes = new List(args.Grids.Count); + foreach (var gridId in args.Grids) + { + var grid = Comp(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().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); + } + } +} diff --git a/Content.Server/DeltaV/StationEvents/Events/LoadFarGridRule.cs b/Content.Server/DeltaV/StationEvents/Events/LoadFarGridRule.cs new file mode 100644 index 00000000000..66039084f75 --- /dev/null +++ b/Content.Server/DeltaV/StationEvents/Events/LoadFarGridRule.cs @@ -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 +{ + [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(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(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); + } +} diff --git a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs b/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs deleted file mode 100644 index 6fad44d2c56..00000000000 --- a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs +++ /dev/null @@ -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 -{ - [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(); - var aabbs = EntityQuery().SelectMany(x => - x.Grids.Select(x => - xformQuery.GetComponent(x).WorldMatrix.TransformBox(_entities.GetComponent(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(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(id, out var grid)) return; - var outpostaabb = _entities.GetComponent(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().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); - } -} diff --git a/Content.Server/Roles/ListeningPostRoleComponent.cs b/Content.Server/Roles/ListeningPostRoleComponent.cs new file mode 100644 index 00000000000..7fbc07835a2 --- /dev/null +++ b/Content.Server/Roles/ListeningPostRoleComponent.cs @@ -0,0 +1,9 @@ +using Content.Shared.Roles; + +namespace Content.Server.Roles; + +/// +/// DeltaV - listening post ops have their own role +/// +[RegisterComponent, ExclusiveAntagonist] +public sealed partial class ListeningPostRoleComponent : AntagonistRoleComponent; diff --git a/Content.Server/Roles/RoleSystem.cs b/Content.Server/Roles/RoleSystem.cs index f7a51773573..ba8889f9205 100644 --- a/Content.Server/Roles/RoleSystem.cs +++ b/Content.Server/Roles/RoleSystem.cs @@ -19,6 +19,7 @@ public override void Initialize() SubscribeAntagEvents(); SubscribeAntagEvents(); SubscribeAntagEvents(); + SubscribeAntagEvents(); // DeltaV - listening post role } public string? MindGetBriefing(EntityUid? mindId) diff --git a/Resources/Locale/en-US/deltav/game-ticking/game-rules/rule-listening-post.ftl b/Resources/Locale/en-US/deltav/game-ticking/game-rules/rule-listening-post.ftl new file mode 100644 index 00000000000..77f2dc3f33d --- /dev/null +++ b/Resources/Locale/en-US/deltav/game-ticking/game-rules/rule-listening-post.ftl @@ -0,0 +1 @@ +listening-post-round-end-agent-name = Listening Post Operative diff --git a/Resources/Maps/Shuttles/DeltaV/DV-pirateradio.yml b/Resources/Maps/Shuttles/DeltaV/listening_post.yml similarity index 99% rename from Resources/Maps/Shuttles/DeltaV/DV-pirateradio.yml rename to Resources/Maps/Shuttles/DeltaV/listening_post.yml index 9cff388fc88..0e92bd7f06e 100644 --- a/Resources/Maps/Shuttles/DeltaV/DV-pirateradio.yml +++ b/Resources/Maps/Shuttles/DeltaV/listening_post.yml @@ -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: diff --git a/Resources/Prototypes/DeltaV/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/DeltaV/Entities/Markers/Spawners/ghost_roles.yml index 4fa82531ebf..2c2a63bb398 100644 --- a/Resources/Prototypes/DeltaV/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/DeltaV/Entities/Markers/Spawners/ghost_roles.yml @@ -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 diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/human.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/human.yml deleted file mode 100644 index d539c58496a..00000000000 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/human.yml +++ /dev/null @@ -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 diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml index 2c8b01553a7..9441d436a05 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml @@ -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 diff --git a/Resources/Prototypes/DeltaV/GameRules/events.yml b/Resources/Prototypes/DeltaV/GameRules/events.yml index c1faea220b1..0cd0d18db58 100644 --- a/Resources/Prototypes/DeltaV/GameRules/events.yml +++ b/Resources/Prototypes/DeltaV/GameRules/events.yml @@ -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 diff --git a/Resources/Prototypes/DeltaV/Roles/Antags/listening_post.yml b/Resources/Prototypes/DeltaV/Roles/Antags/listening_post.yml new file mode 100644 index 00000000000..d6b8adf1949 --- /dev/null +++ b/Resources/Prototypes/DeltaV/Roles/Antags/listening_post.yml @@ -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