Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/real-ee/master' into feat/…
Browse files Browse the repository at this point in the history
…floof-upstream-merge-2024-09-05

# Conflicts:
#	Resources/Locale/en-US/devices/device-network.ftl
#	Resources/Locale/en-US/reagents/meta/consumable/drink/alcohol.ftl
#	Resources/Locale/en-US/revenant/revenant.ftl
#	Resources/Locale/en-US/weapons/melee/melee.ftl
#	Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml
#	Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml
#	Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml
#	Resources/Prototypes/Roles/Jobs/Science/research_director.yml
  • Loading branch information
Mnemotechnician committed Sep 8, 2024
2 parents bb9e777 + 08be023 commit b6914bc
Show file tree
Hide file tree
Showing 45 changed files with 724 additions and 121 deletions.
45 changes: 45 additions & 0 deletions Content.Server/HeightAdjust/BloodstreamAdjustSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.CCVar;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Contests;
using Content.Shared.HeightAdjust;
using Robust.Shared.Configuration;

namespace Content.Server.HeightAdjust;

public sealed class BloodstreamAdjustSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly ContestsSystem _contests = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;

public override void Initialize()
{
SubscribeLocalEvent<BloodstreamAffectedByMassComponent, MapInitEvent>((uid, comp, _) => TryAdjustBloodstream((uid, comp)));
SubscribeLocalEvent<BloodstreamAffectedByMassComponent, HeightAdjustedEvent>((uid, comp, _) => TryAdjustBloodstream((uid, comp)));
}

/// <summary>
/// Adjusts the bloodstream of the specified entity based on the settings provided by the component.
/// </summary>
public bool TryAdjustBloodstream(Entity<BloodstreamAffectedByMassComponent> ent)
{
if (!TryComp<BloodstreamComponent>(ent, out var bloodstream)
|| !_solutionContainer.TryGetSolution(ent.Owner, bloodstream.BloodSolutionName, out var bloodSolutionEnt)
|| !_config.GetCVar(CCVars.HeightAdjustModifiesBloodstream))
return false;

var bloodSolution = bloodSolutionEnt.Value.Comp.Solution;

var factor = Math.Pow(_contests.MassContest(ent, bypassClamp: true, rangeFactor: 4f), ent.Comp.Power);
factor = Math.Clamp(factor, ent.Comp.Min, ent.Comp.Max);

var newVolume = bloodstream.BloodMaxVolume * factor;
var newBloodLevel = bloodSolution.FillFraction * newVolume;
bloodSolution.MaxVolume = newVolume;
bloodSolution.SetContents([new ReagentQuantity(bloodstream.BloodReagent, newBloodLevel, null)], false);

return true;
}
}
26 changes: 26 additions & 0 deletions Content.Server/HeightAdjust/BloodstreamAffectedByMassComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Content.Server.Body.Components;

namespace Content.Server.HeightAdjust;

/// <summary>
/// When applied to a humanoid or any mob, adjusts their blood level based on the mass contest between them
/// and an average humanoid.
/// <br/>
/// The formula for the resulting bloodstream volume is <code>V = BloodMaxVolume * MassContest^Power</code>
/// clamped between the specified Min and Max values.
/// </summary>
[RegisterComponent]
public sealed partial class BloodstreamAffectedByMassComponent : Component
{
/// <summary>
/// Minimum and maximum resulting volume factors. A minimum value of 0.5 means that the resulting volume will be at least 50% of the original.
/// </summary>
[DataField]
public float Min = 1 / 3f, Max = 3f;

/// <summary>
/// The power to which the outcome of the mass contest will be risen.
/// </summary>
[DataField]
public float Power = 1f;
}
42 changes: 42 additions & 0 deletions Content.Server/InteractionVerbs/Actions/MoodAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Content.Shared.InteractionVerbs;
using Content.Shared.Mood;
using Robust.Shared.Prototypes;

namespace Content.Server.InteractionVerbs.Actions;

