Files
crystall-punk-14/Content.Server/_CP14/Religion/CP14ReligionSystem.cs
2025-08-25 17:00:57 +03:00

300 lines
9.8 KiB
C#

using Content.Server._CP14.MagicEnergy;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Server.Speech;
using Content.Shared._CP14.MagicEnergy.Components;
using Content.Shared._CP14.Religion.Components;
using Content.Shared._CP14.Religion.Prototypes;
using Content.Shared._CP14.Religion.Systems;
using Content.Shared.Chat;
using Content.Shared.Eye;
using Content.Shared.FixedPoint;
using Content.Shared.Speech;
using Robust.Server.GameObjects;
using Robust.Server.GameStates;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server._CP14.Religion;
public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
{
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly ChatSystem _chatSys = default!;
[Dependency] private readonly PvsOverrideSystem _pvs = default!;
[Dependency] private readonly CP14MagicEnergySystem _magicEnergy = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedEyeSystem _eye = default!;
private EntityQuery<CP14ReligionEntityComponent> _godQuery;
/// <summary>
/// If ReligionObserver receives a radius higher than this value, this entity will automatically be placed in PvsOverride for the god in order to function correctly outside of the player's PVS.
/// </summary>
/// <remarks> Maybe there is a variable for the distance outside the screen in PVS, I don't know. This number works best</remarks>
private const float ObservationOverrideRadius = 6.5f;
public override void Initialize()
{
base.Initialize();
InitializeUI();
_godQuery = GetEntityQuery<CP14ReligionEntityComponent>();
SubscribeLocalEvent<CP14ReligionObserverComponent, ComponentInit>(OnObserverInit);
SubscribeLocalEvent<CP14ReligionObserverComponent, AfterAutoHandleStateEvent>(OnObserverHandleState);
SubscribeLocalEvent<CP14ReligionEntityComponent, ComponentInit>(OnGodInit);
SubscribeLocalEvent<CP14ReligionEntityComponent, ComponentShutdown>(OnGodShutdown);
SubscribeLocalEvent<CP14ReligionEntityComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<CP14ReligionEntityComponent, PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<CP14ReligionSpeakerComponent, CP14SpokeAttemptEvent>(OnSpokeAttempt);
SubscribeLocalEvent<ExpandICChatRecipientsEvent>(OnExpandRecipients);
SubscribeLocalEvent<CP14ReligionAltarComponent, ListenEvent>(OnListen);
SubscribeLocalEvent<CP14ReligionEntityComponent, GetVisMaskEvent>(OnGetVis);
}
private void OnGetVis(Entity<CP14ReligionEntityComponent> ent, ref GetVisMaskEvent args)
{
args.VisibilityMask |= (int)VisibilityFlags.Ghost;
}
private void OnExpandRecipients(ExpandICChatRecipientsEvent ev)
{
foreach (var recipient in ev.Recipients)
{
var recipientEntity = recipient.Key.AttachedEntity;
if (!_godQuery.TryComp(recipientEntity, out var god) || god.Religion is null)
continue;
if (!InVision(ev.Source, (recipientEntity.Value, god)))
{
// If the recipient is not in vision, we don't want to send them the message.
ev.Recipients.Remove(recipient.Key);
}
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<CP14ReligionFollowerComponent, CP14MagicEnergyContainerComponent>();
while (query.MoveNext(out var uid, out var follower, out var energy))
{
if (follower.NextUpdateTime >= _gameTiming.CurTime)
continue;
if (follower.Religion is null)
continue;
follower.NextUpdateTime = _gameTiming.CurTime + TimeSpan.FromSeconds(follower.ManaTransferDelay);
foreach (var god in GetGods(follower.Religion.Value))
{
_magicEnergy.TransferEnergy((uid, energy),
god.Owner,
follower.EnergyToGodTransfer,
out _,
out _,
safe: true);
}
}
}
private void OnSpokeAttempt(Entity<CP14ReligionSpeakerComponent> ent, ref CP14SpokeAttemptEvent args)
{
args.Cancel();
if (!_godQuery.TryComp(ent, out var god) || god.Religion is null)
return;
if (ent.Comp.RestrictedReligionZone && !InVision(ent, (ent, god)))
return;
_magicEnergy.ChangeEnergy(ent.Owner, -ent.Comp.ManaCost, out _, out _);
var speaker = Spawn(ent.Comp.Speaker);
_transform.DropNextTo(speaker, ent.Owner);
var message = args.Message;
var type = args.Type;
Timer.Spawn(333,
() =>
{
_chatSys.TrySendInGameICMessage(speaker,
message,
type,
ChatTransmitRange.Normal,
nameOverride: MetaData(ent).EntityName,
ignoreActionBlocker: true);
});
}
private void OnObserverHandleState(Entity<CP14ReligionObserverComponent> ent, ref AfterAutoHandleStateEvent args)
{
var query = EntityQueryEnumerator<CP14ReligionEntityComponent>();
while (query.MoveNext(out var uid, out var god))
{
UpdatePvsOverrides(new Entity<CP14ReligionEntityComponent>(uid, god));
}
}
private void OnObserverInit(Entity<CP14ReligionObserverComponent> ent, ref ComponentInit args)
{
if (ent.Comp.Religion is null)
return;
var gods = GetGods(ent.Comp.Religion.Value);
foreach (var god in gods)
{
UpdatePvsOverrides(god);
}
}
private void OnGodInit(Entity<CP14ReligionEntityComponent> ent, ref ComponentInit args)
{
AddPvsOverrides(ent);
_eye.RefreshVisibilityMask(ent.Owner);
}
private void OnGodShutdown(Entity<CP14ReligionEntityComponent> ent, ref ComponentShutdown args)
{
RemovePvsOverrides(ent);
_eye.RefreshVisibilityMask(ent.Owner);
}
private void OnPlayerAttached(Entity<CP14ReligionEntityComponent> ent, ref PlayerAttachedEvent args)
{
AddPvsOverrides(ent);
}
private void OnPlayerDetached(Entity<CP14ReligionEntityComponent> ent, ref PlayerDetachedEvent args)
{
RemovePvsOverrides(ent);
}
private void OnListen(Entity<CP14ReligionAltarComponent> ent, ref ListenEvent args)
{
if (ent.Comp.Religion is null)
return;
var wrappedMessage =
Loc.GetString("cp14-altar-wrapped-message",
("name", MetaData(args.Source).EntityName),
("msg", args.Message));
SendMessageToGods(ent.Comp.Religion.Value, wrappedMessage, args.Source);
}
public override void SendMessageToGods(ProtoId<CP14ReligionPrototype> religion, string msg, EntityUid source)
{
var gods = GetGods(religion);
HashSet<INetChannel> channels = new();
foreach (var god in gods)
{
if (!TryComp<ActorComponent>(god, out var godActor))
continue;
channels.Add(godActor.PlayerSession.Channel);
}
_chat.ChatMessageToMany(ChatChannel.Notifications,
msg,
msg,
source,
false,
true,
channels,
colorOverride: Color.Aqua);
}
public FixedPoint2 GetFollowerPercentage(Entity<CP14ReligionEntityComponent> god)
{
FixedPoint2 total = 0;
FixedPoint2 followers = 0;
var allHumans = Mind.GetAliveHumans();
foreach (var human in allHumans)
{
total += 1;
if (!TryComp<CP14ReligionFollowerComponent>(human.Comp.CurrentEntity, out var relFollower))
continue;
if (relFollower.Religion != god.Comp.Religion)
continue;
followers += 1;
}
if (total == 0)
return 0f;
return followers / total;
}
private void AddPvsOverrides(Entity<CP14ReligionEntityComponent> ent)
{
if (ent.Comp.Religion is null)
return;
if (!TryComp<ActorComponent>(ent, out var actor))
return;
ent.Comp.Session = actor.PlayerSession;
var query = EntityQueryEnumerator<CP14ReligionObserverComponent>();
while (query.MoveNext(out var uid, out var observer))
{
if (!observer.Active)
continue;
if (observer.Radius <= ObservationOverrideRadius)
continue;
ent.Comp.PvsOverridedObservers.Add(uid);
_pvs.AddSessionOverride(uid, actor.PlayerSession);
}
}
private void RemovePvsOverrides(Entity<CP14ReligionEntityComponent> ent)
{
if (ent.Comp.Religion is null)
return;
if (ent.Comp.Session is null)
return;
foreach (var observer in ent.Comp.PvsOverridedObservers)
{
_pvs.RemoveSessionOverride(observer, ent.Comp.Session);
}
ent.Comp.Session = null;
ent.Comp.PvsOverridedObservers.Clear();
}
private void UpdatePvsOverrides(Entity<CP14ReligionEntityComponent> ent)
{
if (ent.Comp.Session is null)
return;
RemovePvsOverrides(ent);
AddPvsOverrides(ent);
}
}
public sealed class CP14SpokeAttemptEvent(string message, InGameICChatType type, ICommonSession? player)
: CancellableEntityEventArgs
{
public string Message = message;
public InGameICChatType Type = type;
public ICommonSession? Player = player;
}