diff --git a/Content.Server/DeltaV/Abilities/Psionics/PrecognitionPowerSystem.cs b/Content.Server/DeltaV/Abilities/Psionics/PrecognitionPowerSystem.cs new file mode 100644 index 00000000000..23fe9d7e3eb --- /dev/null +++ b/Content.Server/DeltaV/Abilities/Psionics/PrecognitionPowerSystem.cs @@ -0,0 +1,232 @@ +using Content.Server.Chat.Managers; +using Content.Server.DoAfter; +using Content.Server.DeltaV.StationEvents.NextEvent; +using Content.Server.GameTicking; +using Content.Server.Mind; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Actions.Events; +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Content.Shared.Eye.Blinding.Components; +using Content.Shared.Popups; +using Content.Shared.Psionics.Events; +using Content.Shared.StatusEffect; +using Content.Shared.Stunnable; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; +using Robust.Shared.Player; + +namespace Content.Server.Abilities.Psionics; + +public sealed class PrecognitionPowerSystem : EntitySystem +{ + [Dependency] private readonly DoAfterSystem _doAfterSystem = 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 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 IRobustRandom _random = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnPowerUsed); + SubscribeLocalEvent(OnDoAfter); + } + + private void OnMapInit(Entity 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(ent, out var psionic) && psionic.PsionicAbility == null) + { + psionic.PsionicAbility = ent.Comp.PrecognitionActionEntity; + psionic.ActivePowers.Add(ent.Comp); + } + } + + private void OnShutdown(EntityUid uid, PrecognitionPowerComponent component, ComponentShutdown args) + { + _actions.RemoveAction(uid, component.PrecognitionActionEntity); + if (TryComp(uid, out var psionic)) + psionic.ActivePowers.Remove(component); + } + + private void OnPowerUsed(EntityUid uid, PrecognitionPowerComponent component, PrecognitionPowerActionEvent args) + { + var ev = new PrecognitionDoAfterEvent(_gameTiming.CurTime); + var doAfterArgs = new DoAfterArgs(EntityManager, uid, component.UseDelay, ev, uid) + { + BreakOnDamage = true + }; + + // A custom shader for seeing visions would be nice but this will do for now. + _statusEffects.TryAddStatusEffect(uid, "TemporaryBlindness", component.UseDelay, true); + _statusEffects.TryAddStatusEffect(uid, "SlowedDown", component.UseDelay, true); + + _doAfterSystem.TryStartDoAfter(doAfterArgs, out var doAfterId); + component.DoAfter = doAfterId; + + var player = _audio.PlayGlobal(component.VisionSound, Filter.Entities(uid), true); + if (player != null) + component.SoundStream = player.Value.Entity; + _psionics.LogPowerUsed(uid, "Precognition"); + args.Handled = true; + } + + /// + /// Upon completion will send a message to the user corrosponding to the next station event to occour. + /// + /// + /// + /// + private void OnDoAfter(EntityUid uid, PrecognitionPowerComponent component, PrecognitionDoAfterEvent args) + { + if (args.Handled) + return; + + if (args.Cancelled) + { + // Need to clean up the applied effects in case of cancel and alert the player. + component.SoundStream = _audio.Stop(component.SoundStream); + _statusEffects.TryRemoveStatusEffect(uid, "TemporaryBlindness"); + _statusEffects.TryRemoveStatusEffect(uid, "SlowedDown"); + + _popups.PopupEntity( + Loc.GetString("psionic-power-precognition-failure-by-damage"), + uid, + uid, + PopupType.SmallCaution); + + if (_actions.TryGetActionData(component.PrecognitionActionEntity, out var actionData)) + // If canceled give a short delay before being able to try again + actionData.Cooldown = + (_gameTicker.RoundDuration(), + _gameTicker.RoundDuration() + TimeSpan.FromSeconds(15)); + return; + } + + // 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); + + 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. + 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); + + component.DoAfter = null; + } + + /// + /// Gets the precognition result message corosponding to the passed event id. + /// + /// message string corosponding to the event id passed + private string GetResultMessage(EntProtoId? eventId, PrecognitionPowerComponent component) + { + foreach (var (eventProto, precognitionResult) in component.AllResults) + { + if (eventProto.ID == eventId && precognitionResult != null) + return precognitionResult.Message; + } + Log.Error($"Prototype {eventId} does not have an associated precognitionResult!"); + return string.Empty; + } + + /// + /// + /// The localized string of a weighted randomly chosen precognition result + public string? GetRandomResult() + { + var precognitionResults = GetAllPrecognitionResults(); + var sumOfWeights = 0; + foreach (var precognitionResult in precognitionResults.Values) + sumOfWeights += (int)precognitionResult.Weight; + + sumOfWeights = _random.Next(sumOfWeights); + foreach (var precognitionResult in precognitionResults.Values) + { + sumOfWeights -= (int)precognitionResult.Weight; + + if (sumOfWeights <= 0) + return precognitionResult.Message; + } + + Log.Error("Result was not found after weighted pick process!"); + return null; + } + + /// + /// Gets the soonest nextEvent to occur within the window. + /// + /// The earliest reletive time that will be return a nextEvent + /// The latest reletive latest time that will be return a nextEvent + /// Component for the next event to occour if one exists in the window. + private NextEventComponent? FindEarliestNextEvent(TimeSpan minDetectWindow, TimeSpan maxDetectWindow) + { + TimeSpan? earliestNextEventTime = null; + NextEventComponent? earliestNextEvent = null; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var nextEventComponent)) + { + // Update if the event is the most recent event that isnt too close or too far from happening to be of use + if (nextEventComponent.NextEventTime > _gameTicker.RoundDuration() + minDetectWindow + && nextEventComponent.NextEventTime < _gameTicker.RoundDuration() + maxDetectWindow + && earliestNextEvent == null + || nextEventComponent.NextEventTime < earliestNextEventTime) + earliestNextEvent ??= nextEventComponent; + } + return earliestNextEvent; + } + + public Dictionary GetAllPrecognitionResults() + { + var allEvents = new Dictionary(); + foreach (var prototype in _prototype.EnumeratePrototypes()) + { + if (prototype.Abstract) + continue; + + if (!prototype.TryGetComponent(out var precognitionResult, _factory)) + continue; + + allEvents.Add(prototype, precognitionResult); + } + + return allEvents; + } +} diff --git a/Content.Server/DeltaV/StationEvents/NextEvent/NextEventComponent.cs b/Content.Server/DeltaV/StationEvents/NextEvent/NextEventComponent.cs new file mode 100644 index 00000000000..78fa5a2f067 --- /dev/null +++ b/Content.Server/DeltaV/StationEvents/NextEvent/NextEventComponent.cs @@ -0,0 +1,19 @@ +using Robust.Shared.Prototypes; + +namespace Content.Server.DeltaV.StationEvents.NextEvent; + +[RegisterComponent, Access(typeof(NextEventSystem))] +public sealed partial class NextEventComponent : Component +{ + /// + /// Id of the next event that will be run by EventManagerSystem. + /// + [DataField] + public EntProtoId? NextEventId; + + /// + /// Round time of the scheduler's next station event. + /// + [DataField] + public TimeSpan NextEventTime; +} diff --git a/Content.Server/DeltaV/StationEvents/NextEvent/NextEventSystem.cs b/Content.Server/DeltaV/StationEvents/NextEvent/NextEventSystem.cs new file mode 100644 index 00000000000..72c4a8a6f0d --- /dev/null +++ b/Content.Server/DeltaV/StationEvents/NextEvent/NextEventSystem.cs @@ -0,0 +1,18 @@ +using Content.Server.DeltaV.StationEvents.NextEvent; +using Robust.Shared.Prototypes; + +namespace Content.Server.DeltaV.StationEvents.NextEvent; + +public sealed class NextEventSystem : EntitySystem +{ + /// + /// Updates the NextEventComponent with the provided id and time and returns the previously stored id. + /// + public EntProtoId? UpdateNextEvent(NextEventComponent component, EntProtoId newEventId, TimeSpan newEventTime) + { + EntProtoId? oldEventId = component.NextEventId; // Store components current NextEventId for return + component.NextEventId = newEventId; + component.NextEventTime = newEventTime; + return oldEventId; + } +} diff --git a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs index bdc9a47e186..882d841b24f 100644 --- a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs @@ -1,5 +1,7 @@ using System.Linq; using Content.Server.Administration; +using Content.Server.Chat.Managers; // DeltaV +using Content.Server.DeltaV.StationEvents.NextEvent; // DeltaV using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; using Content.Server.StationEvents.Components; @@ -9,6 +11,7 @@ using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; // DeltaV using Robust.Shared.Toolshed; using Robust.Shared.Utility; @@ -21,14 +24,27 @@ namespace Content.Server.StationEvents [UsedImplicitly] public sealed class BasicStationEventSchedulerSystem : GameRuleSystem { + [Dependency] private readonly IChatManager _chatManager = default!; // DeltaV + [Dependency] private readonly IGameTiming _timing = default!; // DeltaV [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EventManagerSystem _event = default!; + [Dependency] private readonly NextEventSystem _next = default!; // DeltaV protected override void Started(EntityUid uid, BasicStationEventSchedulerComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { // A little starting variance so schedulers dont all proc at once. component.TimeUntilNextEvent = RobustRandom.NextFloat(component.MinimumTimeUntilFirstEvent, component.MinimumTimeUntilFirstEvent + 120); + + // DeltaV - end init NextEventComp + if (TryComp(uid, out var nextEventComponent) + && _event.TryGenerateRandomEvent(component.ScheduledGameRules, out string? firstEvent, TimeSpan.FromSeconds(component.TimeUntilNextEvent)) + && firstEvent != null) + { + _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 } protected override void Ended(EntityUid uid, BasicStationEventSchedulerComponent component, GameRuleComponent gameRule, @@ -57,6 +73,23 @@ public override void Update(float frameTime) continue; } + // DeltaV events using NextEventComponent + if (TryComp(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)) + 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); + 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 + _event.RunRandomEvent(eventScheduler.ScheduledGameRules); ResetTimer(eventScheduler); } diff --git a/Content.Server/StationEvents/EventManagerSystem.cs b/Content.Server/StationEvents/EventManagerSystem.cs index 741b95d969c..eadacdbbadf 100644 --- a/Content.Server/StationEvents/EventManagerSystem.cs +++ b/Content.Server/StationEvents/EventManagerSystem.cs @@ -11,7 +11,8 @@ using Content.Shared.EntityTable.EntitySelectors; using Content.Shared.EntityTable; using Content.Server.Psionics.Glimmer; // DeltaV -using Content.Shared.Psionics.Glimmer; // DeltaV +using Content.Shared.Psionics.Glimmer; +using System.Diagnostics.CodeAnalysis; // DeltaV namespace Content.Server.StationEvents; @@ -59,37 +60,67 @@ public void RunRandomEvent() /// public void RunRandomEvent(EntityTableSelector limitedEventsTable) { - if (!TryBuildLimitedEvents(limitedEventsTable, out var limitedEvents)) + if (TryGenerateRandomEvent(limitedEventsTable, out string? randomLimitedEvent)) // DeltaV - seperated into own method + GameTicker.AddGameRule(randomLimitedEvent); + } + + // DeltaV - overloaded for backwards compatiblity + public bool TryGenerateRandomEvent(EntityTableSelector limitedEventsTable, [NotNullWhen(true)] out string? randomLimitedEvent) + { + return TryGenerateRandomEvent(limitedEventsTable, out randomLimitedEvent, null); + } + // DeltaV - end overloaded for backwards compatiblity + + // DeltaV - separate event generation method + /// + /// Returns a random event from the list of events given that can be run at a given time. + /// + /// The list of events that can be chosen. + /// Generated event + /// The time to use for checking time restrictions. Uses current time if null. + /// + public bool TryGenerateRandomEvent(EntityTableSelector limitedEventsTable, [NotNullWhen(true)] out string? randomLimitedEvent, TimeSpan? eventRunTime) + { + randomLimitedEvent = null; + if (!TryBuildLimitedEvents(limitedEventsTable, out var limitedEvents, eventRunTime)) { Log.Warning("Provided event table could not build dict!"); - return; + return false; } - var randomLimitedEvent = FindEvent(limitedEvents); // this picks the event, It might be better to use the GetSpawns to do it, but that will be a major rebalancing fuck. + randomLimitedEvent = FindEvent(limitedEvents); // this picks the event, It might be better to use the GetSpawns to do it, but that will be a major rebalancing fuck. + // DeltaV - randomLimitedEvent declared by enclosing method if (randomLimitedEvent == null) { Log.Warning("The selected random event is null!"); - return; + return false; } if (!_prototype.TryIndex(randomLimitedEvent, out _)) { Log.Warning("A requested event is not available!"); - return; + return false; } - GameTicker.AddGameRule(randomLimitedEvent); + return true; + } + // DeltaV - end separate event generation method + + // DeltaV - overloaded for backwards compatiblity + public bool TryBuildLimitedEvents(EntityTableSelector limitedEventsTable, out Dictionary limitedEvents) + { + return TryBuildLimitedEvents(limitedEventsTable, out limitedEvents, null); } + // DeltaV - end overloaded for backwards compatiblity /// /// Returns true if the provided EntityTableSelector gives at least one prototype with a StationEvent comp. /// - public bool TryBuildLimitedEvents(EntityTableSelector limitedEventsTable, out Dictionary limitedEvents) + public bool TryBuildLimitedEvents(EntityTableSelector limitedEventsTable, out Dictionary limitedEvents, TimeSpan? eventRunTime) // DeltaV - Add a time overide { limitedEvents = new Dictionary(); - - var availableEvents = AvailableEvents(); // handles the player counts and individual event restrictions - + // DeltaV - Overide time for stashing events + var availableEvents = AvailableEvents(eventRunTime); // handles the player counts and individual event restrictions if (availableEvents.Count == 0) { Log.Warning("No events were available to run!"); @@ -175,6 +206,16 @@ public bool TryBuildLimitedEvents(EntityTableSelector limitedEventsTable, out Di return null; } + // DeltaV - overloaded for backwards compatiblity + public Dictionary AvailableEvents( + bool ignoreEarliestStart = false, + int? playerCountOverride = null, + TimeSpan? currentTimeOverride = null) + { + return AvailableEvents(null, ignoreEarliestStart, playerCountOverride, currentTimeOverride); + } + // DeltaV - end overloaded for backwards compatiblity + /// /// Gets the events that have met their player count, time-until start, etc. /// @@ -182,6 +223,7 @@ public bool TryBuildLimitedEvents(EntityTableSelector limitedEventsTable, out Di /// Override for round time, if using this to simulate events rather than in an actual round. /// public Dictionary AvailableEvents( + TimeSpan? eventRunTime, bool ignoreEarliestStart = false, int? playerCountOverride = null, TimeSpan? currentTimeOverride = null) @@ -189,9 +231,12 @@ public Dictionary AvailableEvents( var playerCount = playerCountOverride ?? _playerManager.PlayerCount; // playerCount does a lock so we'll just keep the variable here - var currentTime = currentTimeOverride ?? (!ignoreEarliestStart - ? GameTicker.RoundDuration() - : TimeSpan.Zero); + var currentTime = currentTimeOverride ?? ( + (!ignoreEarliestStart + ? eventRunTime // DeltaV - Use eventRunTime instead of RoundDuration if provided + ?? GameTicker.RoundDuration() + : TimeSpan.Zero) + ); var result = new Dictionary(); diff --git a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs index a5dbe102ca1..67c8f9fb5d4 100644 --- a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs @@ -1,16 +1,22 @@ +using Content.Server.Chat.Managers; // DeltaV +using Content.Server.DeltaV.StationEvents.NextEvent; // DeltaV using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; using Content.Server.StationEvents.Components; using Content.Shared.GameTicking.Components; using Robust.Shared.Random; +using Robust.Shared.Timing; // DeltaV namespace Content.Server.StationEvents; public sealed class RampingStationEventSchedulerSystem : GameRuleSystem { + [Dependency] private readonly IChatManager _chatManager = default!; // DeltaV + [Dependency] private readonly IGameTiming _timing = default!; // DeltaV [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EventManagerSystem _event = default!; [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly NextEventSystem _next = default!; // DeltaV /// /// Returns the ChaosModifier which increases as round time increases to a point. @@ -36,6 +42,16 @@ protected override void Started(EntityUid uid, RampingStationEventSchedulerCompo component.StartingChaos = component.MaxChaos / 10; PickNextEventTime(uid, component); + + // DeltaV - end init NextEventComp + if (TryComp(uid, out var nextEventComponent) + && _event.TryGenerateRandomEvent(component.ScheduledGameRules, out string? firstEvent, TimeSpan.FromSeconds(component.TimeUntilNextEvent)) + && firstEvent != null) + { + _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 } public override void Update(float frameTime) @@ -57,6 +73,23 @@ public override void Update(float frameTime) continue; } + // DeltaV events using NextEventComponent + if (TryComp(uid, out var nextEventComponent)) // If there is a nextEventComponent use the stashed event instead of running it directly. + { + PickNextEventTime(uid, scheduler); + var nextEventTime = _timing.CurTime + TimeSpan.FromSeconds(scheduler.TimeUntilNextEvent); + if (!_event.TryGenerateRandomEvent(scheduler.ScheduledGameRules, out string? generatedEvent, nextEventTime) || generatedEvent == null) + continue; + _chatManager.SendAdminAlert(Loc.GetString("station-event-system-run-event-delayed", ("eventName", generatedEvent), ("seconds", (int)scheduler.TimeUntilNextEvent))); + // Cycle the stashed event with the new generated event and time. + string? 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 + PickNextEventTime(uid, scheduler); _event.RunRandomEvent(scheduler.ScheduledGameRules); } diff --git a/Content.Shared/DeltaV/Abilities/Psionics/PrecognitionPowerComponent.cs b/Content.Shared/DeltaV/Abilities/Psionics/PrecognitionPowerComponent.cs new file mode 100644 index 00000000000..26ecf48f7aa --- /dev/null +++ b/Content.Shared/DeltaV/Abilities/Psionics/PrecognitionPowerComponent.cs @@ -0,0 +1,33 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Abilities.Psionics; + +[RegisterComponent] +public sealed partial class PrecognitionPowerComponent : Component +{ + [DataField] + public float RandomResultChance = 0.2F; + + [DataField] + public Dictionary AllResults; + + [DataField] + public SoundSpecifier VisionSound = new SoundPathSpecifier("/Audio/DeltaV/Effects/clang2.ogg"); + + [DataField] + public EntityUid? SoundStream; + + [DataField] + public DoAfterId? DoAfter; + + [DataField] + public TimeSpan UseDelay = TimeSpan.FromSeconds(8.35); // The length of the sound effect + + [DataField] + public EntProtoId PrecognitionActionId = "ActionPrecognition"; + + [DataField] + public EntityUid? PrecognitionActionEntity; +} diff --git a/Content.Shared/DeltaV/Abilities/Psionics/PrecognitionResultComponent.cs b/Content.Shared/DeltaV/Abilities/Psionics/PrecognitionResultComponent.cs new file mode 100644 index 00000000000..34a8979f4fa --- /dev/null +++ b/Content.Shared/DeltaV/Abilities/Psionics/PrecognitionResultComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared.Abilities.Psionics; + +[RegisterComponent] +public sealed partial class PrecognitionResultComponent : Component +{ + [DataField] + public string Message = default!; + + [DataField] + public float Weight = 1; +} diff --git a/Content.Shared/DeltaV/Actions/Events/PrecognitionPowerActionEvent.cs b/Content.Shared/DeltaV/Actions/Events/PrecognitionPowerActionEvent.cs new file mode 100644 index 00000000000..addc4712f2e --- /dev/null +++ b/Content.Shared/DeltaV/Actions/Events/PrecognitionPowerActionEvent.cs @@ -0,0 +1,3 @@ +namespace Content.Shared.Actions.Events; + +public sealed partial class PrecognitionPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/Nyanotrasen/Psionics/Events.cs b/Content.Shared/Nyanotrasen/Psionics/Events.cs index cf9a50c6e18..3837e3d0ee9 100644 --- a/Content.Shared/Nyanotrasen/Psionics/Events.cs +++ b/Content.Shared/Nyanotrasen/Psionics/Events.cs @@ -21,6 +21,26 @@ public PsionicRegenerationDoAfterEvent(TimeSpan startedAt) public override DoAfterEvent Clone() => this; } + // DeltaV Precognition + [Serializable, NetSerializable] + public sealed partial class PrecognitionDoAfterEvent : SimpleDoAfterEvent + { + [DataField("startedAt", required: true)] + public TimeSpan StartedAt; + + private PrecognitionDoAfterEvent() + { + } + + public PrecognitionDoAfterEvent(TimeSpan startedAt) + { + StartedAt = startedAt; + } + + public override DoAfterEvent Clone() => this; + } + // DeltaV End Precognition + [Serializable, NetSerializable] public sealed partial class GlimmerWispDrainDoAfterEvent : SimpleDoAfterEvent { diff --git a/Resources/Locale/en-US/deltav/abilities/psionic.ftl b/Resources/Locale/en-US/deltav/abilities/psionic.ftl new file mode 100644 index 00000000000..677c0b19def --- /dev/null +++ b/Resources/Locale/en-US/deltav/abilities/psionic.ftl @@ -0,0 +1,46 @@ +psionic-power-precognition-failure-by-damage = Your concentration was broken! You fail to decipher anything of use. +psionic-power-precognition-no-event-result-message = You see a vision of an undisturbed lake. + +psionic-power-precognition-xeno-vents-result-message = You see a vision, peering around a corner you see a strange, squelching beast tear at the insides of one of your coworkers as they let out a bloodcurdling scream. +psionic-power-precognition-mothroach-spawn-result-message = You see a hungering mass of newborns, chittering and screeching as they sink their mandibles into the station's knitwork. +psionic-power-precognition-listening-post-result-message = you hear a cacophony of foreign voices speaking over radio static; you wake with a creeping sense of paranoia. +psionic-power-precognition-paradox-anomaly-result-message = You see your coworker splitting in two. Where there was one, there are now two. You are unsure which of the beings is truly your coworker. +psionic-power-precognition-fugitive-result-message = You see hounds around every corner all hunting for someone who does not belong. +psionic-power-precognition-syndicate-recruiter-result-message = You see someone cutting ties on a chain-link fence and reforging its now disparate parts under a new oath of blood. +psionic-power-precognition-synthesis-specialist-result-message = You smell a dangerous mixture of chemicals in the air; the distant sound of a small plasma engine roars to life. +psionic-power-precognition-cargo-gifts-base-result-message = You see a vision of yourself, gathered for a time-old tradition of receiving gifts. You didn't ask for these, but you must pretend to appreciate nonetheless. +psionic-power-precognition-anomoly-spawn-result-message = You attempt to look forward, but the future is distorted by a blast of noöspheric energies converging on a single point. +psionic-power-precognition-bluespace-artifact-result-message = You attempt to look forward but are blinded by noöspheric energies coalescing into an object beyond comprehension. +psionic-power-precognition-bluespace-locker-result-message = You attempt to look forward, but the noösphere seems distorted by a constantly shifting bluespace energy. +psionic-power-precognition-breaker-flip-result-message = You see torches snuff around you and keepers rekindling the lost flames. +psionic-power-precognition-bureaucratic-error-result-message = You see a vision of yourself trapped in a room, trying to solve a puzzle with both missing and duplicate pieces. +psionic-power-precognition-clerical-error-result-message = You see faces you once knew being obscured in a fog of static, identities lost. +psionic-power-precognition-closet-skeleton-result-message = You hear a crackling laugh echo and clinking bones in the dusty recesses of the station. +psionic-power-precognition-dragon-spawn-result-message = Reality around you bulges and breaks as a great beast cries for war. The smell of salty sea and blood fills the air. +psionic-power-precognition-ninja-spawn-result-message = You see a vision of shadows brought to life, hounds of war howling their cries as they chase it through dark corners of the station. +psionic-power-precognition-revenant-spawn-result-message = The shadows around you grow threefold taller and threefold darker. Something lurks within them, a predator stalking you in the darkness. +psionic-power-precognition-gas-leak-result-message = For but a moment, it feels as if you cannot breathe. With a blink, everything returns to normal. +psionic-power-precognition-kudzu-growth-result-message = Leaves and vines pierce through the dusty tiles of the station, crawling about your ankles, trying to drag you down with them. +psionic-power-precognition-power-grid-check-result-message = You see torches snuff around you only to spontaneously ignite moments later. +psionic-power-precognition-solar-flare-result-message = The stars look beautiful tonight, shrinking and growing and shooting great bolts like fireworks into the sky. +psionic-power-precognition-vent-clog-result-message = You smell something horrific on the artificial breeze of the station, for a moment your eyes fill with fog. When you blink it away, the smell is gone. +psionic-power-precognition-slimes-spawn-result-message = Something lurks deep within the darkest corners of the station, crying for blood. Soft squelches and bubbling howls accompany the call. +psionic-power-precognition-snake-spawn-result-message = Something lurks deep within the darkest corners of the station, crying for blood. The sounds of hissing growls accompany the call. +psionic-power-precognition-spider-spawn-result-message = Something lurks deep within the darkest corners of the station, crying for blood. A symphony of clicks and chitters accompanies the call. +psionic-power-precognition-spider-clown-spawn-result-message = Something lurks deep within the darkest corners of the station, crying for blood. An unholy mass of honks accompanies the call. +psionic-power-precognition-zombie-outbreak-result-message = Your coworker lies on the cold ground before you; skull ripped open, eyes blank. You think you see the body twitch. +psionic-power-precognition-lone-ops-spawn-result-message = You see a vision of a beast with a blood-red carapace, laughing as it eats through the station, bite by bite. +psionic-power-precognition-sleeper-agents-result-message = You see a vision of life through the eyes of a non-descript coworker, a soft but dangerous buzzing accompanies you at the base of their skull. It sounds like radio static. +psionic-power-precognition-mass-hallucinations-result-message = You attempt to see a vision of the future, but all you see is a phantasmagoria of chaotic shapes. +psionic-power-precognition-ion-storm-result-message = You see a vision of the rigid being destroyed and reshaped into something new and wrong. +psionic-power-precognition-meteor-swarm-result-message = You see a fiery vision of shooting stars falling from the sky, colorful trails shooting through the station you call home. +psionic-power-precognition-game-rule-urist-swarm-result-message = You see a fiery vision of... PEOPLE falling from the sky! colorful trails shooting through the station you call home. +psionic-power-precognition-immovable-rod-spawn-result-message = You see a fiery vision of a MASSIVE star falling from the sky! colorful trails shooting through the station you call home. +psionic-power-precognition-mouse-migration-result-message = You see a vision of living as a simplistic creature, scurrying underfoot of creatures beyond your comprehension. +psionic-power-precognition-king-rat-migration-result-message = You see a vision of living as a simplistic creature, scurrying underfoot of creatures beyond your comprehension. +psionic-power-precognition-cockroach-migration-result-message = You see a vision of living as a simplistic creature, scurrying underfoot of creatures beyond your comprehension. +psionic-power-precognition-snail-migration-result-message = You see a vision of living as a simplistic creature, scurrying underfoot of creatures beyond your comprehension. +psionic-power-precognition-random-sentience-result-message = Something bright and beautiful sparks to life within your third eye. Nothing brings wonder quite like new life. +psionic-power-precognition-unknown-shuttle-cargo-lost-result-message = You see a vision of a simple ship of old Terra, adrift of the sea, far away from home. +psionic-power-precognition-unknown-shuttle-traveling-cuisine-result-message = You see a vision of peace, a cozy meal sizzling on a warm stove. A delicious smells wafts through the air. +psionic-power-precognition-unknown-shuttle-disaster-evac-pod-result-message = You see a vision of death and blood, of a destruction so complete only few survive, drifting through the coldness of space. diff --git a/Resources/Locale/en-US/deltav/station-events/station-event-system.ftl b/Resources/Locale/en-US/deltav/station-events/station-event-system.ftl new file mode 100644 index 00000000000..d845303ee26 --- /dev/null +++ b/Resources/Locale/en-US/deltav/station-events/station-event-system.ftl @@ -0,0 +1 @@ +station-event-system-run-event-delayed = Running event {$eventName} in {$seconds} seconds diff --git a/Resources/Prototypes/DeltaV/Actions/types.yml b/Resources/Prototypes/DeltaV/Actions/types.yml index d1c7ac7ca35..8e8218a0798 100644 --- a/Resources/Prototypes/DeltaV/Actions/types.yml +++ b/Resources/Prototypes/DeltaV/Actions/types.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity id: ActionOpenRadioImplant name: Open Radio Implant description: Opens the bluespace key compartment of the radio implant embedded in your skull. @@ -34,3 +34,17 @@ sprite: Structures/Furniture/Tables/generic.rsi state: full event: !type:ToggleCrawlingStateEvent + +- type: entity + id: ActionPrecognition + name: Precognition + description: See into the future to get a hint about the next random event. + components: + - type: InstantAction + icon: + sprite: DeltaV/Interface/Actions/actions_psionics.rsi + state: precognition + useDelay: 240 + checkCanInteract: false + checkConsciousness: false + event: !type:PrecognitionPowerActionEvent diff --git a/Resources/Prototypes/DeltaV/GameRules/events.yml b/Resources/Prototypes/DeltaV/GameRules/events.yml index 9e4496cf0ab..45a6a1d0237 100644 --- a/Resources/Prototypes/DeltaV/GameRules/events.yml +++ b/Resources/Prototypes/DeltaV/GameRules/events.yml @@ -30,6 +30,8 @@ params: volume: -4 duration: null #ending is handled by MeteorSwarmRule + - type: PrecognitionResult + message: psionic-power-precognition-meteor-swarm-result-message - type: MeteorSwarmRule - type: entity @@ -44,6 +46,8 @@ minimumPlayers: 20 weight: 1 duration: 60 + - type: PrecognitionResult + message: psionic-power-precognition-xeno-vents-result-message - type: VentCrittersRule entries: - id: MobXeno @@ -72,6 +76,8 @@ minimumPlayers: 15 weight: 4 duration: 60 + - type: PrecognitionResult + message: psionic-power-precognition-mothroach-spawn-result-message - type: VentCrittersRule entries: - id: MobMothroach @@ -87,6 +93,8 @@ minimumPlayers: 25 maxOccurrences: 1 duration: null + - type: PrecognitionResult + message: psionic-power-precognition-listening-post-result-message - type: RuleGrids - type: LoadFarGridRule path: /Maps/Shuttles/DeltaV/listening_post.yml @@ -146,6 +154,8 @@ components: - type: StationEvent duration: null + - type: PrecognitionResult + message: psionic-power-precognition-paradox-anomaly-result-message - type: ParadoxClonerRule - type: AntagObjectives objectives: @@ -169,6 +179,8 @@ - type: StationEvent minimumPlayers: 40 # it's really easy to find fugitives on lowpop duration: null + - type: PrecognitionResult + message: psionic-power-precognition-fugitive-result-message - type: FugitiveRule - type: AntagLoadProfileRule - type: AntagObjectives diff --git a/Resources/Prototypes/DeltaV/GameRules/glimmer_events.yml b/Resources/Prototypes/DeltaV/GameRules/glimmer_events.yml index dc940422f8c..2d972f9809a 100644 --- a/Resources/Prototypes/DeltaV/GameRules/glimmer_events.yml +++ b/Resources/Prototypes/DeltaV/GameRules/glimmer_events.yml @@ -17,6 +17,7 @@ parent: BaseGameRule id: GlimmerEventScheduler components: + - type: NextEvent - type: BasicStationEventScheduler minMaxEventTiming: min: 300 diff --git a/Resources/Prototypes/DeltaV/GameRules/unknown_shuttles.yml b/Resources/Prototypes/DeltaV/GameRules/unknown_shuttles.yml index 2959bebcc0b..38ec9d0df36 100644 --- a/Resources/Prototypes/DeltaV/GameRules/unknown_shuttles.yml +++ b/Resources/Prototypes/DeltaV/GameRules/unknown_shuttles.yml @@ -14,6 +14,8 @@ minimumPlayers: 20 maxOccurrences: 1 duration: null + - type: PrecognitionResult + message: psionic-power-precognition-syndicate-recruiter-result-message - type: RuleGrids - type: LoadMapRule preloadedGrid: SyndieRecruiterShip @@ -56,6 +58,8 @@ minimumPlayers: 20 maxOccurrences: 1 duration: null + - type: PrecognitionResult + message: psionic-power-precognition-synthesis-specialist-result-message - type: RuleGrids - type: LoadMapRule preloadedGrid: SyndieSynthesisShip diff --git a/Resources/Prototypes/GameRules/cargo_gifts.yml b/Resources/Prototypes/GameRules/cargo_gifts.yml index f4e4a5bf8fc..001334162ec 100644 --- a/Resources/Prototypes/GameRules/cargo_gifts.yml +++ b/Resources/Prototypes/GameRules/cargo_gifts.yml @@ -26,6 +26,8 @@ delay: min: 10 max: 10 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-cargo-gifts-base-result-message - type: StationEvent startColor: "#18abf5" startAudio: diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 698ee7d4774..b22bc621c80 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -83,6 +83,8 @@ path: /Audio/Announcements/announce.ogg weight: 12 # DeltaV - was 8 duration: 35 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-anomoly-spawn-result-message - type: AnomalySpawnRule - type: entity @@ -99,6 +101,8 @@ path: /Audio/Announcements/announce.ogg weight: 8 duration: 35 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-bluespace-artifact-result-message - type: BluespaceArtifactRule - type: entity @@ -110,6 +114,8 @@ reoccurrenceDelay: 5 earliestStart: 1 duration: 1 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-bluespace-locker-result-message - type: BluespaceLockerRule - type: entity @@ -120,6 +126,8 @@ weight: 10 duration: 1 minimumPlayers: 15 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-breaker-flip-result-message - type: BreakerFlipRule - type: entity @@ -133,6 +141,8 @@ minimumPlayers: 25 weight: 5 duration: 1 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-bureaucratic-error-result-message - type: BureaucraticErrorRule ignoredJobs: - StationAi @@ -146,6 +156,8 @@ minimumPlayers: 15 weight: 5 duration: 1 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-clerical-error-result-message - type: ClericalErrorRule - type: entity @@ -156,6 +168,8 @@ weight: 8 # DeltaV - was 10 duration: 1 minimumPlayers: 10 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-closet-skeleton-result-message - type: RandomEntityStorageSpawnRule prototype: MobSkeletonCloset @@ -169,6 +183,8 @@ reoccurrenceDelay: 20 minimumPlayers: 45 # DeltaV - was 20 duration: null + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-dragon-spawn-result-message - type: SpaceSpawnRule spawnDistance: 0 - type: AntagSpawner @@ -198,6 +214,8 @@ earliestStart: 30 reoccurrenceDelay: 60 # DeltaV - was 20 minimumPlayers: 40 # DeltaV - was 30 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-ninja-spawn-result-message - type: SpaceSpawnRule - type: AntagLoadProfileRule - type: AntagObjectives @@ -253,6 +271,8 @@ duration: 1 earliestStart: 45 minimumPlayers: 20 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-revenant-spawn-result-message - type: RandomSpawnRule prototype: MobRevenant @@ -277,6 +297,8 @@ path: /Audio/Announcements/gas_leak.ogg # DeltaV - custom announcer endAnnouncement: station-event-gas-leak-end-announcement weight: 10 # DeltaV - was 8 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-gas-leak-result-message - type: GasLeakRule - type: entity @@ -288,6 +310,8 @@ minimumPlayers: 10 # DeltaV - Was 15 weight: 5 # DeltaV - was 7 duration: 240 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-kudzu-growth-result-message - type: KudzuGrowthRule - type: entity @@ -304,6 +328,8 @@ volume: -4 duration: 60 maxDuration: 120 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-power-grid-check-result-message - type: PowerGridCheckRule - type: entity @@ -318,6 +344,8 @@ path: /Audio/Announcements/attention.ogg duration: 120 maxDuration: 240 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-solar-flare-result-message - type: SolarFlareRule onlyJamHeadsets: true affectedChannels: @@ -346,6 +374,8 @@ minimumPlayers: 15 weight: 7.5 # DeltaV - was 5 duration: 60 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-vent-clog-result-message - type: VentClogRule - type: entity @@ -360,6 +390,8 @@ minimumPlayers: 15 weight: 5 duration: 60 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-slimes-spawn-result-message - type: VentCrittersRule entries: - id: MobAdultSlimesBlueAngry @@ -381,6 +413,8 @@ minimumPlayers: 15 weight: 5 duration: 60 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-snake-spawn-result-message - type: VentCrittersRule entries: - id: MobPurpleSnake @@ -402,6 +436,8 @@ minimumPlayers: 15 weight: 5 duration: 60 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-spider-spawn-result-message - type: VentCrittersRule entries: - id: MobGiantSpiderAngry @@ -419,6 +455,8 @@ minimumPlayers: 30 # DeltaV - was 20 weight: 1 # DeltaV - was 1.5 duration: 60 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-spider-clown-spawn-result-message - type: VentCrittersRule entries: - id: MobClownSpider @@ -433,6 +471,8 @@ minimumPlayers: 40 weight: 1 # Zombies was happening basically every single survival round, so now it's super rare duration: 1 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-zombie-outbreak-result-message - type: ZombieRule - type: AntagSelection definitions: @@ -467,6 +507,8 @@ minimumPlayers: 30 # DeltaV - was 20 reoccurrenceDelay: 30 duration: 1 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-lone-ops-spawn-result-message - type: RuleGrids - type: LoadMapRule mapPath: /Maps/Shuttles/ShuttleEvent/striker.yml @@ -507,6 +549,8 @@ path: /Audio/Announcements/attention.ogg # DeltaV - Use the generic announcement sound duration: null # the rule has to last the whole round not 1 second occursDuringRoundEnd: false + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-sleeper-agents-result-message - type: AlertLevelInterceptionRule - type: AntagSelection definitions: @@ -529,6 +573,8 @@ weight: 10 duration: 150 maxDuration: 300 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-mass-hallucinations-result-message - type: MassHallucinationsRule minTimeBetweenIncidents: 30 # DeltaV - was 0.1 maxTimeBetweenIncidents: 300 @@ -544,6 +590,8 @@ weight: 10 reoccurrenceDelay: 30 #DeltaV - Was 20 #20 mins feels too short, and can scramble borgs way too much duration: 1 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-ion-storm-result-message - type: IonStormRule # DeltaV - Disable Mimic event. It causes a ton of heisentests, and does practially nothing in game diff --git a/Resources/Prototypes/GameRules/meteorswarms.yml b/Resources/Prototypes/GameRules/meteorswarms.yml index 95f9985b6a2..ab8bf899204 100644 --- a/Resources/Prototypes/GameRules/meteorswarms.yml +++ b/Resources/Prototypes/GameRules/meteorswarms.yml @@ -55,6 +55,7 @@ parent: BaseGameRule id: MeteorSwarmScheduler components: + - type: NextEvent # DeltaV: Queue Event - type: GameRule - type: BasicStationEventScheduler minimumTimeUntilFirstEvent: 600 # 10 min @@ -68,6 +69,7 @@ parent: BaseGameRule id: MeteorSwarmMildScheduler components: + - type: NextEvent # DeltaV: Queue Event - type: GameRule - type: BasicStationEventScheduler minimumTimeUntilFirstEvent: 600 # 10 min @@ -82,6 +84,7 @@ parent: BaseGameRule id: KesslerSyndromeScheduler components: + - type: NextEvent # DeltaV: Queue Event - type: GameRule - type: RampingStationEventScheduler scheduledGameRules: !type:NestedSelector @@ -99,6 +102,8 @@ reoccurrenceDelay: 1 earliestStart: 12 minimumPlayers: 25 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-meteor-swarm-result-message - type: MeteorSwarm - type: entity @@ -184,6 +189,8 @@ components: - type: StationEvent weight: 0.05 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-game-rule-urist-swarm-result-message - type: MeteorSwarm announcement: station-event-meteor-urist-start-announcement announcementSound: /Audio/Announcements/attention.ogg @@ -209,6 +216,8 @@ duration: 1 earliestStart: 30 minimumPlayers: 25 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-immovable-rod-spawn-result-message - type: ImmovableRodRule rodPrototypes: - id: ImmovableRodKeepTilesStill diff --git a/Resources/Prototypes/GameRules/pests.yml b/Resources/Prototypes/GameRules/pests.yml index 354a7423a9c..0bc1e582e37 100644 --- a/Resources/Prototypes/GameRules/pests.yml +++ b/Resources/Prototypes/GameRules/pests.yml @@ -28,6 +28,8 @@ earliestStart: 15 weight: 6 duration: 50 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-mouse-migration-result-message - type: VentCrittersRule entries: - id: MobMouse @@ -52,6 +54,8 @@ weight: 6 duration: 50 minimumPlayers: 30 # Hopefully this is enough for the Rat King's potential Army (it was not, raised from 15 -> 30) + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-king-rat-migration-result-message - type: VentCrittersRule entries: - id: MobMouse @@ -76,6 +80,8 @@ path: /Audio/Announcements/attention.ogg weight: 6 duration: 50 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-cockroach-migration-result-message - type: VentCrittersRule entries: - id: MobCockroach @@ -93,6 +99,8 @@ path: /Audio/Announcements/attention.ogg weight: 6 duration: 50 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-snail-migration-result-message - type: VentCrittersRule entries: - id: MobSnail @@ -114,6 +122,8 @@ weight: 6 duration: 50 minimumPlayers: 30 + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-snail-migration-result-message - type: VentCrittersRule entries: - id: MobSnail diff --git a/Resources/Prototypes/GameRules/random_sentience.yml b/Resources/Prototypes/GameRules/random_sentience.yml index a2c749000a6..eec41bf45ec 100644 --- a/Resources/Prototypes/GameRules/random_sentience.yml +++ b/Resources/Prototypes/GameRules/random_sentience.yml @@ -8,6 +8,8 @@ maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it startAudio: path: /Audio/Announcements/attention.ogg + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-random-sentience-result-message - type: RandomSentienceRule minSentiences: 2 maxSentiences: 5 @@ -22,4 +24,4 @@ id: RandomSentienceEventStrength values: prefix: random-sentience-event-strength- - count: 8 \ No newline at end of file + count: 8 diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 50de7c4b746..dac93f951f5 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -313,6 +313,7 @@ id: BasicStationEventScheduler parent: BaseGameRule components: + - type: NextEvent # DeltaV: Queue Event for precognition - type: BasicStationEventScheduler minMaxEventTiming: # DeltaV min: 300 # DeltaV - 5 mins, was 3 @@ -324,6 +325,7 @@ id: RampingStationEventScheduler parent: BaseGameRule components: + - type: NextEvent # DeltaV: Queue Event for precognition - type: RampingStationEventScheduler averageChaos: 4.5 # DeltaV averageEndTime: 180 # DeltaV @@ -334,6 +336,7 @@ id: SpaceTrafficControlEventScheduler # iff we make a selector for EntityTables that can respect StationEventComp restrictions, or somehow impliment them otherwise in said tables, parent: BaseGameRule # we can remerge this with the other schedulers, but it will silently fail due to that limitation without a separate scheduler to balance atm. components: + - type: NextEvent # DeltaV: Queue Event for precognition - type: BasicStationEventScheduler minimumTimeUntilFirstEvent: 2700 # 45 mins #shows up like half way through shift. minMaxEventTiming: @@ -346,6 +349,7 @@ id: SpaceTrafficControlFriendlyEventScheduler parent: BaseGameRule components: + - type: NextEvent # DeltaV: Queue Event for precognition - type: BasicStationEventScheduler minimumTimeUntilFirstEvent: 1200 # 20 mins minMaxEventTiming: diff --git a/Resources/Prototypes/GameRules/unknown_shuttles.yml b/Resources/Prototypes/GameRules/unknown_shuttles.yml index c2962e25072..e5c939edbb3 100644 --- a/Resources/Prototypes/GameRules/unknown_shuttles.yml +++ b/Resources/Prototypes/GameRules/unknown_shuttles.yml @@ -59,6 +59,8 @@ components: - type: StationEvent maxOccurrences: 2 # should be the same as [copies] in shuttle_incoming_event.yml + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-unknown-shuttle-cargo-lost-result-message - type: LoadMapRule preloadedGrid: ShuttleCargoLost @@ -69,6 +71,8 @@ - type: StationEvent startAnnouncement: station-event-unknown-shuttle-incoming maxOccurrences: 2 # should be the same as [copies] in shuttle_incoming_event.yml + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-unknown-shuttle-traveling-cuisine-result-message - type: LoadMapRule preloadedGrid: TravelingCuisine @@ -79,6 +83,8 @@ - type: StationEvent startAnnouncement: station-event-unknown-shuttle-incoming maxOccurrences: 3 # should be the same as [copies] in shuttle_incoming_event.yml + - type: PrecognitionResult # DeltaV - Precogniton + message: psionic-power-precognition-unknown-shuttle-disaster-evac-pod-result-message - type: LoadMapRule preloadedGrid: DisasterEvacPod diff --git a/Resources/Prototypes/Nyanotrasen/psionicPowers.yml b/Resources/Prototypes/Nyanotrasen/psionicPowers.yml index 4dff75d9867..145d77a245d 100644 --- a/Resources/Prototypes/Nyanotrasen/psionicPowers.yml +++ b/Resources/Prototypes/Nyanotrasen/psionicPowers.yml @@ -5,6 +5,7 @@ DispelPower: 1 TelegnosisPower: 1 PsionicRegenerationPower: 1 + PrecognitionPower: 1 # DeltaV MassSleepPower: 0.3 NoosphericZapPower: 0.3 # PsionicInvisibilityPower: 0.15 diff --git a/Resources/Textures/DeltaV/Interface/Actions/actions_psionics.rsi/meta.json b/Resources/Textures/DeltaV/Interface/Actions/actions_psionics.rsi/meta.json new file mode 100644 index 00000000000..e0c9ccac8c5 --- /dev/null +++ b/Resources/Textures/DeltaV/Interface/Actions/actions_psionics.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-4.0", + "copyright": "Sprited by chamomileteatime on Discord for DeltaV", + "size": { + "x": 64, + "y": 64 + }, + "states": [ + { + "name": "precognition" + } + ] +} diff --git a/Resources/Textures/DeltaV/Interface/Actions/actions_psionics.rsi/precognition.png b/Resources/Textures/DeltaV/Interface/Actions/actions_psionics.rsi/precognition.png new file mode 100644 index 00000000000..1bf396261e9 Binary files /dev/null and b/Resources/Textures/DeltaV/Interface/Actions/actions_psionics.rsi/precognition.png differ