Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cleanup more stuff #2481

Merged
merged 2 commits into from
Dec 23, 2024
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
105 changes: 59 additions & 46 deletions Content.Server/DeltaV/Abilities/Psionics/PrecognitionPowerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Content.Shared.Abilities.Psionics;
using Content.Shared.Actions.Events;
using Content.Shared.Actions;
using Content.Shared.Chat;
using Content.Shared.DoAfter;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Popups;
Expand All @@ -22,32 +23,38 @@ namespace Content.Server.Abilities.Psionics;

public sealed class PrecognitionPowerSystem : EntitySystem
{
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popups = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;

/// <summary>
/// A map between game rule prototypes and their results to give.
/// </summary>
public Dictionary<EntProtoId, PrecognitionResultComponent> Results = new();

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

SubscribeLocalEvent<PrecognitionPowerComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<PrecognitionPowerComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<PrecognitionPowerComponent, PrecognitionPowerActionEvent>(OnPowerUsed);
SubscribeLocalEvent<PrecognitionPowerComponent, PrecognitionDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
}

private void OnMapInit(Entity<PrecognitionPowerComponent> ent, ref MapInitEvent args)
{
ent.Comp.AllResults = GetAllPrecognitionResults();
_actions.AddAction(ent, ref ent.Comp.PrecognitionActionEntity, ent.Comp.PrecognitionActionId);
_actions.StartUseDelay(ent.Comp.PrecognitionActionEntity);
if (TryComp<PsionicComponent>(ent, out var psionic) && psionic.PsionicAbility == null)
Expand All @@ -66,7 +73,7 @@ private void OnShutdown(EntityUid uid, PrecognitionPowerComponent component, Com

private void OnPowerUsed(EntityUid uid, PrecognitionPowerComponent component, PrecognitionPowerActionEvent args)
{
var ev = new PrecognitionDoAfterEvent(_gameTiming.CurTime);
var ev = new PrecognitionDoAfterEvent();
var doAfterArgs = new DoAfterArgs(EntityManager, uid, component.UseDelay, ev, uid)
{
BreakOnDamage = true
Expand All @@ -76,7 +83,7 @@ private void OnPowerUsed(EntityUid uid, PrecognitionPowerComponent component, Pr
_statusEffects.TryAddStatusEffect<TemporaryBlindnessComponent>(uid, "TemporaryBlindness", component.UseDelay, true);
_statusEffects.TryAddStatusEffect<SlowedDownComponent>(uid, "SlowedDown", component.UseDelay, true);

_doAfterSystem.TryStartDoAfter(doAfterArgs, out var doAfterId);
_doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId);
component.DoAfter = doAfterId;

var player = _audio.PlayGlobal(component.VisionSound, Filter.Entities(uid), true);
Expand Down Expand Up @@ -104,7 +111,7 @@ private void OnDoAfter(EntityUid uid, PrecognitionPowerComponent component, Prec
_statusEffects.TryRemoveStatusEffect(uid, "TemporaryBlindness");
_statusEffects.TryRemoveStatusEffect(uid, "SlowedDown");

_popups.PopupEntity(
_popup.PopupEntity(
Loc.GetString("psionic-power-precognition-failure-by-damage"),
uid,
uid,
Expand All @@ -121,33 +128,31 @@ private void OnDoAfter(EntityUid uid, PrecognitionPowerComponent component, Prec
// Determines the window that will be looked at for events, avoiding events that are too close or too far to be useful.
var minDetectWindow = TimeSpan.FromSeconds(30);
var maxDetectWindow = TimeSpan.FromMinutes(10);
string? message = null;

if (!_mind.TryGetMind(uid, out _, out var mindComponent) || mindComponent.Session == null)
return;

var nextEvent = (FindEarliestNextEvent(minDetectWindow, maxDetectWindow));
if (nextEvent == null) // A special message given if there is no event within the time window.
message = "psionic-power-precognition-no-event-result-message";

if (nextEvent != null && nextEvent.NextEventId != null)
message = GetResultMessage(nextEvent.NextEventId, component);
var nextEvent = FindEarliestNextEvent(minDetectWindow, maxDetectWindow);
LocId? message = nextEvent?.NextEventId is {} nextEventId
? GetResultMessage(nextEventId)
// A special message given if there is no event within the time window.
: "psionic-power-precognition-no-event-result-message";

if (_random.Prob(component.RandomResultChance)) // This will replace the proper result message with a random one occasionaly to simulate some unreliablity.
message = GetRandomResult();

if (string.IsNullOrEmpty(message)) // If there is no message to send don't bother trying to send it.
if (message is not {} locId) // If there is no message to send don't bother trying to send it.
return;

// Send a message describing the vision they see
message = Loc.GetString(message);
_chat.ChatMessageToOne(Shared.Chat.ChatChannel.Server,
message,
Loc.GetString("chat-manager-server-wrap-message", ("message", message)),
uid,
false,
mindComponent.Session.Channel,
Color.PaleVioletRed);
var msg = Loc.GetString(locId);
_chat.ChatMessageToOne(ChatChannel.Server,
msg,
Loc.GetString("chat-manager-server-wrap-message", ("message", message)),
uid,
false,
mindComponent.Session.Channel,
Color.PaleVioletRed);

component.DoAfter = null;
}
Expand All @@ -156,37 +161,37 @@ private void OnDoAfter(EntityUid uid, PrecognitionPowerComponent component, Prec
/// Gets the precognition result message corosponding to the passed event id.
/// </summary>
/// <returns>message string corosponding to the event id passed</returns>
private string GetResultMessage(EntProtoId? eventId, PrecognitionPowerComponent component)
private LocId GetResultMessage(EntProtoId eventId)
{
foreach (var (eventProto, precognitionResult) in component.AllResults)
if (!Results.TryGetValue(eventId, out var result))
{
if (eventProto.ID == eventId && precognitionResult != null)
return precognitionResult.Message;
Log.Error($"Prototype {eventId} does not have an associated precognitionResult!");
return string.Empty;
}
Log.Error($"Prototype {eventId} does not have an associated precognitionResult!");
return string.Empty;

return result.Message;
}

/// <summary>
/// </summary>
/// <returns>The localized string of a weighted randomly chosen precognition result</returns>
public string? GetRandomResult()
/// <returns>The locale message id of a weighted randomly chosen precognition result</returns>
public LocId? GetRandomResult()
{
var precognitionResults = GetAllPrecognitionResults();
var sumOfWeights = 0;
foreach (var precognitionResult in precognitionResults.Values)
sumOfWeights += (int)precognitionResult.Weight;
// funny weighted random
var sumOfWeights = 0f;
foreach (var precognitionResult in Results.Values)
sumOfWeights += precognitionResult.Weight;

sumOfWeights = _random.Next(sumOfWeights);
foreach (var precognitionResult in precognitionResults.Values)
sumOfWeights = (float) _random.Next((double) sumOfWeights);
foreach (var precognitionResult in Results.Values)
{
sumOfWeights -= (int)precognitionResult.Weight;
sumOfWeights -= precognitionResult.Weight;

if (sumOfWeights <= 0)
if (sumOfWeights <= 0f)
return precognitionResult.Message;
}

Log.Error("Result was not found after weighted pick process!");
Log.Error("Precognition result was not found after weighted pick process!");
return null;
}

Expand All @@ -208,25 +213,33 @@ private string GetResultMessage(EntProtoId? eventId, PrecognitionPowerComponent
&& nextEventComponent.NextEventTime < _gameTicker.RoundDuration() + maxDetectWindow
&& earliestNextEvent == null
|| nextEventComponent.NextEventTime < earliestNextEventTime)
{
earliestNextEvent ??= nextEventComponent;
}
}
return earliestNextEvent;
}

public Dictionary<EntityPrototype, PrecognitionResultComponent> GetAllPrecognitionResults()
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
{
var allEvents = new Dictionary<EntityPrototype, PrecognitionResultComponent>();
foreach (var prototype in _prototype.EnumeratePrototypes<EntityPrototype>())
if (!args.WasModified<EntityPrototype>())
return;

CachePrecognitionResults();
}

private void CachePrecognitionResults()
{
Results.Clear();
foreach (var prototype in _proto.EnumeratePrototypes<EntityPrototype>())
{
if (prototype.Abstract)
continue;

if (!prototype.TryGetComponent<PrecognitionResultComponent>(out var precognitionResult, _factory))
continue;

allEvents.Add(prototype, precognitionResult);
Results.Add(prototype.ID, precognitionResult);
}

return allEvents;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;

namespace Content.Server.DeltaV.StationEvents.NextEvent;

[RegisterComponent, Access(typeof(NextEventSystem))]
[AutoGenerateComponentPause]
public sealed partial class NextEventComponent : Component
{
/// <summary>
Expand All @@ -14,6 +16,6 @@ public sealed partial class NextEventComponent : Component
/// <summary>
/// Round time of the scheduler's next station event.
/// </summary>
[DataField]
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan NextEventTime;
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,14 @@ protected override void Started(EntityUid uid, BasicStationEventSchedulerCompone
// A little starting variance so schedulers dont all proc at once.
component.TimeUntilNextEvent = RobustRandom.NextFloat(component.MinimumTimeUntilFirstEvent, component.MinimumTimeUntilFirstEvent + 120);

// DeltaV - end init NextEventComp
// Begin DeltaV Additions: init NextEventComp
if (TryComp<NextEventComponent>(uid, out var nextEventComponent)
&& _event.TryGenerateRandomEvent(component.ScheduledGameRules, out string? firstEvent, TimeSpan.FromSeconds(component.TimeUntilNextEvent))
&& firstEvent != null)
&& _event.TryGenerateRandomEvent(component.ScheduledGameRules, TimeSpan.FromSeconds(component.TimeUntilNextEvent)) is {} firstEvent)
{
_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", firstEvent), ("seconds", (int)component.TimeUntilNextEvent)));
_next.UpdateNextEvent(nextEventComponent, firstEvent, TimeSpan.FromSeconds(component.TimeUntilNextEvent));
}
// DeltaV - end init NextEventComp
// End DeltaV Additions
}

protected override void Ended(EntityUid uid, BasicStationEventSchedulerComponent component, GameRuleComponent gameRule,
Expand Down Expand Up @@ -73,22 +72,24 @@ public override void Update(float frameTime)
continue;
}

// DeltaV events using NextEventComponent
// Begin DeltaV Additions: events using NextEventComponent
if (TryComp<NextEventComponent>(uid, out var nextEventComponent)) // If there is a nextEventComponent use the stashed event instead of running it directly.
{
ResetTimer(eventScheduler); // Time needs to be reset ahead of time since we need to chose events based on the next time it will run.
var nextEventTime = _timing.CurTime + TimeSpan.FromSeconds(eventScheduler.TimeUntilNextEvent);
if (!_event.TryGenerateRandomEvent(eventScheduler.ScheduledGameRules, out string? generatedEvent, nextEventTime))
if (_event.TryGenerateRandomEvent(eventScheduler.ScheduledGameRules, nextEventTime) is not {} generatedEvent)
continue;

_chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("seconds", (int)eventScheduler.TimeUntilNextEvent)));
// Cycle the stashed event with the new generated event and time.
string? storedEvent = _next.UpdateNextEvent(nextEventComponent, generatedEvent, nextEventTime);
var storedEvent = _next.UpdateNextEvent(nextEventComponent, generatedEvent, nextEventTime);
if (string.IsNullOrEmpty(storedEvent)) //If there was no stored event don't try to run it.
continue;

GameTicker.AddGameRule(storedEvent);
continue;
}
// DeltaV end events using NextEventComponent
// End DeltaV Additions: events using NextEventComponent

_event.RunRandomEvent(eventScheduler.ScheduledGameRules);
ResetTimer(eventScheduler);
Expand Down
Loading
Loading