-
Notifications
You must be signed in to change notification settings - Fork 330
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New psionic ablility: Precognition (#2131)
* TESTING enable events for dev enviroment * Add NextEventComponent * Check for schedulers NextEventComponent * Seperate gernateing event to its own method * Add NextEventSystem and use in BasicStationEventSchedulerSystem * TESTING: Override time and player restrictions * Stash events in NextEventComponent (#1) * Add NextEventComponent * Check for schedulers NextEventComponent * Seperate gernateing event to its own method * Add NextEventSystem and use in BasicStationEventSchedulerSystem * Format code * Add nextEvent time perdiction * Use RunTime instead of float minutes * Bug fixes * Add NextEvent to Ramping and Meteors * Fix timing on BasticStationEvents * initialize NextEventComponent when created * Event scheduler caching (#2) * Check for schedulers NextEventComponent * Seperate gernateing event to its own method * Add NextEventSystem and use in BasicStationEventSchedulerSystem * Format code * Add nextEvent time perdiction * Use RunTime instead of float minutes * Bug fixes * Add NextEvent to Ramping and Meteors * Fix timing on BasticStationEvents * initialize NextEventComponent when created --------- Signed-off-by: SolStar <44028047+ewokswagger@users.noreply.github.com> * Revert "Event scheduler caching (#2)" This reverts commit bf9cd26. * Revert "Merge branch 'seer' into stash-next-event" This reverts commit 656ca26, reversing changes made to 36f45be. * Revert "Caching next exent" This reverts commit 9f1bee4, reversing changes made to 82678d9. * Reapply "Event scheduler caching (#2)" This reverts commit 82678d9. * More merge conflict nonsence * oops * oops 2 * Oops 3 * Precognition Psionic ability (#3) * Precognition ability added * Precog get next event * Get soonest event and display * update prototypes with precog results * Add random * Use Timespan for UseDelay * Damage breaks doafter * typo * fix localization * fix do after * Add effects durring do after * Revert "TESTING enable events for dev enviroment" This reverts commit 0345313. * Revert testing changes * add deltav comments * Cleaning up! * Move NextEvent to server space * Fix NextEventId init value * Reverted upstream file to block scoped namespace * Add precognitnon result messages * reverting testing changes for real * Add admin alert for upcoming events * Add sound effect * make alert more subtule * extended max window size * fix message mixup * yaml fixes * more yaml fixes * Delta Changes * totaly a yaml error trust * remove unsessesary weights --------- Signed-off-by: SolStar <44028047+ewokswagger@users.noreply.github.com>
- Loading branch information
1 parent
6e72bde
commit d5716a8
Showing
26 changed files
with
637 additions
and
16 deletions.
There are no files selected for viewing
232 changes: 232 additions & 0 deletions
232
Content.Server/DeltaV/Abilities/Psionics/PrecognitionPowerSystem.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<PrecognitionPowerComponent, MapInitEvent>(OnMapInit); | ||
SubscribeLocalEvent<PrecognitionPowerComponent, ComponentShutdown>(OnShutdown); | ||
SubscribeLocalEvent<PrecognitionPowerComponent, PrecognitionPowerActionEvent>(OnPowerUsed); | ||
SubscribeLocalEvent<PrecognitionPowerComponent, PrecognitionDoAfterEvent>(OnDoAfter); | ||
} | ||
|
||
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) | ||
{ | ||
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<PsionicComponent>(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<TemporaryBlindnessComponent>(uid, "TemporaryBlindness", component.UseDelay, true); | ||
_statusEffects.TryAddStatusEffect<SlowedDownComponent>(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; | ||
} | ||
|
||
/// <summary> | ||
/// Upon completion will send a message to the user corrosponding to the next station event to occour. | ||
/// </summary> | ||
/// <param name="uid"></param> | ||
/// <param name="component"></param> | ||
/// <param name="args"></param> | ||
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; | ||
} | ||
|
||
/// <summary> | ||
/// 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) | ||
{ | ||
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; | ||
} | ||
|
||
/// <summary> | ||
/// </summary> | ||
/// <returns>The localized string of a weighted randomly chosen precognition result</returns> | ||
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; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the soonest nextEvent to occur within the window. | ||
/// </summary> | ||
/// <param name="minDetectWindow"></param> The earliest reletive time that will be return a nextEvent | ||
/// <param name="maxDetectWindow"></param> The latest reletive latest time that will be return a nextEvent | ||
/// <returns>Component for the next event to occour if one exists in the window.</returns> | ||
private NextEventComponent? FindEarliestNextEvent(TimeSpan minDetectWindow, TimeSpan maxDetectWindow) | ||
{ | ||
TimeSpan? earliestNextEventTime = null; | ||
NextEventComponent? earliestNextEvent = null; | ||
var query = EntityQueryEnumerator<NextEventComponent>(); | ||
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<EntityPrototype, PrecognitionResultComponent> GetAllPrecognitionResults() | ||
{ | ||
var allEvents = new Dictionary<EntityPrototype, PrecognitionResultComponent>(); | ||
foreach (var prototype in _prototype.EnumeratePrototypes<EntityPrototype>()) | ||
{ | ||
if (prototype.Abstract) | ||
continue; | ||
|
||
if (!prototype.TryGetComponent<PrecognitionResultComponent>(out var precognitionResult, _factory)) | ||
continue; | ||
|
||
allEvents.Add(prototype, precognitionResult); | ||
} | ||
|
||
return allEvents; | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
Content.Server/DeltaV/StationEvents/NextEvent/NextEventComponent.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using Robust.Shared.Prototypes; | ||
|
||
namespace Content.Server.DeltaV.StationEvents.NextEvent; | ||
|
||
[RegisterComponent, Access(typeof(NextEventSystem))] | ||
public sealed partial class NextEventComponent : Component | ||
{ | ||
/// <summary> | ||
/// Id of the next event that will be run by EventManagerSystem. | ||
/// </summary> | ||
[DataField] | ||
public EntProtoId? NextEventId; | ||
|
||
/// <summary> | ||
/// Round time of the scheduler's next station event. | ||
/// </summary> | ||
[DataField] | ||
public TimeSpan NextEventTime; | ||
} |
18 changes: 18 additions & 0 deletions
18
Content.Server/DeltaV/StationEvents/NextEvent/NextEventSystem.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
{ | ||
/// <summary> | ||
/// Updates the NextEventComponent with the provided id and time and returns the previously stored id. | ||
/// </summary> | ||
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.