Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Content.IntegrationTests/Tests/Round/JobTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ public async Task JobWeightTest()
[Test]
public async Task JobPriorityTest()
{
// ES START
// We fuckin HATE job priority
return;
// ES END

await using var pair = await PoolManager.GetServerClient(new PoolSettings
{
DummyTicker = false,
Expand Down
16 changes: 14 additions & 2 deletions Content.Server/GameTicking/GameTicker.Spawning.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
// ES START
using Content.Server._ES.Auditions;
// ES END

namespace Content.Server.GameTicking
{
Expand All @@ -35,6 +38,9 @@ public sealed partial class GameTicker
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly SharedJobSystem _jobs = default!;
[Dependency] private readonly AdminSystem _admin = default!;
// ES START
[Dependency] private readonly AuditionsSystem _auditions = default!;
// ES END

[ValidatePrototypeId<EntityPrototype>]
public const string ObserverPrototypeName = "MobObserver";
Expand Down Expand Up @@ -214,6 +220,10 @@ private void SpawnPlayer(ICommonSession player,

character = HumanoidCharacterProfile.RandomWithSpecies(speciesId);
}
// ES START
var (newMind, mindComp, characterComp) = _auditions.GetRandomCharacterFromPool(station);
character = characterComp.Profile;
// ES END

// We raise this event to allow other systems to handle spawning this player themselves. (e.g. late-join wizard, etc)
var bev = new PlayerBeforeSpawnEvent(player, character, jobId, lateJoin, station);
Expand Down Expand Up @@ -262,8 +272,10 @@ private void SpawnPlayer(ICommonSession player,

DebugTools.AssertNotNull(data);

var newMind = _mind.CreateMind(data!.UserId, character.Name);
_mind.SetUserId(newMind, data.UserId);
// ES START
//var newMind = _mind.CreateMind(data!.UserId, character.Name);
_mind.SetUserId(newMind, data!.UserId);
// ES END

var jobPrototype = _prototypeManager.Index<JobPrototype>(jobId);

Expand Down
103 changes: 73 additions & 30 deletions Content.Server/_ES/Auditions/AuditionsSystem.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
using System.Diagnostics;
using System.Linq;
using Content.Server._ES.Auditions.Components;
using Content.Server.Administration;
using Content.Server.Mind;
using Content.Shared._ES.Auditions;
using Content.Shared.Administration;
using Content.Shared.GameTicking;
using Content.Shared.Mind;
using Content.Shared.Random.Helpers;
using JetBrains.Annotations;
using Robust.Shared.Random;
using Robust.Shared.Toolshed;

Expand All @@ -14,32 +20,66 @@ namespace Content.Server._ES.Auditions;
public sealed class AuditionsSystem : SharedAuditionsSystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly MindSystem _mind = default!;

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

SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnSpawnComplete);
}

private void OnSpawnComplete(PlayerSpawnCompleteEvent ev)
{
if (!_mind.TryGetMind(ev.Mob, out var mind, out _))
return;

var cast = EnsureComp<StationCastComponent>(ev.Station);
cast.Crew.Add(mind);
}

public Entity<MindComponent, CharacterComponent> GetRandomCharacterFromPool(EntityUid station)
{
var producer = GetProducer();

var cast = EnsureComp<StationCastComponent>(station);

if (producer.UnusedCharacterPool.Count < producer.PoolRefreshSize)
{
Log.Debug($"Pool depleted below refresh size ({producer.PoolRefreshSize}). Replenishing pool.");
GenerateCast(producer.PoolSize - producer.UnusedCharacterPool.Count, producer);
}

var weightedMembers = new Dictionary<EntityUid, float>();
foreach (var castMember in producer.UnusedCharacterPool)
{
if (!TryComp<CharacterComponent>(castMember, out var characterComponent))
continue;

// arbitrary formula but good enough
weightedMembers.Add(castMember, 4 * MathF.Pow(4, characterComponent.Relationships.Keys.Count(k => cast.Crew.Contains(k))));
}

var ent = _random.PickAndTake(weightedMembers);
return (ent, Comp<MindComponent>(ent), Comp<CharacterComponent>(ent));
}

/// <summary>
/// Hires a cast, and integrates relationships between all of the characters.
/// </summary>
public void GenerateCast(
int captainCount = 26,
int crewCount = 10,
ProducerComponent? producer = null
)
public void GenerateCast(int count, ProducerComponent? producer = null)
{
if (!TryGetProducer(ref producer))
throw new Exception("Could not get ProducerComponent!");
producer ??= GetProducer();

var preEvt = new PreCastGenerateEvent(producer);
RaiseLocalEvent(ref preEvt);

var captains = GenerateEmptySocialGroup();
captains.Comp.RelativeContext = producer.CaptainContext;

var newCharacters = new List<EntityUid>();

for (var i = 0; i < captainCount; i++)
for (var i = 0; i < count; i++)
{
var newCrew = GenerateRandomCrew(crewCount);
captains.Comp.Members.Add(newCrew.Comp.Members[0]);
newCharacters.AddRange(newCrew.Comp.Members);
var newCrew = GenerateCharacter(producer: producer);
newCharacters.Add(newCrew);
}

var psgEvt = new PostShipGenerateEvent(producer);
Expand All @@ -64,21 +104,10 @@ public void GenerateCast(
}

IntegrateRelationshipGroup(producer.IntercrewContext, newCharacters);
producer.Characters.AddRange(newCharacters);

var postEvt = new PostCastGenerateEvent(producer);
RaiseLocalEvent(ref postEvt);
}

public void GenerateCast(
int captainCount = 26,
int minimumCrew = 5,
int maximumCrew = 12,
ProducerComponent? producer = null
)
{
GenerateCast(captainCount, _random.Next(minimumCrew, maximumCrew), producer);
}
}

[ToolshedCommand, AdminCommand(AdminFlags.Round)]
Expand All @@ -87,14 +116,14 @@ public sealed class CastCommand : ToolshedCommand
private AuditionsSystem? _auditions;

[CommandImplementation("generate")]
public IEnumerable<string> Generate(int captainCount = 26, int crewSize = 10)
public IEnumerable<string> Generate(int crewSize = 10)
{
_auditions ??= GetSys<AuditionsSystem>();

var stopwatch = new Stopwatch();
stopwatch.Start();

_auditions.GenerateCast(captainCount, crewSize, null);
_auditions.GenerateCast(crewSize);

yield return $"Generated cast in {stopwatch.Elapsed.TotalMilliseconds} ms.";
}
Expand All @@ -109,8 +138,8 @@ public IEnumerable<string> View([PipedArgument] EntityUid castMember)
}
else
{
yield return $"{character.Name}, {character.Age} years old ({character.DateOfBirth.ToShortDateString()})\nBackground: {character.Background}\nRelationships\n";
Dictionary<string, List<string>> relationships = new();
yield return $"{character.Name}, {character.Profile.Age} years old ({character.DateOfBirth.ToShortDateString()})\nBackground: {character.Background}\nRelationships\n";
Dictionary<string, List<EntityUid>> relationships = new();
foreach (var relationship in character.Relationships)
{
if (relationships.ContainsKey(relationship.Value))
Expand All @@ -125,12 +154,26 @@ public IEnumerable<string> View([PipedArgument] EntityUid castMember)
}
}
}

