Files
OldThink/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs
withoutcode333 0e4e5b7805 Upstream core (#282)
* yes

(cherry picked from commit a6b5e1c66dfe4241977bcde753af594392164eca)

* Is real, navernoe (#944)

* its real

* fix shield

* remove comments game preset

* maximum predicted

* fixes

* АААААААААААААААААААААААААААА ПОМОГИТЕ Я ЕБНУЛСЯ ПОКА ФИКСИЛ ЭТУ ЗАЛУПУ

* govnoedit

* secret

(cherry picked from commit 22c7b68048590b5098efbfff0d0f5205d3a64c48)

* [Feature/Tweaks] Raznoe (#934)

* make thruster great again

* make hardsuit hos great again

* new ficha for medical hud

* fix

* vrode da

* GOOOVNO REMIX REVERB EXTRA

* fix

* правки

---------

Co-authored-by: BIGZi0348 <svalker0348@gmail.com>
(cherry picked from commit 141e61a0449873842f46d83eff9e9ce857147d60)

* Automatic changelog update

(cherry picked from commit d14fe5fb6c934ed522df0b5bc453e4c04707a6db)

* [Feature] Executions (#932)

* based

* cleanup

* cleanup + fixes

* fix

* fix

* fix ftl

* Update Resources/Locale/ru-RU/_white/executions/execution.ftl

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update execution.ftl

* правки

* vrode norm

* da

---------

Co-authored-by: BIGZi0348 <118811750+BIGZi0348@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: BIGZi0348 <svalker0348@gmail.com>
(cherry picked from commit 83e164172f8e290acee7634f14ac51281be020ad)

* Automatic changelog update

(cherry picked from commit 71f907c563a30a1fc7ef5751a4d6f2c780a14f4c)

* hotfix (#946)

(cherry picked from commit f577caec41ab277ee8fc1c18fe64f7e26a6e50f5)

---------

Co-authored-by: RavmorganButOnCocaine <valtos@nextmail.ru>
2025-04-13 19:03:49 +05:00

1399 lines
45 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Linq;
using System.Numerics;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Chat.Systems;
using Content.Server.Body.Systems;
using Content.Server.DoAfter;
using Content.Server.Hands.Systems;
using Content.Server.Weapons.Ranged.Systems;
using Content.Server._White.Cult.GameRule;
using Content.Server._White.Cult.Runes.Comps;
using Content.Server._White.Cult.UI;
using Content.Server.Antag;
using Content.Server.Bible.Components;
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Fluids.Components;
using Content.Server.Ghost;
using Content.Server.Pinpointer;
using Content.Server.Revenant.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Cuffs.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.DoAfter;
using Content.Shared.Interaction;
using Content.Shared.Humanoid;
using Content.Shared.Interaction.Events;
using Content.Shared.Maps;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Popups;
using Content.Shared.Projectiles;
using Content.Shared.Rejuvenate;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.Runes;
using Content.Shared._White.Cult.UI;
using Content.Shared.Cuffs;
using Content.Shared.GameTicking;
using Content.Shared.Mindshield.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.UserInterface;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Physics.Events;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent;
namespace Content.Server._White.Cult.Runes.Systems;
public sealed partial class CultSystem : EntitySystem
{
[Dependency] private readonly EntityManager _entityManager = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedTransformSystem _xform = default!;
[Dependency] private readonly CultRuleSystem _ruleSystem = default!;
[Dependency] private readonly HandsSystem _handsSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly GunSystem _gunSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
[Dependency] private readonly PullingSystem _pulling = default!;
[Dependency] private readonly SharedCuffableSystem _cuffable = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly MobThresholdSystem _thresholdSystem = default!;
[Dependency] private readonly NavMapSystem _navMap = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
public override void Initialize()
{
base.Initialize();
// Runes
SubscribeLocalEvent<CultRuneBaseComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<CultRuneOfferingComponent, CultRuneInvokeEvent>(OnInvokeOffering);
SubscribeLocalEvent<CultRuneBuffComponent, CultRuneInvokeEvent>(OnInvokeBuff);
SubscribeLocalEvent<CultRuneTeleportComponent, CultRuneInvokeEvent>(OnInvokeTeleport);
SubscribeLocalEvent<CultRuneApocalypseComponent, CultRuneInvokeEvent>(OnInvokeApocalypse);
SubscribeLocalEvent<CultRuneReviveComponent, CultRuneInvokeEvent>(OnInvokeRevive);
SubscribeLocalEvent<CultRuneBarrierComponent, CultRuneInvokeEvent>(OnInvokeBarrier);
SubscribeLocalEvent<CultRuneSummoningComponent, CultRuneInvokeEvent>(OnInvokeSummoning);
SubscribeLocalEvent<CultRuneBloodBoilComponent, CultRuneInvokeEvent>(OnInvokeBloodBoil);
SubscribeLocalEvent<CultistComponent, SummonNarsieDoAfterEvent>(NarsieSpawn);
SubscribeLocalEvent<CultEmpowerComponent, CultEmpowerSelectedBuiMessage>(OnEmpowerSelected);
SubscribeLocalEvent<CultEmpowerComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<CultEmpowerComponent, CultRuneInvokeEvent>(OnActiveInWorld);
// UI
SubscribeLocalEvent<RuneDrawerProviderComponent, ActivatableUIOpenAttemptEvent>(OnRuneDrawAttempt);
SubscribeLocalEvent<RuneDrawerProviderComponent, BeforeActivatableUIOpenEvent>(BeforeRuneDraw);
SubscribeLocalEvent<RuneDrawerProviderComponent, ListViewItemSelectedMessage>(OnRuneSelected);
SubscribeLocalEvent<CultTeleportRuneProviderComponent, TeleportRunesListWindowItemSelectedMessage>(OnTeleportRuneSelected);
SubscribeLocalEvent<CultRuneSummoningProviderComponent, SummonCultistListWindowItemSelectedMessage>(OnCultistSelected);
// Rune drawing/erasing
SubscribeLocalEvent<CultistComponent, CultDrawEvent>(OnDraw);
SubscribeLocalEvent<CultistComponent, NameSelectorMessage>(OnChoose);
SubscribeLocalEvent<CultRuneBaseComponent, InteractUsingEvent>(TryErase);
SubscribeLocalEvent<CultRuneBaseComponent, CultEraseEvent>(OnErase);
SubscribeLocalEvent<CultRuneBaseComponent, StartCollideEvent>(HandleCollision);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
InitializeBuffSystem();
InitializeNarsie();
InitializeSoulShard();
InitializeConstructs();
InitializeBarrierSystem();
InitializeConstructsAbilities();
InitializeActions();
InitializeSpells();
}
private float _timeToDraw;
private const string TeleportRunePrototypeId = "TeleportRune";
private const string ApocalypseRunePrototypeId = "ApocalypseRune";
private const string RitualDaggerPrototypeId = "RitualDagger";
private const string RunicMetalPrototypeId = "CultRunicMetal";
private const string SteelPrototypeId = "Steel";
private const string NarsiePrototypeId = "Narsie";
private const string CultBarrierPrototypeId = "CultBarrier";
private bool _doAfterAlreadyStarted;
private readonly SoundPathSpecifier _teleportInSound = new("/Audio/White/Cult/veilin.ogg");
private readonly SoundPathSpecifier _teleportOutSound = new("/Audio/White/Cult/veilout.ogg");
private readonly SoundPathSpecifier _magic = new("/Audio/White/Cult/magic.ogg");
private readonly SoundPathSpecifier _apocRuneEndDrawing = new("/Audio/White/Cult/finisheddraw.ogg");
private readonly SoundPathSpecifier _narsie40Sec = new("/Audio/White/Cult/40sec.ogg");
private Entity<AudioComponent>? _narsieSummonningAudio;
private void OnRoundRestart(RoundRestartCleanupEvent ev)
{
CultRuneReviveComponent.ChargesLeft = 3;
}
/*
* Rune draw start ----
*/
private void OnRuneDrawAttempt(Entity<RuneDrawerProviderComponent> ent, ref ActivatableUIOpenAttemptEvent args)
{
if (!HasComp<CultistComponent>(args.User))
args.Cancel();
}
private void BeforeRuneDraw(Entity<RuneDrawerProviderComponent> ent, ref BeforeActivatableUIOpenEvent args)
{
_ui.SetUiState(ent.Owner, ListViewSelectorUiKey.Key, new ListViewBUIState(ent.Comp.RunePrototypes, true));
}
private void OnRuneSelected(EntityUid uid, RuneDrawerProviderComponent component, ListViewItemSelectedMessage args)
{
var runePrototype = args.SelectedItem;
var whoCalled = args.Actor;
if (!HasComp<ActorComponent>(whoCalled))
return;
TryDraw(whoCalled, runePrototype);
}
private void TryDraw(EntityUid whoCalled, string runePrototype)
{
_timeToDraw = 4f;
if (HasComp<CultBuffComponent>(whoCalled))
_timeToDraw /= 2;
if (!IsAllowedToDraw(whoCalled))
return;
if (runePrototype == ApocalypseRunePrototypeId)
{
if (!_mindSystem.TryGetMind(whoCalled, out _, out var mind) ||
mind.Session is not { } playerSession)
return;
_euiManager.OpenEui(new ApocalypseRuneEui(whoCalled, _entityManager), playerSession);
return;
}
var ev = new CultDrawEvent
{
Rune = runePrototype
};
var argsDoAfterEvent = new DoAfterArgs(_entityManager, whoCalled, _timeToDraw, ev, whoCalled)
{
BreakOnMove = true,
NeedHand = true
};
if (!_doAfterSystem.TryStartDoAfter(argsDoAfterEvent))
return;
_audio.PlayPvs("/Audio/White/Cult/butcher.ogg", whoCalled, AudioParams.Default.WithMaxDistance(2f));
}
private void OnDraw(EntityUid uid, CultistComponent comp, CultDrawEvent args)
{
if (args.Cancelled)
return;
var howMuchBloodTake = -10;
var rune = args.Rune;
var user = args.User;
if (HasComp<CultBuffComponent>(user))
howMuchBloodTake /= 2;
if (!TryComp<BloodstreamComponent>(user, out var bloodstreamComponent))
return;
_bloodstreamSystem.TryModifyBloodLevel(user, howMuchBloodTake, bloodstreamComponent, createPuddle: false);
_audio.PlayPvs("/Audio/White/Cult/blood.ogg", user, AudioParams.Default.WithMaxDistance(2f));
if (rune == TeleportRunePrototypeId)
{
if (!HasComp<ActorComponent>(user))
return;
_ui.OpenUi(user, NameSelectorUIKey.Key, user);
return;
}
SpawnRune(user, rune);
}
private void OnChoose(EntityUid uid, CultistComponent component, NameSelectorMessage args)
{
if (!HasComp<ActorComponent>(uid))
return;
_ui.CloseUi(uid, NameSelectorUIKey.Key, uid);
SpawnRune(uid, TeleportRunePrototypeId, true, args.Name);
}
//Erasing start
private void TryErase(EntityUid uid, CultRuneBaseComponent component, InteractUsingEvent args)
{
if (TryComp<BibleComponent>(args.Used, out var bible) && HasComp<BibleUserComponent>(args.User))
{
_popupSystem.PopupEntity(Loc.GetString("cult-erased-rune"), args.User, args.User);
_audio.PlayPvs(bible.HealSoundPath, args.User);
EntityManager.DeleteEntity(args.Target);
return;
}
var entityPrototype = MetaData(args.Used).EntityPrototype;
if (entityPrototype == null)
return;
var used = entityPrototype.ID;
var user = args.User;
var target = args.Target;
var time = 3;
if (used != RitualDaggerPrototypeId)
return;
if (!HasComp<CultistComponent>(user))
return;
if (HasComp<CultBuffComponent>(user))
time /= 2;
var netEntity = GetNetEntity(target);
var ev = new CultEraseEvent
{
TargetEntityId = netEntity
};
var argsDoAfterEvent = new DoAfterArgs(_entityManager, user, time, ev, target)
{
BreakOnMove = true,
NeedHand = true
};
if (_doAfterSystem.TryStartDoAfter(argsDoAfterEvent))
{
_popupSystem.PopupEntity(Loc.GetString("cult-started-erasing-rune"), args.User, args.User);
}
}
private void OnErase(EntityUid uid, CultRuneBaseComponent component, CultEraseEvent args)
{
if (args.Cancelled)
return;
var target = GetEntity(args.TargetEntityId);
_entityManager.DeleteEntity(target);
_popupSystem.PopupEntity(Loc.GetString("cult-erased-rune"), args.User, args.User);
}
private void HandleCollision(EntityUid uid, CultRuneBaseComponent component, ref StartCollideEvent args)
{
if (!TryComp<SolutionContainerManagerComponent>(args.OtherEntity, out var solution) ||
!HasComp<VaporComponent>(args.OtherEntity) && !HasComp<SprayComponent>(args.OtherEntity))
{
return;
}
var cultRule = EntityManager.EntityQuery<CultRuleComponent>().FirstOrDefault();
if (cultRule is null)
{
return;
}
var solutions = _solutionContainerSystem.EnumerateSolutions((args.OtherEntity, solution));
if (solutions.Any(x => x.Solution.Comp.Solution.ContainsPrototype(cultRule.HolyWaterReagent)))
{
Del(uid);
}
}
//Erasing end
/*
* Rune draw end ----
*/
//------------------------------------------//
/*
* Base Start ----
*/
private void OnActivate(EntityUid uid, CultRuneBaseComponent component, ActivateInWorldEvent args)
{
if (args.Handled)
return;
args.Handled = true;
if (!HasComp<CultistComponent>(args.User))
return;
var cultists = new HashSet<EntityUid>
{
args.User
};
if (component.InvokersMinCount > 1 || component.GatherInvokers)
cultists = GatherCultists(uid, component.CultistGatheringRange);
if (cultists.Count < component.InvokersMinCount)
{
_popupSystem.PopupEntity(Loc.GetString("not-enough-cultists"), args.User, args.User);
return;
}
var ev = new CultRuneInvokeEvent(uid, args.User, cultists);
RaiseLocalEvent(uid, ev);
if (ev.Result)
{
OnAfterInvoke(uid, cultists, ev.InvokePhraseOverride);
}
}
private void OnAfterInvoke(EntityUid rune, HashSet<EntityUid> cultists, string? invokePhraseOverride = null)
{
if (!TryComp<CultRuneBaseComponent>(rune, out var component))
return;
foreach (var cultist in cultists)
{
_chat.TrySendInGameICMessage(cultist, invokePhraseOverride ?? component.InvokePhrase,
component.InvokeChatType, false, false, null, null, null, false);
}
}
/*
* Base End ----
*/
//------------------------------------------//
/*
* Offering Rune START ----
*/
private void OnInvokeOffering(EntityUid uid, CultRuneOfferingComponent component, CultRuneInvokeEvent args)
{
var targets =
_lookup.GetEntitiesInRange(uid, component.RangeTarget, LookupFlags.Dynamic | LookupFlags.Sundries);
targets.RemoveWhere(x =>
!_entityManager.HasComponent<HumanoidAppearanceComponent>(x) || HasComp<CultistComponent>(x));
if (targets.Count == 0)
return;
var victim = FindNearestTarget(uid, targets.ToList());
if (victim == null)
return;
TryComp<MobStateComponent>(victim.Value, out var state);
if (state == null)
return;
bool result;
if (state.CurrentState != MobState.Dead)
{
// Выполнение действия в зависимости от условий - ахуеть а я не думал, это чатгпт писал?
if (TryComp<MindContainerComponent>(victim.Value, out var mind) &&
mind.Mind != null &&
!HasComp<BibleUserComponent>(victim.Value) &&
!HasComp<MindShieldComponent>(victim.Value) &&
!IsTarget(mind.Mind.Value) &&
_playerManager.TryGetSessionByEntity(victim.Value, out var session) )
{
result = Convert(uid, victim.Value, args.User, session, args.Cultists);
args.InvokePhraseOverride = "Mah'weyh pleggh at e'ntrath!";
}
else
{
result = Sacrifice(uid, victim.Value, args.User, args.Cultists);
args.InvokePhraseOverride = "Barhah hra zar'garis!";
}
}
else // Жертва мертва, выполняется альтернативное действие
{
// Жертва мертва, выполняется альтернативное действие
result = Sacrifice(uid, victim.Value, args.User, args.Cultists, true);
args.InvokePhraseOverride = "Barhah hra zar'garis!";
}
args.Result = result;
}
private bool IsTarget(EntityUid mindId)
{
var target = _ruleSystem.GetTarget();
if (target == null)
return false;
return mindId == target.Owner;
}
private bool Sacrifice(
EntityUid rune,
EntityUid target,
EntityUid user,
HashSet<EntityUid> cultists,
bool isDead = false)
{
if (!TryComp<CultRuneOfferingComponent>(rune, out var offering))
return false;
var requiredCount = isDead ? offering.SacrificeDeadMinCount : offering.SacrificeMinCount;
if (cultists.Count < requiredCount)
{
_popupSystem.PopupEntity(Loc.GetString("cult-sacrifice-not-enough-cultists"), user, user);
return false;
}
if (!SpawnShard(target))
_bodySystem.GibBody(target);
AddChargesToReviveRune();
return true;
}
private bool Convert(EntityUid rune, EntityUid target, EntityUid user, ICommonSession session, HashSet<EntityUid> cultists)
{
if (!TryComp<CultRuneOfferingComponent>(rune, out var offering))
return false;
if (cultists.Count < offering.ConvertMinCount)
{
_popupSystem.PopupEntity(Loc.GetString("cult-offering-rune-not-enough"), user, user);
return false;
}
if (!_entityManager.TryGetComponent<ActorComponent>(target, out var actorComponent))
return false;
_stunSystem.TryStun(target, TimeSpan.FromSeconds(2f), false);
_antag.ForceMakeAntag<CultRuleComponent>(session, "Cult");
HealCultist(target);
if (TryComp(target, out CuffableComponent? cuffs) && cuffs.Container.ContainedEntities.Count >= 1)
{
var lastAddedCuffs = cuffs.LastAddedCuffs;
_cuffable.Uncuff(target, user, lastAddedCuffs);
}
_statusEffectsSystem.TryRemoveStatusEffect(target, "Muted");
RemCompDeferred<BlightComponent>(target);
return true;
}
/*
* Offering Rune END ----
*/
//------------------------------------------//
/*
* Buff Rune Start ----
*/
private void OnInvokeBuff(EntityUid uid, CultRuneBuffComponent component, CultRuneInvokeEvent args)
{
var targets =
_lookup.GetEntitiesInRange(uid, component.RangeTarget, LookupFlags.Dynamic | LookupFlags.Sundries);
targets.RemoveWhere(x =>
!_entityManager.HasComponent<HumanoidAppearanceComponent>(x) ||
!_entityManager.HasComponent<CultistComponent>(x));
if (targets.Count == 0)
return;
var victim = FindNearestTarget(uid, targets.ToList());
if (victim == null)
return;
TryComp<MobStateComponent>(victim.Value, out var state);
var result = false;
if (state != null && state.CurrentState != MobState.Dead)
{
result = AddCultistBuff(victim.Value, args.User);
}
args.Result = result;
}
private bool AddCultistBuff(EntityUid target, EntityUid user)
{
if (TryComp<CultBuffComponent>(target, out var buff) && buff.BuffTime > buff.BuffLimit)
{
_popupSystem.PopupEntity(Loc.GetString("cult-buff-already-buffed"), user, user);
return false;
}
buff = EnsureComp<CultBuffComponent>(target);
buff.BuffTime = buff.StartingBuffTime;
return true;
}
/*
* Empower Rune End ----
*/
//------------------------------------------//
/*
* Teleport rune start ----
*/
private void OnInvokeTeleport(EntityUid uid, CultRuneTeleportComponent component, CultRuneInvokeEvent args)
{
var targets =
_lookup.GetEntitiesInRange(uid, component.RangeTarget, LookupFlags.Dynamic | LookupFlags.Sundries);
if (targets.Count == 0)
{
return;
}
args.Result = Teleport(uid, args.User, targets.ToList());
}
private bool Teleport(EntityUid rune, EntityUid user, List<EntityUid>? victims = null)
{
if (!OpenTeleportUi(user, rune))
return false;
_entityManager.EnsureComponent<CultTeleportRuneProviderComponent>(user, out var providerComponent);
providerComponent.Targets = victims;
providerComponent.BaseRune = rune;
return true;
}
private bool OpenTeleportUi(EntityUid user, EntityUid? exceptRune = null)
{
var runesQuery = EntityQueryEnumerator<CultRuneTeleportComponent>();
var list = new List<int>();
var labels = new List<string>();
while (runesQuery.MoveNext(out var runeUid, out var teleportComponent))
{
if (teleportComponent.Label == null)
continue;
if (runeUid == exceptRune)
continue;
if (!int.TryParse(runeUid.ToString(), out var intValue))
continue;
list.Add(intValue);
labels.Add(teleportComponent.Label);
}
if (!TryComp<ActorComponent>(user, out var actorComponent))
return false;
if (list.Count == 0)
{
_popupSystem.PopupEntity(Loc.GetString("cult-teleport-rune-not-found"), user, user);
return false;
}
_ui.SetUiState(user, RuneTeleporterUiKey.Key, new TeleportRunesListWindowBUIState(list, labels));
_ui.TryToggleUi(user, RuneTeleporterUiKey.Key, actorComponent.PlayerSession);
return true;
}
private void OnTeleportRuneSelected(
EntityUid uid,
CultTeleportRuneProviderComponent component,
TeleportRunesListWindowItemSelectedMessage args)
{
var targets = component.Targets;
var user = args.Actor;
var selectedRune = new EntityUid(args.SelectedItem);
var baseRune = component.BaseRune;
if (targets is null || targets.Count == 0)
return;
if (baseRune == null)
return;
if (!TryComp<TransformComponent>(selectedRune, out var xFormSelected) ||
!TryComp<TransformComponent>(baseRune, out var xFormBase))
return;
foreach (var target in targets)
{
StopPulling(target);
_xform.SetCoordinates(target, xFormSelected.Coordinates);
}
//Play tp sound
_audio.PlayPvs(_teleportInSound, xFormSelected.Coordinates);
_audio.PlayPvs(_teleportOutSound, xFormBase.Coordinates);
if (HasComp<CultTeleportRuneProviderComponent>(user))
{
RemComp<CultTeleportRuneProviderComponent>(user);
}
}
/*
* Teleport rune end ----
*/
//------------------------------------------//
/*
* Apocalypse rune start ----
*/
private void OnInvokeApocalypse(EntityUid uid, CultRuneApocalypseComponent component, CultRuneInvokeEvent args)
{
args.Result = TrySummonNarsie(args.User, args.Cultists, component);
}
private bool TrySummonNarsie(EntityUid user, HashSet<EntityUid> cultists, CultRuneApocalypseComponent component)
{
var canSummon = _ruleSystem.CanSummonNarsie();
if (!canSummon)
{
_popupSystem.PopupEntity(Loc.GetString("cult-narsie-not-completed-tasks"), user, user);
return false;
}
if (cultists.Count < component.SummonMinCount)
{
_popupSystem.PopupEntity(Loc.GetString("cult-narsie-summon-not-enough"), user, user);
return false;
}
if (_doAfterAlreadyStarted)
{
_popupSystem.PopupEntity(Loc.GetString("cult-narsie-already-summoning"), user, user);
return false;
}
if (!TryComp<DoAfterComponent>(user, out var doAfterComponent))
{
if (doAfterComponent is { AwaitedDoAfters.Count: >= 1 })
{
_popupSystem.PopupEntity(Loc.GetString("cult-narsie-summon-do-after"), user, user);
return false;
}
}
var ev = new SummonNarsieDoAfterEvent();
var argsDoAfterEvent = new DoAfterArgs(_entityManager, user, TimeSpan.FromSeconds(40), ev, user)
{
BreakOnMove = true
};
if (!_doAfterSystem.TryStartDoAfter(argsDoAfterEvent))
return false;
_popupSystem.PopupEntity(Loc.GetString("cult-stay-still"), user, user, PopupType.LargeCaution);
_doAfterAlreadyStarted = true;
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-started"), "CULT", false,
colorOverride: Color.DarkRed);
_narsieSummonningAudio = _audio.PlayGlobal(_narsie40Sec, Filter.Broadcast(), false, AudioParams.Default.WithLoop(true).WithVolume(0.15f));
return true;
}
private void NarsieSpawn(EntityUid uid, CultistComponent component, SummonNarsieDoAfterEvent args)
{
_doAfterAlreadyStarted = false;
_audio.Stop(_narsieSummonningAudio?.Owner, _narsieSummonningAudio?.Comp);
if (args.Cancelled)
{
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-prevented"), "CULT", false,
colorOverride: Color.DarkRed);
return;
}
var transform = CompOrNull<TransformComponent>(args.User)?.Coordinates;
if (transform == null)
return;
var ev = new CultNarsieSummoned();
RaiseLocalEvent(ev);
_entityManager.SpawnEntity(NarsiePrototypeId, transform.Value);
//_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-narsie-summoned"), "CULT", true, _apocRuneEndDrawing,
// colorOverride: Color.DarkRed);
}
/*
* Apocalypse rune end ----
*/
//------------------------------------------//
/*
* Revive rune start ----
*/
private void OnInvokeRevive(EntityUid uid, CultRuneReviveComponent component, CultRuneInvokeEvent args)
{
var targets =
_lookup.GetEntitiesInRange(uid, component.RangeTarget, LookupFlags.Dynamic | LookupFlags.Sundries);
targets.RemoveWhere(x => !_entityManager.HasComponent<HumanoidAppearanceComponent>(x));
if (targets.Count == 0)
return;
var victim = FindNearestTarget(uid, targets.ToList());
if (victim == null)
return;
if (_mobState.IsAlive(victim.Value))
{
_popupSystem.PopupEntity(Loc.GetString("cult-revive-rune-already-alive"), args.User, args.User);
return;
}
var result = Revive(victim.Value, args.User);
args.Result = result;
}
private bool Revive(EntityUid target, EntityUid user)
{
if (HasComp<CultistComponent>(target))
{
if (CultRuneReviveComponent.ChargesLeft == 0)
{
_popupSystem.PopupEntity(Loc.GetString("cult-revive-rune-no-charges"), user, user);
return false;
}
CultRuneReviveComponent.ChargesLeft--;
_entityManager.EventBus.RaiseLocalEvent(target, new RejuvenateEvent());
}
else
{
if (!TryComp(target, out DamageableComponent? damageable) ||
!TryComp(target, out MobThresholdsComponent? threshold) ||
!TryComp(target, out MobStateComponent? mobState))
return false;
if (!_mobState.IsDead(target, mobState))
{
_popupSystem.PopupEntity(Loc.GetString("cult-revive-rune-already-alive"), user, user);
return false;
}
var airlossGroup = _prototypeManager.Index<DamageGroupPrototype>("Airloss");
var deadThreshold = _thresholdSystem.GetThresholdForState(target, MobState.Dead, threshold);
if (damageable.Damage.TryGetDamageInGroup(airlossGroup, out var toHeal))
{
var afterHeal = damageable.TotalDamage - toHeal;
if (deadThreshold <= afterHeal)
{
_popupSystem.PopupEntity(Loc.GetString("cult-revive-rune-too-damaged"), user, user);
return false;
}
var asphyxType = _prototypeManager.Index<DamageTypePrototype>("Asphyxiation");
var bloodlossType = _prototypeManager.Index<DamageTypePrototype>("Bloodloss");
var heal = new Action<DamageTypePrototype>(type =>
{
if (!damageable.Damage.DamageDict.TryGetValue(type.ID, out var damage))
return;
_damageableSystem.TryChangeDamage(target, new DamageSpecifier(type, -damage));
});
heal(asphyxType);
heal(bloodlossType);
}
if (damageable.TotalDamage < deadThreshold)
_mobState.ChangeMobState(target, MobState.Critical, mobState, user);
}
EntityUid? transferTo = null;
if (!_mindSystem.TryGetMind(target, out var mindId, out var mind))
{
if (!TryComp<CultistComponent>(target, out var cultist) || cultist.OriginalMind == null)
return true;
(mindId, mind) = cultist.OriginalMind.Value;
transferTo = target;
}
if (mind.Session is not { } playerSession)
return true;
// notify them they're being revived.
if (mind.CurrentEntity != target)
{
_euiManager.OpenEui(new ReturnToBodyEui(mind, _mindSystem, mindId, transferTo), playerSession);
}
return true;
}
/*
* Revive rune end ----
*/
//------------------------------------------//
/*
* Barrier rune start ----
*/
private void OnInvokeBarrier(EntityUid uid, CultRuneBarrierComponent component, CultRuneInvokeEvent args)
{
args.Result = SpawnBarrier(args.Rune);
}
private bool SpawnBarrier(EntityUid rune)
{
var transform = CompOrNull<TransformComponent>(rune)?.Coordinates;
if (transform == null)
return false;
_entityManager.SpawnEntity(CultBarrierPrototypeId, transform.Value);
_entityManager.DeleteEntity(rune);
return true;
}
/*
* Barrier rune end ----
*/
//------------------------------------------//
/*
* Summoning rune start ----
*/
private void OnInvokeSummoning(EntityUid uid, CultRuneSummoningComponent component, CultRuneInvokeEvent args)
{
args.Result = Summon(uid, args.User, args.Cultists, component);
}
private bool Summon(
EntityUid rune,
EntityUid user,
HashSet<EntityUid> cultistHashSet,
CultRuneSummoningComponent component)
{
var cultistsQuery = EntityQueryEnumerator<CultistComponent>();
var list = new List<int>();
var labels = new List<string>();
if (cultistHashSet.Count < component.SummonMinCount)
{
_popupSystem.PopupEntity(Loc.GetString("cult-summon-rune-need-minimum-cultists"), user, user);
return false;
}
while (cultistsQuery.MoveNext(out var cultistUid, out _))
{
var meta = MetaData(cultistUid);
if (cultistHashSet.Contains(cultistUid))
continue;
if (!int.TryParse(cultistUid.ToString(), out var intValue))
continue;
list.Add(intValue);
labels.Add(meta.EntityName);
}
if (!TryComp<ActorComponent>(user, out var actorComponent))
return false;
if (list.Count == 0)
{
_popupSystem.PopupEntity(Loc.GetString("cult-cultists-not-found"), user, user);
return false;
}
_entityManager.EnsureComponent<CultRuneSummoningProviderComponent>(user, out var providerComponent);
providerComponent.BaseRune = rune;
_ui.SetUiState(user, SummonCultistUiKey.Key, new SummonCultistListWindowBUIState(list, labels));
_ui.TryToggleUi(user, SummonCultistUiKey.Key, actorComponent.PlayerSession);
return true;
}
private void OnCultistSelected(
EntityUid uid,
CultRuneSummoningProviderComponent component,
SummonCultistListWindowItemSelectedMessage args)
{
var user = args.Actor;
var target = new EntityUid(args.SelectedItem);
var baseRune = component.BaseRune;
if (!TryComp<PullableComponent>(target, out var pullableComponent))
return;
if (!TryComp<CuffableComponent>(target, out var cuffableComponent))
return;
if (baseRune == null)
return;
if (!TryComp<TransformComponent>(baseRune, out var xFormBase))
return;
var isCuffed = cuffableComponent.CuffedHandCount > 0;
var isPulled = pullableComponent.BeingPulled;
if (isPulled)
{
_popupSystem.PopupEntity("Его кто-то держит!", user);
return;
}
if (isCuffed)
{
_popupSystem.PopupEntity("Он в наручниках!", user);
return;
}
StopPulling(target, false);
_xform.SetCoordinates(target, xFormBase.Coordinates);
_audio.PlayPvs(_teleportInSound, xFormBase.Coordinates);
if (HasComp<CultRuneSummoningProviderComponent>(user))
{
RemComp<CultRuneSummoningProviderComponent>(user);
}
}
/*
* Summoning rune end ----
*/
//------------------------------------------//
/*
* BloodBoil rune start ----
*/
private void OnInvokeBloodBoil(EntityUid uid, CultRuneBloodBoilComponent component, CultRuneInvokeEvent args)
{
args.Result = PrepareShoot(uid, args.User, args.Cultists, 1.0f, component);
}
private bool PrepareShoot(
EntityUid rune,
EntityUid user,
HashSet<EntityUid> cultists,
float severity,
CultRuneBloodBoilComponent component)
{
cultists = cultists.Where(HasComp<CultistComponent>).ToHashSet(); // Prevent constructs from using the rune
if (cultists.Count < component.SummonMinCount)
{
_popupSystem.PopupEntity(Loc.GetString("cult-blood-boil-rune-need-minimum"), user, user);
return false;
}
var xformQuery = GetEntityQuery<TransformComponent>();
var xform = xformQuery.GetComponent(rune);
var inRange = _lookup.GetEntitiesInRange(rune, component.ProjectileRange * severity, LookupFlags.Dynamic);
inRange.RemoveWhere(x =>
!_entityManager.HasComponent<HumanoidAppearanceComponent>(x) ||
_entityManager.HasComponent<CultistComponent>(x));
var list = inRange.ToList();
if (list.Count == 0)
{
_popupSystem.PopupEntity(Loc.GetString("cult-blood-boil-rune-no-targets"), user, user);
return false;
}
_random.Shuffle(list);
var bloodCost = 120 / cultists.Count;
foreach (var cultist in cultists)
{
if (!TryComp<BloodstreamComponent>(cultist, out var bloodstreamComponent) ||
bloodstreamComponent.BloodSolution is null)
{
return false;
}
if (bloodstreamComponent.BloodSolution.Value.Comp.Solution.Volume < bloodCost)
{
_popupSystem.PopupEntity(Loc.GetString("cult-blood-boil-rune-no-blood"), user, user);
return false;
}
_bloodstreamSystem.TryModifyBloodLevel(cultist, -bloodCost, bloodstreamComponent);
}
var projectileCount =
(int) MathF.Round(MathHelper.Lerp(component.MinProjectiles, component.MaxProjectiles, severity));
while (projectileCount > 0)
{
var target = _random.Pick(list);
var targetCoords = xformQuery.GetComponent(target).Coordinates.Offset(_random.NextVector2(0.5f));
var flammable = GetEntityQuery<FlammableComponent>();
if (!flammable.TryGetComponent(target, out var fl))
continue;
fl.FireStacks += 1;
_flammableSystem.Ignite(target, target);
Shoot(
rune,
component,
xform.Coordinates,
targetCoords,
severity);
projectileCount--;
}
_audio.PlayPvs(_magic, rune, AudioParams.Default.WithMaxDistance(2f));
return true;
}
private void Shoot(
EntityUid uid,
CultRuneBloodBoilComponent component,
EntityCoordinates coords,
EntityCoordinates targetCoords,
float severity)
{
var mapPos = coords.ToMap(EntityManager, _xform);
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out _)
? coords.WithEntityId(gridUid, EntityManager)
: new(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
var ent = Spawn(component.ProjectilePrototype, spawnCoords);
var direction = targetCoords.ToMapPos(EntityManager, _xform) - mapPos.Position;
if (!TryComp<ProjectileComponent>(ent, out var comp))
return;
comp.Damage *= severity;
_gunSystem.ShootProjectile(ent, direction, Vector2.Zero, uid, uid, component.ProjectileSpeed);
}
/*
* BloodBoil rune end ----
*/
//------------------------------------------//
/*
* Empower rune start ----
*/
private void OnActiveInWorld(EntityUid uid, CultEmpowerComponent component, CultRuneInvokeEvent args)
{
if (!component.IsRune || !TryComp<CultistComponent>(args.User, out _) || !HasComp<ActorComponent>(args.User))
return;
args.Result = !_ui.HasUi(uid, CultEmpowerUiKey.Key) &&
_ui.TryOpenUi(uid, CultEmpowerUiKey.Key, args.User);
}
private void OnUseInHand(EntityUid uid, CultEmpowerComponent component, UseInHandEvent args)
{
if (!HasComp<CultistComponent>(args.User) || !HasComp<ActorComponent>(args.User))
return;
_ui.OpenUi(uid, CultEmpowerUiKey.Key, args.User);
}
private void OnEmpowerSelected(EntityUid uid, CultEmpowerComponent component, CultEmpowerSelectedBuiMessage args)
{
if (!TryComp<CultistComponent>(args.Actor, out var comp))
return;
var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.ActionType));
if (action == null)
return;
if (component.IsRune)
{
if (comp.SelectedEmpowers.Count >= component.MaxAllowedCultistActions)
{
_popupSystem.PopupEntity(Loc.GetString("cult-too-much-empowers"), uid);
return;
}
comp.SelectedEmpowers.Add(GetNetEntity(_actionsSystem.AddAction(args.Actor, action)));
Dirty(args.Actor, comp);
}
else if (comp.SelectedEmpowers.Count < component.MinRequiredCultistActions)
{
comp.SelectedEmpowers.Add(GetNetEntity(_actionsSystem.AddAction(args.Actor, action)));
Dirty(args.Actor, comp);
}
}
/*
* Empower rune end ----
*/
//------------------------------------------//
/*
* Helpers Start ----
*/
private EntityUid? FindNearestTarget(EntityUid uid, List<EntityUid> targets)
{
if (!TryComp<TransformComponent>(uid, out var runeTransform))
return null;
var range = 999f;
EntityUid? victim = null;
foreach (var target in targets)
{
if (!TryComp<TransformComponent>(target, out var targetTransform))
continue;
runeTransform.Coordinates.TryDistance(_entityManager, targetTransform.Coordinates, out var newRange);
if (newRange < range)
{
range = newRange;
victim = target;
}
}
return victim;
}
private HashSet<EntityUid> GatherCultists(EntityUid uid, float range)
{
var entities = _lookup.GetEntitiesInRange(uid, range, LookupFlags.Dynamic);
entities.RemoveWhere(x => !HasComp<CultistComponent>(x) && !HasComp<ConstructComponent>(x));
return entities;
}
private void SpawnRune(EntityUid uid, string? rune, bool teleportRune = false, string? label = null)
{
var transform = CompOrNull<TransformComponent>(uid)?.Coordinates;
if (transform == null)
return;
if (rune == null)
return;
if (teleportRune)
{
var teleportRuneEntity = _entityManager.SpawnEntity(rune, transform.Value);
_xform.AttachToGridOrMap(teleportRuneEntity);
TryComp<CultRuneTeleportComponent>(teleportRuneEntity, out var sex);
{
if (sex == null)
return;
label = string.IsNullOrEmpty(label) ? Loc.GetString("cult-teleport-rune-default-label") : label;
if (label.Length > 18)
{
label = label.Substring(0, 18);
}
sex.Label = label;
}
return;
}
var damage = 10;
if (rune == ApocalypseRunePrototypeId)
{
if (!TryComp(uid, out TransformComponent? transComp))
{
return;
}
damage = 40;
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-narsie-summon-drawn-position",
("location", FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((uid, transComp))))),
"CULT", true, _apocRuneEndDrawing, colorOverride: Color.DarkRed);
}
var damageSpecifier = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"), damage);
_damageableSystem.TryChangeDamage(uid, damageSpecifier, true, false);
_xform.AttachToGridOrMap(_entityManager.SpawnEntity(rune, transform.Value));
}
private bool SpawnShard(EntityUid target)
{
if (!TryComp<MindContainerComponent>(target, out var mindComponent))
return false;
var transform = CompOrNull<TransformComponent>(target)?.Coordinates;
if (transform == null)
return false;
if (mindComponent.Mind.HasValue == false)
return false;
var shard = _entityManager.SpawnEntity("SoulShard", transform.Value);
if (shard.Valid == false)
return false;
_mindSystem.TransferTo(mindComponent.Mind.Value, shard);
_bodySystem.GibBody(target);
return true;
}
private void AddChargesToReviveRune(uint amount = 1)
{
CultRuneReviveComponent.ChargesLeft += amount;
}
private bool IsAllowedToDraw(EntityUid uid)
{
var transform = Transform(uid);
var gridUid = transform.GridUid;
var tile = transform.Coordinates.GetTileRef();
if (!gridUid.HasValue)
{
_popupSystem.PopupEntity(Loc.GetString("cult-cant-draw-rune"), uid, uid);
return false;
}
if (!tile.HasValue)
{
_popupSystem.PopupEntity(Loc.GetString("cult-cant-draw-rune"), uid, uid);
return false;
}
return true;
}
private void HealCultist(EntityUid player)
{
var damageSpecifier = _prototypeManager.Index<DamageGroupPrototype>("Brute");
var damageSpecifier2 = _prototypeManager.Index<DamageGroupPrototype>("Burn");
_damageableSystem.TryChangeDamage(player, new DamageSpecifier(damageSpecifier, -40));
_damageableSystem.TryChangeDamage(player, new DamageSpecifier(damageSpecifier2, -40));
}
private void StopPulling(EntityUid target, bool checkPullable = true)
{
// break pulls before portal enter so we dont break shit
if (checkPullable && TryComp<PullableComponent>(target, out var pullable) && pullable.BeingPulled)
{
_pulling.TryStopPull(target, pullable);
}
if (_pulling.TryGetPulledEntity(target, out var pulledEntity)
&& TryComp(pulledEntity, out PullableComponent? subjectPulling))
{
_pulling.TryStopPull(pulledEntity.Value, subjectPulling);
}
}
/*
* Helpers End ----
*/
}