Skip to content

Commit

Permalink
Demiplans expeditions (#523)
Browse files Browse the repository at this point in the history
* simple expedition generation

* add simple test key

* expedition map component

* some funny procedural testing

* refactor expeditions from planetbiome to islanddungeons

* some work

* fix: grid dungeon, not map planet dungeon

* unhardcode map components

* finish T1 expedition generation

* Update preset.yml

* indestructable stone

* mob water occlusion

* caves T1 expedition

* Update CP14SpawnExpeditionJob.cs

* Delete shared MissionParams

* rename to demiplans

* pass mapid into job

* demiplan connections

* demiplan exits

* random entry points

* Update config.yml

* some cleanup and renaming

* radius one-time teleport

* rename connections to exitPoint

* merge entry and exit point into rift component

* demipan closing - all rifts deletion

* demiplanEEEEEE

* fixes

* delete floating visuals

* Update CP14DemiplaneTravelingSystem.cs

* intro and outro demiplan music

* rift cores and flashing

* pulling support

* pulling fix + generatordata fix?

* auto destrot demiplans??
  • Loading branch information
TheShuEd authored Oct 31, 2024
1 parent 16e2309 commit 5c4b5d1
Show file tree
Hide file tree
Showing 54 changed files with 1,929 additions and 95 deletions.
118 changes: 118 additions & 0 deletions Content.Server/_CP14/Demiplane/CP14DemiplanSystem.Connections.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using Content.Shared._CP14.Demiplane.Components;
using Robust.Shared.Random;

namespace Content.Server._CP14.Demiplane;

public sealed partial class CP14DemiplaneSystem
{
private void InitConnections()
{
SubscribeLocalEvent<CP14DemiplaneRiftComponent, MapInitEvent>(OnRiftInit);
SubscribeLocalEvent<CP14DemiplaneRiftComponent, ComponentShutdown>(OnRiftShutdown);
}

private void OnRiftInit(Entity<CP14DemiplaneRiftComponent> rift, ref MapInitEvent args)
{
var map = Transform(rift).MapUid;
if (TryComp<CP14DemiplaneComponent>(map, out var demiplan)) // In demiplan
{
if (rift.Comp.TryAutoLinkToMap)
rift.Comp.Demiplan = (map.Value, demiplan);

if (rift.Comp.ActiveTeleport)
AddDemiplanRandomEntryPoint((map.Value, demiplan), rift);
}
else if (rift.Comp.Demiplan is not null) //We out of demiplan
{
if (rift.Comp.ActiveTeleport)
AddDemiplanRandomExitPoint(rift.Comp.Demiplan.Value, rift);
}
}

private void OnRiftShutdown(Entity<CP14DemiplaneRiftComponent> rift, ref ComponentShutdown args)
{
if (rift.Comp.Demiplan is null)
return;

RemoveDemiplanRandomEntryPoint(rift.Comp.Demiplan, rift);
RemoveDemiplanRandomExitPoint(rift.Comp.Demiplan, rift);
}

/// <summary>
///Add a position in the real world where you can get out of this demiplan
/// </summary>
private void AddDemiplanRandomExitPoint(Entity<CP14DemiplaneComponent> demiplan,
Entity<CP14DemiplaneRiftComponent> exitPoint)
{
if (demiplan.Comp.ExitPoints.Contains(exitPoint))
return;

demiplan.Comp.ExitPoints.Add(exitPoint);
exitPoint.Comp.Demiplan = demiplan;
}

/// <summary>
/// Removing the demiplan exit point, one of which the player can exit to
/// </summary>
private void RemoveDemiplanRandomExitPoint(Entity<CP14DemiplaneComponent>? demiplan,
Entity<CP14DemiplaneRiftComponent> exitPoint)
{
if (demiplan is not null && demiplan.Value.Comp.ExitPoints.Contains(exitPoint))
{
demiplan.Value.Comp.ExitPoints.Remove(exitPoint);
exitPoint.Comp.Demiplan = null;
}

if (exitPoint.Comp.DeleteAfterDisconnect)
QueueDel(exitPoint);
}

/// <summary>
/// Add a position within the demiplan that can be entered into the demiplan
/// </summary>
private void AddDemiplanRandomEntryPoint(Entity<CP14DemiplaneComponent> demiplan,
Entity<CP14DemiplaneRiftComponent> entryPoint)
{
if (demiplan.Comp.EntryPoints.Contains(entryPoint))
return;

demiplan.Comp.EntryPoints.Add(entryPoint);
entryPoint.Comp.Demiplan = demiplan;
}

private void RemoveDemiplanRandomEntryPoint(Entity<CP14DemiplaneComponent>? demiplan,
Entity<CP14DemiplaneRiftComponent> entryPoint)
{
if (demiplan is not null && demiplan.Value.Comp.EntryPoints.Contains(entryPoint))
{
demiplan.Value.Comp.EntryPoints.Remove(entryPoint);
entryPoint.Comp.Demiplan = null;
}

if (entryPoint.Comp.DeleteAfterDisconnect)
QueueDel(entryPoint);
}

public bool TryGetDemiplanEntryPoint(Entity<CP14DemiplaneComponent> demiplan, out Entity<CP14DemiplaneRiftComponent>? entryPoint)
{
entryPoint = null;

if (demiplan.Comp.EntryPoints.Count == 0)
return false;

entryPoint = _random.Pick(demiplan.Comp.EntryPoints);
return true;
}

public bool TryGetDemiplanExitPoint(Entity<CP14DemiplaneComponent> demiplan,
out Entity<CP14DemiplaneRiftComponent>? exitPoint)
{
exitPoint = null;

if (demiplan.Comp.ExitPoints.Count == 0)
return false;

exitPoint = _random.Pick(demiplan.Comp.ExitPoints);
return true;
}
}
121 changes: 121 additions & 0 deletions Content.Server/_CP14/Demiplane/CP14DemiplanSystem.Generation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System.Threading;
using Content.Server._CP14.Demiplane.Components;
using Content.Server._CP14.Demiplane.Jobs;
using Content.Shared._CP14.Demiplane.Components;
using Content.Shared._CP14.Demiplane.Prototypes;
using Content.Shared.Interaction.Events;
using Robust.Shared.CPUJob.JobQueues;
using Robust.Shared.CPUJob.JobQueues.Queues;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;

namespace Content.Server._CP14.Demiplane;

public sealed partial class CP14DemiplaneSystem
{
private readonly JobQueue _expeditionQueue = new();
private readonly List<(CP14SpawnRandomDemiplaneJob Job, CancellationTokenSource CancelToken)> _expeditionJobs = new();
private const double JobMaxTime = 0.002;

private void InitGeneration()
{
SubscribeLocalEvent<CP14DemiplaneGeneratorDataComponent, MapInitEvent>(GeneratorMapInit);
SubscribeLocalEvent<CP14DemiplaneGeneratorDataComponent, UseInHandEvent>(GeneratorUsedInHand);
}

private void UpdateGeneration(float frameTime)
{
_expeditionQueue.Process();

foreach (var (job, cancelToken) in _expeditionJobs.ToArray())
{
switch (job.Status)
{
case JobStatus.Finished:
_expeditionJobs.Remove((job, cancelToken));
break;
}
}
}

/// <summary>
/// Generates a new random demiplane based on the specified parameters
/// </summary>
public void SpawnRandomDemiplane(ProtoId<CP14DemiplaneLocationPrototype> location, out Entity<CP14DemiplaneComponent> demiplan, out MapId mapId)
{
var mapUid = _mapSystem.CreateMap(out mapId, runMapInit: false);
var demiComp = EntityManager.EnsureComponent<CP14DemiplaneComponent>(mapUid);
demiplan = (mapUid, demiComp);

var cancelToken = new CancellationTokenSource();
var job = new CP14SpawnRandomDemiplaneJob(
JobMaxTime,
EntityManager,
_logManager,
_mapManager,
_proto,
_dungeon,
_metaData,
_mapSystem,
mapUid,
mapId,
location,
_random.Next(-10000, 10000),
cancelToken.Token);

_expeditionJobs.Add((job, cancelToken));
_expeditionQueue.EnqueueJob(job);
}

private void GeneratorUsedInHand(Entity<CP14DemiplaneGeneratorDataComponent> generator, ref UseInHandEvent args)
{
if (generator.Comp.LocationConfig is null)
return;

SpawnRandomDemiplane(generator.Comp.LocationConfig.Value, out var demiplane, out var mapId);

//TEST
EnsureComp<CP14DemiplaneDestroyWithoutPlayersComponent>(demiplane);

var tempRift = EntityManager.Spawn("CP14DemiplaneTimedRadiusPassway");
var tempRift2 = EntityManager.Spawn("CP14DemiplanRiftCore");
_transform.SetCoordinates(tempRift, Transform(args.User).Coordinates);
_transform.SetCoordinates(tempRift2, Transform(args.User).Coordinates);

var connection = EnsureComp<CP14DemiplaneRiftComponent>(tempRift);
var connection2 = EnsureComp<CP14DemiplaneRiftComponent>(tempRift2);
AddDemiplanRandomExitPoint(demiplane, (tempRift, connection));
AddDemiplanRandomExitPoint(demiplane, (tempRift2, connection2));

QueueDel(generator); //wtf its crash debug build?
}

private void GeneratorMapInit(Entity<CP14DemiplaneGeneratorDataComponent> generator, ref MapInitEvent args)
{
// Here, a unique Demiplan config should be generated based on the CP14DemiplanGeneratorDataComponent

//Location generation
HashSet<CP14DemiplaneLocationPrototype> suitableConfigs = new();
foreach (var locationConfig in _proto.EnumeratePrototypes<CP14DemiplaneLocationPrototype>())
{
suitableConfigs.Add(locationConfig);
}

if (suitableConfigs.Count == 0)
{
Log.Error("Expedition mission generation failed: No suitable location configs.");
QueueDel(generator);
return;
}

var selectedConfig = _random.Pick(suitableConfigs);
generator.Comp.LocationConfig = selectedConfig;

//Modifier generation

//Scenario generation

//ETC generation
}
}
124 changes: 124 additions & 0 deletions Content.Server/_CP14/Demiplane/CP14DemiplaneSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using Content.Server.Flash;
using Content.Server.Procedural;
using Content.Shared._CP14.Demiplane;
using Content.Shared._CP14.Demiplane.Components;
using Robust.Server.Audio;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;

namespace Content.Server._CP14.Demiplane;

public sealed partial class CP14DemiplaneSystem : CP14SharedDemiplaneSystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly DungeonSystem _dungeon = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly FlashSystem _flash = default!;

public override void Initialize()
{
base.Initialize();

InitGeneration();
InitConnections();

SubscribeLocalEvent<CP14DemiplaneComponent, ComponentShutdown>(OnDemiplanShutdown);
}

public override void Update(float frameTime)
{
base.Update(frameTime);

UpdateGeneration(frameTime);
}

/// <summary>
/// Teleports the entity inside the demiplane, to one of the random entry points.
/// </summary>
/// <param name="demiplane">The demiplane the entity will be teleported to</param>
/// <param name="entity">The entity to be teleported</param>
/// <returns></returns>
public bool TryTeleportIntoDemiplane(Entity<CP14DemiplaneComponent> demiplane, EntityUid? entity)
{
if (entity is null)
return false;

if (!TryGetDemiplanEntryPoint(demiplane, out var entryPoint) || entryPoint is null)
{
Log.Error($"{entity} cant get in demiplane {demiplane}: no active entry points!");
return false;
}

var targetCoord = Transform(entryPoint.Value).Coordinates;
_flash.Flash(entity.Value, null, null, 3000f, 0.5f);
_transform.SetCoordinates(entity.Value, targetCoord);
_audio.PlayGlobal(demiplane.Comp.ArrivalSound, entity.Value);


var ev = new CP14DemiplanEntityEnterEvent(entity.Value);
RaiseLocalEvent(demiplane, ev);

return true;
}

/// <summary>
/// Teleports an entity from the demiplane to the real world, to one of the random exit points in the real world.
/// </summary>
/// <param name="demiplane">The demiplane from which the entity will be teleported</param>
/// <param name="entity">An entity that will be teleported into the real world. This entity must be in the demiplane, otherwise the function will not work.</param>
/// <returns></returns>
public bool TryTeleportOutDemiplane(Entity<CP14DemiplaneComponent> demiplane, EntityUid? entity)
{
if (entity is null)
return false;

if (Transform(entity.Value).MapUid != demiplane.Owner)
return false;

if (!TryGetDemiplanExitPoint(demiplane, out var connection) || connection is null)
{
Log.Error($"{entity} cant get out of demiplane {demiplane}: no active connections!");
return false;
}

var targetCoord = Transform(connection.Value).Coordinates;
_flash.Flash(entity.Value, null, null, 3000f, 0.5f);
_transform.SetCoordinates(entity.Value, targetCoord);
_audio.PlayGlobal(demiplane.Comp.DepartureSound, entity.Value);

var ev = new CP14DemiplanEntityLeaveEvent(entity.Value);
RaiseLocalEvent(demiplane, ev);

return true;
}

private void OnDemiplanShutdown(Entity<CP14DemiplaneComponent> demiplane, ref ComponentShutdown args)
{
//We stop asynchronous generation of a demiplane early if for some reason this demiplane is deleted before generation is complete
foreach (var (job, cancelToken) in _expeditionJobs.ToArray())
{
if (job.DemiplaneMapUid == demiplane.Owner)
{
cancelToken.Cancel();
_expeditionJobs.Remove((job, cancelToken));
}
}

foreach (var exit in demiplane.Comp.ExitPoints)
{
RemoveDemiplanRandomExitPoint(demiplane, exit);
}

foreach (var entry in demiplane.Comp.EntryPoints)
{
RemoveDemiplanRandomEntryPoint(demiplane, entry);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Content.Shared._CP14.Demiplane.Components;
using Content.Shared._CP14.Demiplane.Prototypes;
using Robust.Shared.Prototypes;

namespace Content.Server._CP14.Demiplane.Components;

/// <summary>
/// Stores the data needed to generate a new demiplane
/// </summary>
[RegisterComponent, Access(typeof(CP14DemiplaneSystem))]
public sealed partial class CP14DemiplaneGeneratorDataComponent : Component
{
[DataField]
public ProtoId<CP14DemiplaneLocationPrototype>? LocationConfig;

//Generation settings
}
Loading

0 comments on commit 5c4b5d1

Please sign in to comment.