[CommandImplementation("viewAll")]
public IEnumerable<string> ViewAll()
{
_auditions ??= GetSys<AuditionsSystem>();
var producer = _auditions.GetProducer();
foreach (var character in producer.Characters)
{
foreach (var line in View(character))
{
yield return line;
}
}
}
}

/// <summary>
/// Fires prior to this social group's relationships being integrated.
/// </summary>
[ByRefEvent]
[ByRefEvent, PublicAPI]
public readonly record struct SocialGroupPreIntegrationEvent(Entity<SocialGroupComponent> Group);

/// <summary>
Expand Down
11 changes: 11 additions & 0 deletions Content.Server/_ES/Auditions/Components/StationCastComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Content.Server._ES.Auditions.Components;

/// <summary>
/// This is used for holding an associated cast component for a station.
/// </summary>
[RegisterComponent]
public sealed partial class StationCastComponent : Component
{
[DataField]
public List<EntityUid> Crew = new();
}
23 changes: 7 additions & 16 deletions Content.Shared/_ES/Auditions/CharacterComponent.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using Content.Shared.Humanoid;
using Robust.Shared.Enums;
using Content.Shared.Preferences;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;

namespace Content.Shared._ES.Auditions;

Expand All @@ -12,27 +10,20 @@ namespace Content.Shared._ES.Auditions;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class CharacterComponent : Component
{
[DataField, AutoNetworkedField]
public string Name = "Rain Miskovitch";

[DataField, AutoNetworkedField]
public int Age = 25;
public string Name => Profile.Name;

[DataField, AutoNetworkedField]
public Gender Gender = Gender.Neuter;
public DateTime DateOfBirth;

[DataField, AutoNetworkedField]
public HumanoidCharacterAppearance Appearance = default!;
public Dictionary<EntityUid, ProtoId<RelationshipPrototype>> Relationships = new ();

[DataField, AutoNetworkedField]
public DateTime DateOfBirth = new(2025, 1, 1);
public ProtoId<BackgroundPrototype> Background;

[DataField, AutoNetworkedField]
public Dictionary<string, ProtoId<RelationshipPrototype>> Relationships = new ();

[DataField, AutoNetworkedField]
public ProtoId<BackgroundPrototype> Background = "Colonist";
public List<EntityUid> Memories = new ();

[DataField, AutoNetworkedField]
public List<EntityUid> Memories = new ();
public HumanoidCharacterProfile Profile;
}
26 changes: 19 additions & 7 deletions Content.Shared/_ES/Auditions/ProducerComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,37 @@ namespace Content.Shared._ES.Auditions;
public sealed partial class ProducerComponent : Component
{
/// <summary>
/// All the characters in the cast.
/// The amount of characters we refresh <see cref="UnusedCharacterPool"/> to.
/// </summary>
[DataField]
public List<EntityUid> Characters = new ();
public int PoolSize = 50;

/// <summary>
/// List of all active social groups.
/// Once the pool goes below this amount, we'll refresh it
/// </summary>
[DataField]
public List<EntityUid> SocialGroups = new ();
public int PoolRefreshSize = 25;

/// <summary>
/// All the characters in the cast.
/// </summary>
[DataField]
public List<EntityUid> Characters = new();

/// <summary>
/// A pool of characters who have not been taken by players.
/// </summary>
[DataField]
public RelationshipContext CrewContext = new ("RelationshipPoolCrew", 0.75f);
public List<EntityUid> UnusedCharacterPool = new();

/// <summary>
/// List of all active social groups.
/// </summary>
[DataField]
public RelationshipContext CaptainContext = new ("RelationshipPoolCaptains", 0.5f);
public List<EntityUid> SocialGroups = new ();

[DataField]
public RelationshipContext IntercrewContext = new ("RelationshipPoolIntercrew", 0.1f);
public RelationshipContext IntercrewContext = new ("RelationshipPoolIntercrew", 0.25f);
}

/// <summary>
Expand Down
Loading
Loading