| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- using System.Collections;
- using System.Linq;
- using System.Numerics;
- using System.Threading;
- using System.Threading.Tasks;
- using Content.Server.Atmos;
- using Content.Server.Atmos.Components;
- using Content.Server.Atmos.EntitySystems;
- using Robust.Shared.CPUJob.JobQueues;
- using Content.Server.Ghost.Roles.Components;
- using Content.Server.Parallax;
- using Content.Server.Procedural;
- using Content.Server.Salvage.Expeditions;
- using Content.Server.Salvage.Expeditions.Structure;
- using Content.Shared.Atmos;
- using Content.Shared.Construction.EntitySystems;
- using Content.Shared.Dataset;
- using Content.Shared.Gravity;
- using Content.Shared.Parallax.Biomes;
- using Content.Shared.Physics;
- using Content.Shared.Procedural;
- using Content.Shared.Procedural.Loot;
- using Content.Shared.Random;
- using Content.Shared.Salvage;
- using Content.Shared.Salvage.Expeditions;
- using Content.Shared.Salvage.Expeditions.Modifiers;
- using Content.Shared.Shuttles.Components;
- using Content.Shared.Storage;
- using Robust.Shared.Collections;
- using Robust.Shared.Map;
- using Robust.Shared.Map.Components;
- using Robust.Shared.Prototypes;
- using Robust.Shared.Random;
- using Robust.Shared.Timing;
- using Robust.Shared.Utility;
- using Content.Server.Shuttles.Components;
- namespace Content.Server.Salvage;
- public sealed class SpawnSalvageMissionJob : Job<bool>
- {
- private readonly IEntityManager _entManager;
- private readonly IGameTiming _timing;
- private readonly IMapManager _mapManager;
- private readonly IPrototypeManager _prototypeManager;
- private readonly AnchorableSystem _anchorable;
- private readonly BiomeSystem _biome;
- private readonly DungeonSystem _dungeon;
- private readonly MetaDataSystem _metaData;
- private readonly SharedTransformSystem _xforms;
- private readonly SharedMapSystem _map;
- public readonly EntityUid Station;
- public readonly EntityUid? CoordinatesDisk;
- private readonly SalvageMissionParams _missionParams;
- private readonly ISawmill _sawmill;
- public SpawnSalvageMissionJob(
- double maxTime,
- IEntityManager entManager,
- IGameTiming timing,
- ILogManager logManager,
- IMapManager mapManager,
- IPrototypeManager protoManager,
- AnchorableSystem anchorable,
- BiomeSystem biome,
- DungeonSystem dungeon,
- MetaDataSystem metaData,
- SharedTransformSystem xform,
- SharedMapSystem map,
- EntityUid station,
- EntityUid? coordinatesDisk,
- SalvageMissionParams missionParams,
- CancellationToken cancellation = default) : base(maxTime, cancellation)
- {
- _entManager = entManager;
- _timing = timing;
- _mapManager = mapManager;
- _prototypeManager = protoManager;
- _anchorable = anchorable;
- _biome = biome;
- _dungeon = dungeon;
- _metaData = metaData;
- _xforms = xform;
- _map = map;
- Station = station;
- CoordinatesDisk = coordinatesDisk;
- _missionParams = missionParams;
- _sawmill = logManager.GetSawmill("salvage_job");
- #if !DEBUG
- _sawmill.Level = LogLevel.Info;
- #endif
- }
- protected override async Task<bool> Process()
- {
- _sawmill.Debug("salvage", $"Spawning salvage mission with seed {_missionParams.Seed}");
- var mapUid = _map.CreateMap(out var mapId, runMapInit: false);
- MetaDataComponent? metadata = null;
- var grid = _entManager.EnsureComponent<MapGridComponent>(mapUid);
- var random = new Random(_missionParams.Seed);
- var destComp = _entManager.AddComponent<FTLDestinationComponent>(mapUid);
- destComp.BeaconsOnly = true;
- destComp.RequireCoordinateDisk = true;
- destComp.Enabled = true;
- _metaData.SetEntityName(
- mapUid,
- _entManager.System<SharedSalvageSystem>().GetFTLName(_prototypeManager.Index<LocalizedDatasetPrototype>("NamesBorer"), _missionParams.Seed));
- _entManager.AddComponent<FTLBeaconComponent>(mapUid);
- // Saving the mission mapUid to a CD is made optional, in case one is somehow made in a process without a CD entity
- if (CoordinatesDisk.HasValue)
- {
- var cd = _entManager.EnsureComponent<ShuttleDestinationCoordinatesComponent>(CoordinatesDisk.Value);
- cd.Destination = mapUid;
- _entManager.Dirty(CoordinatesDisk.Value, cd);
- }
- // Setup mission configs
- // As we go through the config the rating will deplete so we'll go for most important to least important.
- var difficultyId = "Moderate";
- var difficultyProto = _prototypeManager.Index<SalvageDifficultyPrototype>(difficultyId);
- var mission = _entManager.System<SharedSalvageSystem>()
- .GetMission(difficultyProto, _missionParams.Seed);
- var missionBiome = _prototypeManager.Index<SalvageBiomeModPrototype>(mission.Biome);
- if (missionBiome.BiomePrototype != null)
- {
- var biome = _entManager.AddComponent<BiomeComponent>(mapUid);
- var biomeSystem = _entManager.System<BiomeSystem>();
- biomeSystem.SetTemplate(mapUid, biome, _prototypeManager.Index<BiomeTemplatePrototype>(missionBiome.BiomePrototype));
- biomeSystem.SetSeed(mapUid, biome, mission.Seed);
- _entManager.Dirty(mapUid, biome);
- // Gravity
- var gravity = _entManager.EnsureComponent<GravityComponent>(mapUid);
- gravity.Enabled = true;
- _entManager.Dirty(mapUid, gravity, metadata);
- // Atmos
- var air = _prototypeManager.Index<SalvageAirMod>(mission.Air);
- // copy into a new array since the yml deserialization discards the fixed length
- var moles = new float[Atmospherics.AdjustedNumberOfGases];
- air.Gases.CopyTo(moles, 0);
- var atmos = _entManager.EnsureComponent<MapAtmosphereComponent>(mapUid);
- _entManager.System<AtmosphereSystem>().SetMapSpace(mapUid, air.Space, atmos);
- _entManager.System<AtmosphereSystem>().SetMapGasMixture(mapUid, new GasMixture(moles, mission.Temperature), atmos);
- if (mission.Color != null)
- {
- var lighting = _entManager.EnsureComponent<MapLightComponent>(mapUid);
- lighting.AmbientLightColor = mission.Color.Value;
- _entManager.Dirty(mapUid, lighting);
- }
- }
- _mapManager.DoMapInitialize(mapId);
- _mapManager.SetMapPaused(mapId, true);
- // Setup expedition
- var expedition = _entManager.AddComponent<SalvageExpeditionComponent>(mapUid);
- expedition.Station = Station;
- expedition.EndTime = _timing.CurTime + mission.Duration;
- expedition.MissionParams = _missionParams;
- var landingPadRadius = 24;
- var minDungeonOffset = landingPadRadius + 4;
- // We'll use the dungeon rotation as the spawn angle
- var dungeonRotation = _dungeon.GetDungeonRotation(_missionParams.Seed);
- var maxDungeonOffset = minDungeonOffset + 12;
- var dungeonOffsetDistance = minDungeonOffset + (maxDungeonOffset - minDungeonOffset) * random.NextFloat();
- var dungeonOffset = new Vector2(0f, dungeonOffsetDistance);
- dungeonOffset = dungeonRotation.RotateVec(dungeonOffset);
- var dungeonMod = _prototypeManager.Index<SalvageDungeonModPrototype>(mission.Dungeon);
- var dungeonConfig = _prototypeManager.Index(dungeonMod.Proto);
- var dungeons = await WaitAsyncTask(_dungeon.GenerateDungeonAsync(dungeonConfig, mapUid, grid, (Vector2i) dungeonOffset,
- _missionParams.Seed));
- var dungeon = dungeons.First();
- // Aborty
- if (dungeon.Rooms.Count == 0)
- {
- return false;
- }
- expedition.DungeonLocation = dungeonOffset;
- List<Vector2i> reservedTiles = new();
- foreach (var tile in _map.GetTilesIntersecting(mapUid, grid, new Circle(Vector2.Zero, landingPadRadius), false))
- {
- if (!_biome.TryGetBiomeTile(mapUid, grid, tile.GridIndices, out _))
- continue;
- reservedTiles.Add(tile.GridIndices);
- }
- var budgetEntries = new List<IBudgetEntry>();
- /*
- * GUARANTEED LOOT
- */
- // We'll always add this loot if possible
- // mainly used for ore layers.
- foreach (var lootProto in _prototypeManager.EnumeratePrototypes<SalvageLootPrototype>())
- {
- if (!lootProto.Guaranteed)
- continue;
- await SpawnDungeonLoot(lootProto, mapUid);
- }
- // Handle boss loot (when relevant).
- // Handle mob loot.
- // Handle remaining loot
- /*
- * MOB SPAWNS
- */
- var mobBudget = difficultyProto.MobBudget;
- var faction = _prototypeManager.Index<SalvageFactionPrototype>(mission.Faction);
- var randomSystem = _entManager.System<RandomSystem>();
- foreach (var entry in faction.MobGroups)
- {
- budgetEntries.Add(entry);
- }
- var probSum = budgetEntries.Sum(x => x.Prob);
- while (mobBudget > 0f)
- {
- var entry = randomSystem.GetBudgetEntry(ref mobBudget, ref probSum, budgetEntries, random);
- if (entry == null)
- break;
- await SpawnRandomEntry(grid, entry, dungeon, random);
- }
- var allLoot = _prototypeManager.Index<SalvageLootPrototype>(SharedSalvageSystem.ExpeditionsLootProto);
- var lootBudget = difficultyProto.LootBudget;
- foreach (var rule in allLoot.LootRules)
- {
- switch (rule)
- {
- case RandomSpawnsLoot randomLoot:
- budgetEntries.Clear();
- foreach (var entry in randomLoot.Entries)
- {
- budgetEntries.Add(entry);
- }
- probSum = budgetEntries.Sum(x => x.Prob);
- while (lootBudget > 0f)
- {
- var entry = randomSystem.GetBudgetEntry(ref lootBudget, ref probSum, budgetEntries, random);
- if (entry == null)
- break;
- _sawmill.Debug($"Spawning dungeon loot {entry.Proto}");
- await SpawnRandomEntry(grid, entry, dungeon, random);
- }
- break;
- default:
- throw new NotImplementedException();
- }
- }
- return true;
- }
- private async Task SpawnRandomEntry(MapGridComponent grid, IBudgetEntry entry, Dungeon dungeon, Random random)
- {
- await SuspendIfOutOfTime();
- var availableRooms = new ValueList<DungeonRoom>(dungeon.Rooms);
- var availableTiles = new List<Vector2i>();
- while (availableRooms.Count > 0)
- {
- availableTiles.Clear();
- var roomIndex = random.Next(availableRooms.Count);
- var room = availableRooms.RemoveSwap(roomIndex);
- availableTiles.AddRange(room.Tiles);
- while (availableTiles.Count > 0)
- {
- var tile = availableTiles.RemoveSwap(random.Next(availableTiles.Count));
- if (!_anchorable.TileFree(grid, tile, (int) CollisionGroup.MachineLayer,
- (int) CollisionGroup.MachineLayer))
- {
- continue;
- }
- var uid = _entManager.SpawnAtPosition(entry.Proto, grid.GridTileToLocal(tile));
- _entManager.RemoveComponent<GhostRoleComponent>(uid);
- _entManager.RemoveComponent<GhostTakeoverAvailableComponent>(uid);
- return;
- }
- }
- // oh noooooooooooo
- }
- private async Task SpawnDungeonLoot(SalvageLootPrototype loot, EntityUid gridUid)
- {
- for (var i = 0; i < loot.LootRules.Count; i++)
- {
- var rule = loot.LootRules[i];
- switch (rule)
- {
- case BiomeMarkerLoot biomeLoot:
- {
- if (_entManager.TryGetComponent<BiomeComponent>(gridUid, out var biome))
- {
- _biome.AddMarkerLayer(gridUid, biome, biomeLoot.Prototype);
- }
- }
- break;
- case BiomeTemplateLoot biomeLoot:
- {
- if (_entManager.TryGetComponent<BiomeComponent>(gridUid, out var biome))
- {
- _biome.AddTemplate(gridUid, biome, "Loot", _prototypeManager.Index<BiomeTemplatePrototype>(biomeLoot.Prototype), i);
- }
- }
- break;
- }
- }
- }
- }
|