/// <summary>
/// An action that adds a moodlet to the target, or removes one.
/// </summary>
[Serializable]
public sealed partial class MoodAction : InteractionAction
{
[DataField(required: true)]
public ProtoId<MoodEffectPrototype> Effect;

/// <summary>
/// Parameters for the <see cref="MoodEffectEvent"/>. Only used if <see cref="Remove"/> is false.
/// </summary>
[DataField]
public float Modifier = 1f, Offset = 0f;

/// <summary>
/// If true, the moodlet will be removed. Otherwise, it will be added.
/// </summary>
[DataField]
public bool Remove = false;

public override bool CanPerform(InteractionArgs args, InteractionVerbPrototype proto, bool isBefore, VerbDependencies deps)
{
return true;
}

public override bool Perform(InteractionArgs args, InteractionVerbPrototype proto, VerbDependencies deps)
{
if (Remove)
deps.EntMan.EventBus.RaiseLocalEvent(args.Target, new MoodRemoveEffectEvent(Effect));
else
deps.EntMan.EventBus.RaiseLocalEvent(args.Target, new MoodEffectEvent(Effect, Modifier, Offset));

return true; // Mood system is shitcode so we can't even know if the effect was added or anything
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
namespace Content.Server.StationEvents.Components;

/// <summary>
/// A station event scheduler that emits events at irregular intervals, with occasional chaos and occasional calmness.
/// </summary>
[RegisterComponent]
public sealed partial class OscillatingStationEventSchedulerComponent : Component
{
// TODO cvars?
[DataField]
public float MinChaos = 0.1f, MaxChaos = 15f;

/// <summary>
/// The amount of chaos at the beginning of the round.
/// </summary>
[DataField]
public float StartingChaosRatio = 0f;

/// <summary>
/// The value of the first derivative of the event delay function at the beginning of the shift.
/// Must be between 1 and -1.
/// </summary>
[DataField]
public float StartingSlope = 1f;

/// <summary>
/// Biases that determine how likely the event rate is to go up or down, and how fast it's going to happen.
/// </summary>
/// <remarks>
/// Downwards bias must always be negative, and upwards must be positive. Otherwise, you'll get odd behavior or errors.
/// </remarks>
[DataField]
public float DownwardsBias = -1f, UpwardsBias = 1f;

/// <summary>
/// Limits that define how large the chaos slope can become.
/// </summary>
/// <remarks>
/// Downwards limit must always be negative, and upwards must be positive. Otherwise, you'll get odd behavior or errors.
/// </remarks>
[DataField]
public float DownwardsLimit = -1f, UpwardsLimit = 1f;

/// <summary>
/// A value between 0 and 1 that determines how slowly the chaos and its first derivative change in time.
/// </summary>
/// <remarks>
/// Changing these values will have a great impact on how fast the event rate changes.
/// </remarks>
[DataField]
public float ChaosStickiness = 0.93f, SlopeStickiness = 0.96f;

/// <summary>
/// Actual chaos data at the current moment. Those are overridden at runtime.
/// </summary>
[DataField]
public float CurrentChaos, CurrentSlope, LastAcceleration;


[DataField]
public TimeSpan NextUpdate = TimeSpan.Zero, LastEventTime = TimeSpan.Zero;

/// <summary>
/// Update interval, which determines how often current chaos is recalculated.
/// Modifying this value does not directly impact the event rate, but changes how stable the slope is.
/// </summary>
[DataField]
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(5f);
}
102 changes: 102 additions & 0 deletions Content.Server/StationEvents/OscillatingStationEventScheduler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.StationEvents.Components;
using Content.Shared.CCVar;
using Robust.Shared.Configuration;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;

namespace Content.Server.StationEvents;

public sealed class OscillatingStationEventSchedulerSystem : GameRuleSystem<OscillatingStationEventSchedulerComponent>
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EventManagerSystem _event = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly IGameTiming _timing = default!;

[Conditional("DEBUG")]
private void DebugValidateParams(OscillatingStationEventSchedulerComponent c)
{
// This monstrousity is necessary because if someone fucks up one of these parameters,
// it will likely either crash the game (in debug), or cause the event scheduler to stop working and spam the server console (in prod)
DebugTools.Assert(c.DownwardsBias <= 0f && c.UpwardsBias >= 0f, "Fix your scheduler bias");
DebugTools.Assert(c.DownwardsLimit <= 0f && c.UpwardsLimit >= 0f, "Fix your scheduler slope limits");
DebugTools.Assert(c.UpdateInterval > TimeSpan.Zero, "Scheduler update interval must be positive");
DebugTools.Assert(c.ChaosStickiness >= 0f && c.ChaosStickiness <= 1f, "Scheduler stickiness must be between 0 and 1");
DebugTools.Assert(c.SlopeStickiness >= 0f && c.SlopeStickiness <= 1f, "Scheduler stickiness must be between 0 and 1");
DebugTools.Assert(c.MinChaos < c.MaxChaos, "Don't set the minimum above the maximum");
}

private TimeSpan CalculateAverageEventTime(OscillatingStationEventSchedulerComponent comp)
{
//TODO Those cvars are bad
var min = _cfg.GetCVar(CCVars.GameEventsOscillatingMinimumTime);
var max = _cfg.GetCVar(CCVars.GameEventsOscillatingAverageTime);

return TimeSpan.FromSeconds(min + (max - min) / comp.CurrentChaos); // Why does C# have no math.lerp??????????????
}

protected override void Started(EntityUid uid, OscillatingStationEventSchedulerComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
DebugValidateParams(comp);

comp.CurrentChaos = comp.MinChaos + comp.StartingChaosRatio * (comp.MaxChaos - comp.MinChaos);
comp.CurrentSlope = comp.StartingSlope;

comp.NextUpdate = _timing.CurTime + CalculateAverageEventTime(comp);
comp.LastEventTime = _timing.CurTime; // Just so we don't run an event the very moment this scheduler gets added
}

protected override void ActiveTick(EntityUid uid, OscillatingStationEventSchedulerComponent comp, GameRuleComponent gameRule, float frameTime)
{
if (comp.NextUpdate > _timing.CurTime)
return;
comp.NextUpdate = _timing.CurTime + comp.UpdateInterval;
DebugValidateParams(comp);

// Slope is the first derivative of chaos, and acceleration is the second
// We randomize acceleration on each tick and simulate its effect on the slope and base function
// But we spread the effect across a longer time span to achieve a smooth and pleasant result
var delta = (float) comp.UpdateInterval.TotalSeconds;
var newAcceleration = _random.NextFloat(comp.DownwardsBias, comp.UpwardsBias);
var newSlope =
Math.Clamp(comp.CurrentSlope + newAcceleration * delta, comp.DownwardsLimit, comp.UpwardsLimit) * (1 - comp.SlopeStickiness)
+ comp.CurrentSlope * comp.SlopeStickiness;
var newChaos =
Math.Clamp(comp.CurrentChaos + newSlope * delta, comp.MinChaos, comp.MaxChaos) * (1 - comp.ChaosStickiness)
+ comp.CurrentChaos * comp.ChaosStickiness;

comp.CurrentChaos = newChaos;
comp.CurrentSlope = newSlope;
comp.LastAcceleration = newAcceleration;

// We do not use fixed "next event" times because that can cause us to skip over chaos spikes due to periods of low chaos
// Instead we recalculate the time until next event every time, so it can change before the event is even started
var targetDelay = CalculateAverageEventTime(comp);
if (_timing.CurTime > comp.LastEventTime + targetDelay && TryRunNextEvent(uid, comp, out _))
{
#if DEBUG
var passed = _timing.CurTime - comp.LastEventTime;
Log.Debug($"Running an event after {passed.TotalSeconds} sec since last event. Next event scheduled in {CalculateAverageEventTime(comp).TotalSeconds} sec.");
#endif

comp.LastEventTime = _timing.CurTime;
}
}

public bool TryRunNextEvent(EntityUid uid, OscillatingStationEventSchedulerComponent comp, [NotNullWhen(true)] out string? runEvent)
{
runEvent = _event.PickRandomEvent();
if (runEvent == null)
return false;

_gameTicker.AddGameRule(runEvent);
return true;
}
}
24 changes: 24 additions & 0 deletions Content.Server/Traits/TraitSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public sealed class TraitSystem : EntitySystem
[Dependency] private readonly IConfigurationManager _configuration = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly PsionicAbilitiesSystem _psionicAbilities = default!;
[Dependency] private readonly IComponentFactory _componentFactory = default!;

public override void Initialize()
{
Expand Down Expand Up @@ -60,11 +61,34 @@ private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args)
/// </summary>
public void AddTrait(EntityUid uid, TraitPrototype traitPrototype)
{
RemoveTraitComponents(uid, traitPrototype);
AddTraitComponents(uid, traitPrototype);
AddTraitActions(uid, traitPrototype);
AddTraitPsionics(uid, traitPrototype);
}

/// <summary>
/// Removes all components defined by a Trait. It's not possible to validate component removals,
/// so if an incorrect string is given, it's basically a skill issue.
/// </summary>
/// <remarks>
/// This comes before AddTraitComponents for a good reason.
/// It allows for a component to optionally be fully wiped and replaced with a new component.
/// </remarks>
public void RemoveTraitComponents(EntityUid uid, TraitPrototype traitPrototype)
{
if (traitPrototype.ComponentRemovals is null)
return;

foreach (var entry in traitPrototype.ComponentRemovals)
{
if (!_componentFactory.TryGetRegistration(entry, out var comp))
continue;

EntityManager.RemoveComponent(uid, comp.Type);
}
}

/// <summary>
/// Adds all Components included with a Trait.
/// </summary>
Expand Down
Loading

0 comments on commit b6914bc

Please sign in to comment.