From c04b9621419f0f44ac50384935f184e6de2f6aa9 Mon Sep 17 00:00:00 2001 From: Remuchi Date: Wed, 27 Mar 2024 19:30:19 +0700 Subject: [PATCH] =?UTF-8?q?=D1=8F=20=D1=83=D1=81=D1=82=D0=B0=D0=BB=D0=B0?= =?UTF-8?q?=20=D0=B1=D0=BE=D1=81=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Changeling/ChangelingRuleComponent.cs | 15 +- .../Changeling/ChangelingRuleSystem.cs | 273 ++++----- .../Changeling/ChangelingSystem.Abilities.cs | 56 +- .../EntitySystems/ReagentDispenserSystem.cs | 11 +- .../Electrocution/ElectrocutionSystem.cs | 109 ++-- .../_White/Cult/GameRule/CultRuleComponent.cs | 50 +- .../_White/Cult/GameRule/CultRuleSystem.cs | 527 +++++++----------- .../Systems/TorchCultistsProviderSystem.cs | 32 +- .../Cult/Items/Systems/VoidTeleportSystem.cs | 57 +- .../Cult/Runes/Systems/CultSystem.Narsie.cs | 3 +- .../Cult/Runes/Systems/CultSystem.Rune.cs | 139 +++-- .../_White/Cult/UI/TeleportSpellEui.cs | 67 +-- .../Movement/Pulling/Systems/PullingSystem.cs | 18 + .../ru-RU/White/stuff.ftl/abilities.ftl | 14 - .../Locale/ru-RU/White/stuff.ftl/archives.ftl | 2 - .../ru-RU/White/stuff.ftl/construct.ftl | 3 - .../ru-RU/White/stuff.ftl/cult-structure.ftl | 21 - .../ru-RU/White/stuff.ftl/runes-entities.ftl | 11 - .../ru-RU/White/stuff.ftl/shuttle-curse.ftl | 8 - .../ru-RU/{ => _white}/cult/abilities.ftl | 19 +- .../stuff.ftl => _white/cult}/constructs.ftl | 0 .../ru-RU/{White => _white/cult}/cult.ftl | 30 +- .../stuff.ftl => _white/cult}/effects.ftl | 0 .../Locale/ru-RU/_white/cult/entities.ftl | 57 ++ .../cult/factory.ftl} | 4 + .../cult-item.ftl => _white/cult/item.ftl} | 0 Resources/Locale/ru-RU/_white/cult/preset.ftl | 14 + .../stuff.ftl => _white/cult}/pylon.ftl | 0 .../ru-RU/_white/cult/runes-entities.ftl | 2 + .../ru-RU/{ => _white}/cult/shuttle-curse.ftl | 0 .../Locale/ru-RU/_white/cult/structure.ftl | 4 + .../{White/stuff.ftl => _white/cult}/tile.ftl | 0 .../cult}/veil-shifter.ftl | 0 .../Locale/ru-RU/{ => _white}/cult/verb.ftl | 0 .../stuff.ftl => _white/cult}/void-torch.ftl | 0 Resources/Locale/ru-RU/cult/constructs.ftl | 8 - .../Locale/ru-RU/cult/cult-structure.ftl | 21 - .../Locale/ru-RU/cult/cultist-factory.ftl | 11 - Resources/Locale/ru-RU/cult/effects.ftl | 1 - Resources/Locale/ru-RU/cult/pylon.ftl | 4 - Resources/Locale/ru-RU/cult/veil-shifter.ftl | 5 - Resources/Locale/ru-RU/cult/void-torch.ftl | 9 - .../Locale/ru-RU/white/gamemodes/cult.ftl | 7 - Resources/Prototypes/GameRules/roundstart.yml | 2 +- Resources/Prototypes/game_presets.yml | 6 +- 45 files changed, 693 insertions(+), 927 deletions(-) delete mode 100644 Resources/Locale/ru-RU/White/stuff.ftl/abilities.ftl delete mode 100644 Resources/Locale/ru-RU/White/stuff.ftl/archives.ftl delete mode 100644 Resources/Locale/ru-RU/White/stuff.ftl/construct.ftl delete mode 100644 Resources/Locale/ru-RU/White/stuff.ftl/cult-structure.ftl delete mode 100644 Resources/Locale/ru-RU/White/stuff.ftl/runes-entities.ftl delete mode 100644 Resources/Locale/ru-RU/White/stuff.ftl/shuttle-curse.ftl rename Resources/Locale/ru-RU/{ => _white}/cult/abilities.ftl (78%) rename Resources/Locale/ru-RU/{White/stuff.ftl => _white/cult}/constructs.ftl (100%) rename Resources/Locale/ru-RU/{White => _white/cult}/cult.ftl (69%) rename Resources/Locale/ru-RU/{White/stuff.ftl => _white/cult}/effects.ftl (100%) create mode 100644 Resources/Locale/ru-RU/_white/cult/entities.ftl rename Resources/Locale/ru-RU/{White/stuff.ftl/cultist-factory.ftl => _white/cult/factory.ftl} (99%) rename Resources/Locale/ru-RU/{cult/cult-item.ftl => _white/cult/item.ftl} (100%) create mode 100644 Resources/Locale/ru-RU/_white/cult/preset.ftl rename Resources/Locale/ru-RU/{White/stuff.ftl => _white/cult}/pylon.ftl (100%) create mode 100644 Resources/Locale/ru-RU/_white/cult/runes-entities.ftl rename Resources/Locale/ru-RU/{ => _white}/cult/shuttle-curse.ftl (100%) create mode 100644 Resources/Locale/ru-RU/_white/cult/structure.ftl rename Resources/Locale/ru-RU/{White/stuff.ftl => _white/cult}/tile.ftl (100%) rename Resources/Locale/ru-RU/{White/stuff.ftl => _white/cult}/veil-shifter.ftl (100%) rename Resources/Locale/ru-RU/{ => _white}/cult/verb.ftl (100%) rename Resources/Locale/ru-RU/{White/stuff.ftl => _white/cult}/void-torch.ftl (100%) delete mode 100644 Resources/Locale/ru-RU/cult/constructs.ftl delete mode 100644 Resources/Locale/ru-RU/cult/cult-structure.ftl delete mode 100644 Resources/Locale/ru-RU/cult/cultist-factory.ftl delete mode 100644 Resources/Locale/ru-RU/cult/effects.ftl delete mode 100644 Resources/Locale/ru-RU/cult/pylon.ftl delete mode 100644 Resources/Locale/ru-RU/cult/veil-shifter.ftl delete mode 100644 Resources/Locale/ru-RU/cult/void-torch.ftl delete mode 100644 Resources/Locale/ru-RU/white/gamemodes/cult.ftl diff --git a/Content.Server/Changeling/ChangelingRuleComponent.cs b/Content.Server/Changeling/ChangelingRuleComponent.cs index c96547e91f..25c705edf9 100644 --- a/Content.Server/Changeling/ChangelingRuleComponent.cs +++ b/Content.Server/Changeling/ChangelingRuleComponent.cs @@ -1,7 +1,5 @@ -using Content.Shared.Preferences; -using Content.Shared.Roles; +using Content.Shared.Roles; using Robust.Shared.Audio; -using Robust.Shared.Player; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Changeling; @@ -9,9 +7,9 @@ namespace Content.Server.Changeling; [RegisterComponent, Access(typeof(ChangelingRuleSystem))] public sealed partial class ChangelingRuleComponent : Component { - public readonly List ChangelingMinds = new(); + public readonly List ChangelingMinds = []; - [DataField("changelingPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string ChangelingPrototypeId = "Changeling"; public int TotalChangelings => ChangelingMinds.Count; @@ -19,17 +17,16 @@ public sealed partial class ChangelingRuleComponent : Component public enum SelectionState { WaitingForSpawn = 0, - ReadyToSelect = 1, - SelectionMade = 2, + ReadyToStart = 1, + Started = 2, } public SelectionState SelectionStatus = SelectionState.WaitingForSpawn; public TimeSpan AnnounceAt = TimeSpan.Zero; - public Dictionary StartCandidates = new(); /// /// Path to antagonist alert sound. /// [DataField("greetSoundNotification")] public SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/changeling_start.ogg"); -} +} \ No newline at end of file diff --git a/Content.Server/Changeling/ChangelingRuleSystem.cs b/Content.Server/Changeling/ChangelingRuleSystem.cs index 32a68b8da2..63a7ffa040 100644 --- a/Content.Server/Changeling/ChangelingRuleSystem.cs +++ b/Content.Server/Changeling/ChangelingRuleSystem.cs @@ -1,20 +1,17 @@ -using System.Linq; -using Content.Server._Miracle.GulagSystem; using Content.Server.Antag; -using Content.Server.Chat.Managers; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.Mind; -using Content.Server.NPC.Systems; using Content.Server.Objectives; using Content.Server.Roles; +using Content.Shared._White.Mood; using Content.Shared.Changeling; using Content.Shared.GameTicking; +using Content.Shared.NPC.Systems; using Content.Shared.Objectives.Components; using Content.Shared.Roles; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; +using Robust.Server.Player; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -23,16 +20,14 @@ namespace Content.Server.Changeling; public sealed class ChangelingRuleSystem : GameRuleSystem { [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly SharedRoleSystem _roleSystem = default!; [Dependency] private readonly ObjectivesSystem _objectives = default!; [Dependency] private readonly ChangelingNameGenerator _nameGenerator = default!; - [Dependency] private readonly GulagSystem _gulag = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; private const int PlayersPerChangeling = 15; private const int MaxChangelings = 4; @@ -40,8 +35,6 @@ public sealed class ChangelingRuleSystem : GameRuleSystem component.AnnounceAt) + if (component.SelectionStatus < ChangelingRuleComponent.SelectionState.Started && + component.AnnounceAt < _gameTiming.CurTime) + { DoChangelingStart(component); + component.SelectionStatus = ChangelingRuleComponent.SelectionState.Started; + } } private void OnStartAttempt(RoundStartAttemptEvent ev) { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var gameRule)) - { - if (!GameTicker.IsGameRuleAdded(uid, gameRule)) - continue; - - if (!ev.Forced && ev.Players.Length < ChangelingMinPlayers) - { - _chatManager.SendAdminAnnouncement(Loc.GetString("changeling-not-enough-ready-players", - ("readyPlayersCount", ev.Players.Length), ("minimumPlayers", ChangelingMinPlayers))); - - ev.Cancel(); - continue; - } - - if (ev.Players.Length == 0) - { - _chatManager.DispatchServerAnnouncement(Loc.GetString("changeling-no-one-ready")); - ev.Cancel(); - } - } - } - - private void DoChangelingStart(ChangelingRuleComponent component) - { - if (component.StartCandidates.Count == 0) - { - Log.Error("Tried to start Changeling mode without any candidates."); - return; - } - - var numChangelings = - MathHelper.Clamp(component.StartCandidates.Count / PlayersPerChangeling, 1, MaxChangelings); - - var changelingPool = - _antagSelection.FindPotentialAntags(component.StartCandidates, component.ChangelingPrototypeId); - - var selectedChangelings = _antagSelection.PickAntag(numChangelings, changelingPool); - - foreach (var changeling in selectedChangelings) - { - MakeChangeling(changeling); - } - - component.SelectionStatus = ChangelingRuleComponent.SelectionState.SelectionMade; + TryRoundStartAttempt(ev, Loc.GetString("changeling-title")); } private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev) { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var changeling, out var gameRule)) + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var changeling, out _)) { - if (!GameTicker.IsGameRuleAdded(uid, gameRule)) - continue; - - foreach (var player in ev.Players) - { - if (!ev.Profiles.ContainsKey(player.UserId)) - continue; - - changeling.StartCandidates[player] = ev.Profiles[player.UserId]; - } - var delay = TimeSpan.FromSeconds(ChangelingStartDelay + _random.NextFloat(0f, ChangelingStartDelayVariance)); changeling.AnnounceAt = _gameTiming.CurTime + delay; - changeling.SelectionStatus = ChangelingRuleComponent.SelectionState.ReadyToSelect; + changeling.SelectionStatus = ChangelingRuleComponent.SelectionState.ReadyToStart; } } - public bool MakeChangeling(ICommonSession changeling, bool giveObjectives = true) - { - var changelingRule = EntityQuery().FirstOrDefault(); - if (changelingRule == null) - { - GameTicker.StartGameRule("Changeling", out var ruleEntity); - changelingRule = Comp(ruleEntity); - } - - if (!_mindSystem.TryGetMind(changeling, out var mindId, out var mind)) - { - Log.Info("Failed getting mind for picked changeling."); - return false; - } - - if (HasComp(mindId)) - { - Log.Error($"Player {changeling.Name} is already a changeling."); - return false; - } - - if (mind.OwnedEntity is not { } entity) - { - Log.Error("Mind picked for changeling did not have an attached entity."); - return false; - } - - _roleSystem.MindAddRole(mindId, new ChangelingRoleComponent - { - PrototypeId = changelingRule.ChangelingPrototypeId - }, mind); - - var briefing = Loc.GetString("changeling-role-briefing-short"); - - _roleSystem.MindAddRole(mindId, new RoleBriefingComponent - { - Briefing = briefing - }, mind, true); - - _roleSystem.MindPlaySound(mindId, changelingRule.GreetSoundNotification, mind); - SendChangelingBriefing(mindId); - changelingRule.ChangelingMinds.Add(mindId); - - // Change the faction - _npcFaction.RemoveFaction(entity, "NanoTrasen", false); - _npcFaction.AddFaction(entity, "Syndicate"); - - EnsureComp(entity, out var readyChangeling); - - readyChangeling.HiveName = _nameGenerator.GetName(); - Dirty(entity, readyChangeling); - - if (!giveObjectives) - return true; - - var difficulty = 0f; - for (var pick = 0; pick < ChangelingMaxPicks && ChangelingMaxDifficulty > difficulty; pick++) - { - var objective = _objectives.GetRandomObjective(mindId, mind, "ChangelingObjectiveGroups"); - if (objective == null) - continue; - - _mindSystem.AddObjective(mindId, mind, objective.Value); - difficulty += Comp(objective.Value).Difficulty; - } - - return true; - } - - private void SendChangelingBriefing(EntityUid mind) - { - if (!_mindSystem.TryGetSession(mind, out var session)) - return; - - _chatManager.DispatchServerMessage(session, Loc.GetString("changeling-role-greeting")); - } - private void HandleLatejoin(PlayerSpawnCompleteEvent ev) { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var changeling, out var gameRule)) + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var changeling, out _)) { - if (!GameTicker.IsGameRuleAdded(uid, gameRule)) - continue; - if (changeling.TotalChangelings >= MaxChangelings) continue; - if (_gulag.IsUserGulaged(ev.Player.UserId, out _)) - continue; - if (!ev.LateJoin) continue; - if (!ev.Profile.AntagPreferences.Contains(changeling.ChangelingPrototypeId)) - continue; - - if (ev.JobId == null || !_prototypeManager.TryIndex(ev.JobId, out var job)) - continue; - - if (!job.CanBeAntag) + if (!_antagSelection.IsPlayerEligible(ev.Player, changeling.ChangelingPrototypeId)) continue; // Before the announcement is made, late-joiners are considered the same as players who readied. - if (changeling.SelectionStatus < ChangelingRuleComponent.SelectionState.SelectionMade) - { - changeling.StartCandidates[ev.Player] = ev.Profile; + if (changeling.SelectionStatus < ChangelingRuleComponent.SelectionState.Started) continue; - } var target = PlayersPerChangeling * changeling.TotalChangelings + 1; - var chance = 1f / PlayersPerChangeling; if (ev.JoinOrder < target) @@ -272,11 +120,16 @@ public sealed class ChangelingRuleSystem : GameRuleSystem(mindId)) + { + Log.Error($"Player {mind.CharacterName} is already a changeling."); + return false; + } + + var briefing = Loc.GetString("changeling-role-briefing-short"); + _antagSelection.SendBriefing(changeling, briefing, null, rule.GreetSoundNotification); + + rule.ChangelingMinds.Add(mindId); + + _roleSystem.MindAddRole(mindId, new ChangelingRoleComponent + { + PrototypeId = rule.ChangelingPrototypeId + }, mind); + + _roleSystem.MindAddRole(mindId, new RoleBriefingComponent + { + Briefing = briefing + }, mind, true); + + // Change the faction + _npcFaction.RemoveFaction(changeling, "NanoTrasen", false); + _npcFaction.AddFaction(changeling, "Syndicate"); + + EnsureComp(changeling, out var readyChangeling); + + readyChangeling.HiveName = _nameGenerator.GetName(); + Dirty(changeling, readyChangeling); + + RaiseLocalEvent(mindId, new MoodEffectEvent("TraitorFocused")); // WD edit + + if (!giveObjectives) + return true; + + var difficulty = 0f; + for (var pick = 0; pick < ChangelingMaxPicks && ChangelingMaxDifficulty > difficulty; pick++) + { + var objective = _objectives.GetRandomObjective(mindId, mind, "ChangelingObjectiveGroups"); + if (objective == null) + continue; + + _mindSystem.AddObjective(mindId, mind, objective.Value); + var adding = Comp(objective.Value).Difficulty; + difficulty += adding; + Log.Debug($"Added objective {ToPrettyString(objective):objective} with {adding} difficulty"); + } + + return true; + } +} \ No newline at end of file diff --git a/Content.Server/Changeling/ChangelingSystem.Abilities.cs b/Content.Server/Changeling/ChangelingSystem.Abilities.cs index 8ca2dbfbe2..5e73892fad 100644 --- a/Content.Server/Changeling/ChangelingSystem.Abilities.cs +++ b/Content.Server/Changeling/ChangelingSystem.Abilities.cs @@ -41,7 +41,11 @@ using Content.Shared.Inventory; using Content.Shared.Miracle.UI; using Content.Shared.Mobs; using Content.Shared.Mobs.Systems; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Ninja.Components; +using Content.Shared.NPC.Components; +using Content.Shared.NPC.Systems; using Content.Shared.Standing; using Content.Shared.StatusEffect; using Content.Shared.Tag; @@ -74,7 +78,7 @@ public sealed partial class ChangelingSystem [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly ActionContainerSystem _actionContainerSystem = default!; - [Dependency] private readonly SharedPullingSystem _pullingSystem = default!; + [Dependency] private readonly PullingSystem _pullingSystem = default!; [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly BloodstreamSystem _blood = default!; [Dependency] private readonly CuffableSystem _cuffable = default!; @@ -174,7 +178,7 @@ public sealed partial class ChangelingSystem return; } - if (!TryComp(args.Target, out var pulled)) + if (!TryComp(args.Target, out var pulled)) { _popup.PopupEntity(Loc.GetString("changeling-popup-absorb-pull"), args.Performer, args.Performer); return; @@ -189,8 +193,7 @@ public sealed partial class ChangelingSystem _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, component.AbsorbDnaDelay, new AbsorbDnaDoAfterEvent(), uid, args.Target, uid) { - BreakOnTargetMove = true, - BreakOnUserMove = true + BreakOnMove = true } ); } @@ -233,11 +236,10 @@ public sealed partial class ChangelingSystem var selectedDna = args.SelectedItem; var user = GetEntity(args.Entity); - _doAfterSystem.TryStartDoAfter( - new DoAfterArgs(EntityManager, user, component.TransformDelay, + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.TransformDelay, new TransformDoAfterEvent { SelectedDna = selectedDna }, user, user, user) { - BreakOnUserMove = true + BreakOnMove = true } ); @@ -268,14 +270,12 @@ public sealed partial class ChangelingSystem _popup.PopupEntity(Loc.GetString("changeling-popup-start-regeneration"), uid, uid); - _doAfterSystem.TryStartDoAfter( - new DoAfterArgs(EntityManager, args.Performer, component.RegenerateDelay, - new RegenerateDoAfterEvent(), args.Performer, - args.Performer, args.Performer) - { - RequireCanInteract = false, - Hidden = true - }); + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, component.RegenerateDelay, + new RegenerateDoAfterEvent(), args.Performer, args.Performer, args.Performer) + { + RequireCanInteract = false, + Hidden = true + }); component.IsRegenerating = true; } @@ -297,7 +297,7 @@ public sealed partial class ChangelingSystem _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, component.LesserFormDelay, new LesserFormDoAfterEvent(), args.Performer, args.Performer) { - BreakOnUserMove = true, + BreakOnMove = true, RequireCanInteract = false }); } @@ -402,10 +402,10 @@ public sealed partial class ChangelingSystem if (!TakeChemicals(uid, component, 50)) return; - if (TryComp(target, out SharedPullerComponent? puller) && puller.Pulling is { } pulled && - TryComp(pulled, out SharedPullableComponent? pullable)) + if (TryComp(target, out PullerComponent? puller) && puller.Pulling is { } pulled + && TryComp(pulled, out PullableComponent? pullable)) { - _pullingSystem.TryStopPull(pullable); + _pullingSystem.TryStopPull(pulled, pullable); } TransformPerson(target, humanData); @@ -568,7 +568,7 @@ public sealed partial class ChangelingSystem _inventorySystem.TryUnequip(uid, outerName, out var outer, true, true); _inventorySystem.TryUnequip(uid, headName, out var helmet, true, true); - if (TryComp(outer, out MetaDataComponent? metaData) && metaData.EntityPrototype is {ID: armorName}) + if (TryComp(outer, out MetaDataComponent? metaData) && metaData.EntityPrototype is { ID: armorName }) { args.Handled = true; return; @@ -654,9 +654,11 @@ public sealed partial class ChangelingSystem if (!TakeChemicals(uid, component, 5)) return; - if (TryComp(uid, out SharedPullerComponent? puller) && puller.Pulling is { } pulled && - TryComp(pulled, out SharedPullableComponent? pullable)) - _pullingSystem.TryStopPull(pullable); + if (TryComp(uid, out PullerComponent? puller) && puller.Pulling is { } pulled + && TryComp(pulled, out PullableComponent? pullable)) + { + _pullingSystem.TryStopPull(pulled, pullable); + } TryTransformChangeling(args.User, args.SelectedDna, component); @@ -689,10 +691,10 @@ public sealed partial class ChangelingSystem } } - if (TryComp(uid, out SharedPullerComponent? puller) && puller.Pulling is { } pulled && - TryComp(pulled, out SharedPullableComponent? pullable)) + if (TryComp(uid, out PullerComponent? puller) && puller.Pulling is { } pulled + && TryComp(pulled, out PullableComponent? pullable)) { - _pullingSystem.TryStopPull(pullable); + _pullingSystem.TryStopPull(pulled, pullable); } if (TryComp(args.Target.Value, out var changelingComponent)) @@ -1119,4 +1121,4 @@ public sealed partial class ChangelingSystem } #endregion -} +} \ No newline at end of file diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs index 8a5d0e0151..c2da6502b8 100644 --- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs @@ -20,6 +20,7 @@ using Robust.Shared.Containers; using Robust.Shared.Prototypes; using System.Linq; using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.Reagent; namespace Content.Server.Chemistry.EntitySystems { @@ -106,8 +107,11 @@ namespace Content.Server.Chemistry.EntitySystems RecheckConnections(uid, component); } - public void UpdateConnection(EntityUid dispenser, EntityUid chemMaster, - ReagentDispenserComponent? dispenserComp = null, ChemMasterComponent? chemMasterComp = null) + public void UpdateConnection( + EntityUid dispenser, + EntityUid chemMaster, + ReagentDispenserComponent? dispenserComp = null, + ChemMasterComponent? chemMasterComp = null) { if (!Resolve(dispenser, ref dispenserComp) || !Resolve(chemMaster, ref chemMasterComp)) return; @@ -224,7 +228,8 @@ namespace Content.Server.Chemistry.EntitySystems var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser, SharedReagentDispenser.OutputSlotName); if (outputContainer is not { Valid: true } || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution, out _)) - { // WD EDIT START + { + // WD EDIT START var chemMasterUid = reagentDispenser.Comp.ChemMaster; if (!reagentDispenser.Comp.ChemMasterInRange || !TryComp(chemMasterUid, out ChemMasterComponent? chemMaster) || diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index ce443f59ed..9ae04dc5e6 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Administration.Logs; using Content.Server.Light.Components; using Content.Server.NodeContainer; using Content.Server.NodeContainer.EntitySystems; -using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.Nodes; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -17,7 +16,7 @@ using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.Jittering; using Content.Shared.Maps; -using Content.Shared.Mobs; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Popups; using Content.Shared.Speech.EntitySystems; using Content.Shared.StatusEffect; @@ -32,8 +31,6 @@ using Robust.Shared.Physics.Events; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; -using PullerComponent = Content.Shared.Movement.Pulling.Components.PullerComponent; namespace Content.Server.Electrocution; @@ -104,7 +101,8 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem var timePassed = Math.Min(frameTime, electrocution.TimeLeft); electrocution.TimeLeft -= timePassed; - electrocution.AccumulatedDamage += electrocution.BaseDamage * (consumer.ReceivedPower / consumer.DrawRate) * timePassed; + electrocution.AccumulatedDamage += + electrocution.BaseDamage * (consumer.ReceivedPower / consumer.DrawRate) * timePassed; if (!MathHelper.CloseTo(electrocution.TimeLeft, 0)) continue; @@ -113,15 +111,19 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem { // TODO: damage should be scaled by shock damage multiplier // TODO: better paralyze/jitter timing - var damage = new DamageSpecifier(_prototypeManager.Index(DamageType), (int) electrocution.AccumulatedDamage); + var damage = new DamageSpecifier(_prototypeManager.Index(DamageType), + (int) electrocution.AccumulatedDamage); + + var actual = + _damageable.TryChangeDamage(electrocution.Electrocuting, damage, origin: electrocution.Source); - var actual = _damageable.TryChangeDamage(electrocution.Electrocuting, damage, origin: electrocution.Source); if (actual != null) { _adminLogger.Add(LogType.Electrocution, $"{ToPrettyString(electrocution.Electrocuting):entity} received {actual.GetTotal():damage} powered electrocution damage from {ToPrettyString(electrocution.Source):source}"); } } + QueueDel(uid); } } @@ -144,19 +146,22 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem { if (!electrified.Enabled) return false; + if (electrified.NoWindowInTile) { var tileRef = transform.Coordinates.GetTileRef(EntityManager, _mapManager); if (tileRef != null) { - foreach (var entity in _entityLookup.GetLocalEntitiesIntersecting(tileRef.Value, flags: LookupFlags.StaticSundries)) + foreach (var entity in _entityLookup.GetLocalEntitiesIntersecting(tileRef.Value, + flags: LookupFlags.StaticSundries)) { if (_tag.HasTag(entity, "Window")) return false; } } } + if (electrified.UsesApcPower) { if (!this.IsPowered(uid, EntityManager)) @@ -199,7 +204,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem if (!_meleeWeapon.GetDamage(args.Used, args.User).Any()) return; - DoCommonElectrocution(args.User, uid, component.UnarmedHitShock, component.UnarmedHitStun, false, 1); + DoCommonElectrocution(args.User, uid, component.UnarmedHitShock, component.UnarmedHitStun, false); } private void OnElectrifiedInteractUsing(EntityUid uid, ElectrifiedComponent electrified, InteractUsingEvent args) @@ -213,6 +218,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem TryDoElectrifiedAct(uid, args.User, siemens, electrified); } + private bool IsPanelClosed(EntityUid uid) // WD { return TryComp(uid, out WiresPanelComponent? panel) && !panel.Open; @@ -221,24 +227,16 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem private float CalculateElectrifiedDamageScale(float power) { // A logarithm allows a curve of damage that grows quickly, but slows down dramatically past a value. This keeps the damage to a reasonable range. - const float DamageShift = 1.67f; // Shifts the curve for an overall higher or lower damage baseline - const float CeilingCoefficent = 1.35f; // Adjusts the approach to maximum damage, higher = Higher top damage - const float LogGrowth = 0.00001f; // Adjusts the growth speed of the curve + const float damageShift = 1.67f; // Shifts the curve for an overall higher or lower damage baseline + const float ceilingCoefficent = 1.35f; // Adjusts the approach to maximum damage, higher = Higher top damage + const float logGrowth = 0.00001f; // Adjusts the growth speed of the curve - return DamageShift + MathF.Log(power * LogGrowth) * CeilingCoefficent; + return damageShift + MathF.Log(power * logGrowth) * ceilingCoefficent; } - private float CalculateElectrifiedDamageScale(float power) - { - // A logarithm allows a curve of damage that grows quickly, but slows down dramatically past a value. This keeps the damage to a reasonable range. - const float DamageShift = 1.67f; // Shifts the curve for an overall higher or lower damage baseline - const float CeilingCoefficent = 1.35f; // Adjusts the approach to maximum damage, higher = Higher top damage - const float LogGrowth = 0.00001f; // Adjusts the growth speed of the curve - - return DamageShift + MathF.Log(power * LogGrowth) * CeilingCoefficent; - } - - public bool TryDoElectrifiedAct(EntityUid uid, EntityUid targetUid, + public bool TryDoElectrifiedAct( + EntityUid uid, + EntityUid targetUid, float siemens = 1, ElectrifiedComponent? electrified = null, NodeContainerComponent? nodeContainer = null, @@ -277,6 +275,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem electrified.SiemensCoefficient ); } + return lastRet; } @@ -304,21 +303,28 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem entity, uid, node, - (int) MathF.Ceiling(electrified.ShockDamage * damageScale * MathF.Pow(RecursiveDamageMultiplier, depth)), - TimeSpan.FromSeconds(electrified.ShockTime * MathF.Min(1f + MathF.Log2(1f + damageScale), 3f) * MathF.Pow(RecursiveTimeMultiplier, depth)), + (int) MathF.Ceiling(electrified.ShockDamage * damageScale * + MathF.Pow(RecursiveDamageMultiplier, depth)), + TimeSpan.FromSeconds(electrified.ShockTime * MathF.Min(1f + MathF.Log2(1f + damageScale), 3f) * + MathF.Pow(RecursiveTimeMultiplier, depth)), true, electrified.SiemensCoefficient); } + return lastRet; } } - private Node? PoweredNode(EntityUid uid, ElectrifiedComponent electrified, NodeContainerComponent? nodeContainer = null) + private Node? PoweredNode( + EntityUid uid, + ElectrifiedComponent electrified, + NodeContainerComponent? nodeContainer = null) { if (!Resolve(uid, ref nodeContainer, false)) return null; - return TryNode(electrified.HighVoltageNode) ?? TryNode(electrified.MediumVoltageNode) ?? TryNode(electrified.LowVoltageNode); + return TryNode(electrified.HighVoltageNode) ?? + TryNode(electrified.MediumVoltageNode) ?? TryNode(electrified.LowVoltageNode); Node? TryNode(string? id) { @@ -328,14 +334,21 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem { return tryNode; } + return null; } } /// public override bool TryDoElectrocution( - EntityUid uid, EntityUid? sourceUid, int shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f, - StatusEffectsComponent? statusEffects = null, bool ignoreInsulation = false) + EntityUid uid, + EntityUid? sourceUid, + int shockDamage, + TimeSpan time, + bool refresh, + float siemensCoefficient = 1f, + StatusEffectsComponent? statusEffects = null, + bool ignoreInsulation = false) { if (!DoCommonElectrocutionAttempt(uid, sourceUid, ref siemensCoefficient, ignoreInsulation) || !DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects)) @@ -396,11 +409,15 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem return true; } - private bool DoCommonElectrocutionAttempt(EntityUid uid, EntityUid? sourceUid, ref float siemensCoefficient, bool ignoreInsulation = false) + private bool DoCommonElectrocutionAttempt( + EntityUid uid, + EntityUid? sourceUid, + ref float siemensCoefficient, + bool ignoreInsulation = false) { - var attemptEvent = new ElectrocutionAttemptEvent(uid, sourceUid, siemensCoefficient, ignoreInsulation ? SlotFlags.NONE : ~SlotFlags.POCKET); + RaiseLocalEvent(uid, attemptEvent, true); // Cancel the electrocution early, so we don't recursively electrocute anything. @@ -411,8 +428,13 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem return true; } - private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, - int? shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f, + private bool DoCommonElectrocution( + EntityUid uid, + EntityUid? sourceUid, + int? shockDamage, + TimeSpan time, + bool refresh, + float siemensCoefficient = 1f, StatusEffectsComponent? statusEffects = null) { if (siemensCoefficient <= 0) @@ -432,7 +454,8 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem return false; } - if (!_statusEffects.TryAddStatusEffect(uid, StatusEffectKey, time, refresh, statusEffects)) + if (!_statusEffects.TryAddStatusEffect(uid, StatusEffectKey, time, refresh, + statusEffects)) return false; var shouldStun = siemensCoefficient > 0.5f; @@ -455,7 +478,8 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem } _stuttering.DoStutter(uid, time * StutteringTimeMultiplier, refresh, statusEffects); - _jittering.DoJitter(uid, time * JitterTimeMultiplier, refresh, JitterAmplitude, JitterFrequency, true, statusEffects); + _jittering.DoJitter(uid, time * JitterTimeMultiplier, refresh, JitterAmplitude, JitterFrequency, true, + statusEffects); _popup.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-popup-player"), uid, uid); @@ -467,6 +491,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem { _popup.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-by-source-popup-others", ("mob", identifiedUid), ("source", (sourceUid.Value))), uid, filter, true); + PlayElectrocutionSound(uid, sourceUid.Value); } else @@ -509,7 +534,9 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem } } - private void OnRandomInsulationMapInit(EntityUid uid, RandomInsulationComponent randomInsulation, + private void OnRandomInsulationMapInit( + EntityUid uid, + RandomInsulationComponent randomInsulation, MapInitEvent args) { if (!TryComp(uid, out var insulated)) @@ -521,12 +548,16 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem SetInsulatedSiemensCoefficient(uid, _random.Pick(randomInsulation.List), insulated); } - private void PlayElectrocutionSound(EntityUid targetUid, EntityUid sourceUid, ElectrifiedComponent? electrified = null) + private void PlayElectrocutionSound( + EntityUid targetUid, + EntityUid sourceUid, + ElectrifiedComponent? electrified = null) { if (!Resolve(sourceUid, ref electrified, false) || !electrified.PlaySoundOnShock) { return; } + _audio.PlayPvs(electrified.ShockNoises, targetUid, AudioParams.Default.WithVolume(electrified.ShockVolume)); } -} +} \ No newline at end of file diff --git a/Content.Server/_White/Cult/GameRule/CultRuleComponent.cs b/Content.Server/_White/Cult/GameRule/CultRuleComponent.cs index 1e1e5a8d89..167306cd48 100644 --- a/Content.Server/_White/Cult/GameRule/CultRuleComponent.cs +++ b/Content.Server/_White/Cult/GameRule/CultRuleComponent.cs @@ -18,37 +18,35 @@ namespace Content.Server._White.Cult.GameRule; [RegisterComponent] public sealed partial class CultRuleComponent : Component { - public readonly SoundSpecifier GreatingsSound = new SoundPathSpecifier("/Audio/White/Cult/blood_cult_greeting.ogg"); + public readonly SoundSpecifier GreetingsSound = new SoundPathSpecifier("/Audio/White/Cult/blood_cult_greeting.ogg"); - [DataField("cultPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] - public static string CultGamePresetPrototype = "Cult"; + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string CultGamePresetPrototype = "Cult"; - [DataField("cultistPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public static string CultistPrototypeId = "Cultist"; + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string CultistPrototypeId = "Cultist"; - [DataField("reaperPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] - public static string ReaperPrototype = "ReaperConstruct"; + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string ReaperPrototype = "ReaperConstruct"; [ViewVariables(VVAccess.ReadOnly), DataField("tileId")] - public static string CultFloor = "CultFloor"; + public string CultFloor = "CultFloor"; - [DataField("eyeColor")] - public static Color EyeColor = Color.FromHex("#f80000"); + [DataField] + public Color EyeColor = Color.FromHex("#f80000"); - public static string HolyWaterReagent = "Holywater"; + public string HolyWaterReagent = "Holywater"; - [DataField("redEyeThreshold")] - public static int ReadEyeThreshold = 5; + [DataField] + public int ReadEyeThreshold = 5; - [DataField("pentagramThreshold")] - public static int PentagramThreshold = 8; + [DataField] + public int PentagramThreshold = 8; - public Dictionary StarCandidates = new(); + [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List StartingItems = []; - [DataField("cultistStartingItems", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List StartingItems = new(); - - [DataField("cultistRolePrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string CultistRolePrototype = "Cultist"; /// @@ -58,19 +56,17 @@ public sealed partial class CultRuleComponent : Component public EntityUid? CultTarget; - public List CurrentCultists = new(); + public List CurrentCultists = []; - public List Constructs = new(); + public List Constructs = []; public CultWinCondition WinCondition; } public enum CultWinCondition : byte { - CultWin, - CultFailure + Win, + Failure } -public sealed class CultNarsieSummoned : EntityEventArgs -{ -} +public sealed class CultNarsieSummoned : EntityEventArgs; diff --git a/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs b/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs index 2736fc7f54..78ab11dab6 100644 --- a/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs +++ b/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs @@ -1,14 +1,9 @@ -using System.Linq; -using Content.Server._Miracle.Components; using Content.Server._Miracle.GulagSystem; using Content.Server.Actions; -using Content.Server.Chat.Managers; +using Content.Server.Antag; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; -using Content.Server.NPC.Systems; -using Content.Server.Roles.Jobs; using Content.Server.RoundEnd; -using Content.Server.Shuttles.Components; using Content.Server.Storage.EntitySystems; using Content.Shared.Body.Systems; using Content.Shared.Humanoid; @@ -17,7 +12,6 @@ using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; -using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Shared.Configuration; using Robust.Shared.Player; @@ -26,40 +20,38 @@ using Content.Shared._White; using Content.Shared._White.Chaplain; using Content.Shared._White.Cult.Components; using Content.Shared.Mind; -using Robust.Shared.Audio.Systems; +using Content.Shared.NPC.Systems; +using Robust.Server.Player; namespace Content.Server._White.Cult.GameRule; public sealed class CultRuleSystem : GameRuleSystem { [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly StorageSystem _storageSystem = default!; [Dependency] private readonly NpcFactionSystem _factionSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; [Dependency] private readonly SharedBodySystem _bodySystem = default!; [Dependency] private readonly SharedRoleSystem _roleSystem = default!; - [Dependency] private readonly JobSystem _jobSystem = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly ActionsSystem _actions = default!; + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly GulagSystem _gulag = default!; - private ISawmill _sawmill = default!; - - private int _minimalCultists; - private int _cultGameRuleMinimapPlayers; + private const int PlayerPerCultist = 10; + private int _minStartingCultists; + private int _maxStartingCultists; public override void Initialize() { base.Initialize(); - _sawmill = Logger.GetSawmill("preset"); - _minimalCultists = _cfg.GetCVar(WhiteCVars.CultMinStartingPlayers); - _cultGameRuleMinimapPlayers = _cfg.GetCVar(WhiteCVars.CultMinPlayers); + _minStartingCultists = _cfg.GetCVar(WhiteCVars.CultMinStartingPlayers); + _maxStartingCultists = _cfg.GetCVar(WhiteCVars.CultMaxStartingPlayers); SubscribeLocalEvent(OnStartAttempt); SubscribeLocalEvent(OnPlayersSpawned); @@ -71,6 +63,101 @@ public sealed class CultRuleSystem : GameRuleSystem SubscribeLocalEvent(OnCultistsStateChanged); } + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + TryRoundStartAttempt(ev, "CULT"); + } + + private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev) + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) + { + DoCultistsStart(cult); + } + } + + private void OnRoundEndText(RoundEndTextAppendEvent ev) + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) + { + var winText = Loc.GetString($"cult-condition-{cult.WinCondition.ToString().ToLower()}"); + ev.AddLine(winText); + + ev.AddLine(Loc.GetString("cultists-list-start")); + + foreach (var (entityName, ckey) in cult.CultistsCache) + { + var lising = Loc.GetString("cultists-list-name", ("name", entityName), ("user", ckey)); + ev.AddLine(lising); + } + } + } + + private void OnNarsieSummon(CultNarsieSummoned ev) + { + var query = EntityQueryEnumerator(); + var rulesQuery = QueryActiveRules(); + while (rulesQuery.MoveNext(out _, out var cult, out _)) + { + cult.WinCondition = CultWinCondition.Win; + _roundEndSystem.EndRound(); + + while (query.MoveNext(out var uid, out _, out var mindContainer, out _)) + { + if (!mindContainer.HasMind || mindContainer.Mind is null) + { + continue; + } + + var reaper = Spawn(cult.ReaperPrototype, Transform(uid).Coordinates); + _mindSystem.TransferTo(mindContainer.Mind.Value, reaper); + + _bodySystem.GibBody(uid); + } + } + } + + private void OnCultistComponentInit(EntityUid uid, CultistComponent component, ComponentInit args) + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) + { + if (!TryComp(uid, out var mindComponent)) + return; + + if (!mindComponent.HasMind) + return; + + cult.CurrentCultists.Add(component); + + if (TryComp(uid, out var actor)) + { + cult.CultistsCache.Add(MetaData(uid).EntityName, actor.PlayerSession.Name); + } + + UpdateCultistsAppearance(cult); + } + } + + private void OnCultistComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args) + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) + { + cult.CurrentCultists.Remove(component); + + foreach (var empower in component.SelectedEmpowers) + { + _actions.RemoveAction(uid, GetEntity(empower)); + } + + RemoveCultistAppearance(uid); + CheckRoundShouldEnd(); + } + } + private void OnCultistsStateChanged(EntityUid uid, CultistComponent component, MobStateChangedEvent ev) { if (ev.NewMobState == MobState.Dead) @@ -79,111 +166,96 @@ public sealed class CultRuleSystem : GameRuleSystem } } - public MindComponent? GetTarget() + private void DoCultistsStart(CultRuleComponent rule) { - var cultistsRule = EntityQuery().FirstOrDefault(); + var eligiblePlayers = + _antagSelection.GetEligiblePlayers(_playerManager.Sessions, rule.CultistRolePrototype); - if (cultistsRule?.CultTarget == null || !TryComp(cultistsRule.CultTarget.Value, out var mind)) + eligiblePlayers.RemoveAll(HasComp); + + if (eligiblePlayers.Count == 0) { - return null; + return; } - return mind; + var cultistsToSelect = + Math.Clamp(_playerManager.PlayerCount / PlayerPerCultist, _minStartingCultists, _maxStartingCultists); + + var selectedCultists = _antagSelection.ChooseAntags(cultistsToSelect, eligiblePlayers); + + foreach (var cultist in selectedCultists) + { + MakeCultist(cultist, rule); + } + + var potentialTargets = FindPotentialTargets(selectedCultists); + rule.CultTarget = _random.PickAndTake(potentialTargets).Mind; + } + + public MindComponent? GetTarget() + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) + { + if (cult.CultTarget == null || !TryComp(cult.CultTarget.Value, out MindComponent? mind)) + { + continue; + } + + return mind; + } + + return null; } public bool CanSummonNarsie() { - var cultistsRule = EntityQuery().FirstOrDefault(); - if (cultistsRule is null) + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) { - return false; + var cultistsAmount = cult.CurrentCultists.Count; + var constructsAmount = cult.Constructs.Count; + var enoughCultists = cultistsAmount + constructsAmount > 10; + + if (!enoughCultists) + { + return false; + } + + var target = GetTarget(); + var targetKilled = target == null || _mindSystem.IsCharacterDeadIc(target); + + return targetKilled; } - var cultistsAmount = cultistsRule.CurrentCultists.Count; - var constructsAmount = cultistsRule.Constructs.Count; - var enoughCultists = cultistsAmount + constructsAmount > 10; - - if (!enoughCultists) - { - return false; - } - - var target = GetTarget(); - var targetKilled = target == null || _mindSystem.IsCharacterDeadIc(target); - - return targetKilled; + return false; } private void CheckRoundShouldEnd() { - var cultistsRule = EntityQuery().FirstOrDefault(); - if (cultistsRule is null) + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) { - return; - } + var aliveCultists = 0; - var aliveCultists = 0; - - foreach (var cultistComponent in cultistsRule.CurrentCultists) - { - var owner = cultistComponent.Owner; - if (!TryComp(owner, out var mobState)) - continue; - - if (_mobStateSystem.IsAlive(owner, mobState)) + foreach (var cultistComponent in cult.CurrentCultists) { - aliveCultists++; + var owner = cultistComponent.Owner; + if (!TryComp(owner, out var mobState)) + continue; + + if (_mobStateSystem.IsAlive(owner, mobState)) + { + aliveCultists++; + } } + + if (aliveCultists != 0) + return; + + cult.WinCondition = CultWinCondition.Failure; + _roundEndSystem.EndRound(); } - - if (aliveCultists != 0) - return; - - cultistsRule.WinCondition = CultWinCondition.CultFailure; - _roundEndSystem.EndRound(); - } - - private void OnCultistComponentInit(EntityUid uid, CultistComponent component, ComponentInit args) - { - var cultistsRule = EntityQuery().FirstOrDefault(); - if (cultistsRule is null) - { - return; - } - - if (!TryComp(uid, out var mindComponent)) - return; - - if (!mindComponent.HasMind) - return; - - cultistsRule.CurrentCultists.Add(component); - - if (TryComp(uid, out var actor)) - { - cultistsRule.CultistsCache.Add(MetaData(uid).EntityName, actor.PlayerSession.Name); - } - - UpdateCultistsAppearance(cultistsRule); - } - - private void OnCultistComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args) - { - var cultistsRule = EntityQuery().FirstOrDefault(); - if (cultistsRule is null) - { - return; - } - - cultistsRule.CurrentCultists.Remove(component); - - foreach (var empower in component.SelectedEmpowers) - { - _actions.RemoveAction(uid, GetEntity(empower)); - } - - RemoveCultistAppearance(uid); - CheckRoundShouldEnd(); } private void RemoveCultistAppearance(EntityUid cultist) @@ -203,104 +275,32 @@ public sealed class CultRuleSystem : GameRuleSystem var cultistsCount = cultRuleComponent.CurrentCultists.Count; var constructsCount = cultRuleComponent.Constructs.Count; var totalCultMembers = cultistsCount + constructsCount; - if (totalCultMembers < CultRuleComponent.ReadEyeThreshold) + if (totalCultMembers < cultRuleComponent.ReadEyeThreshold) return; foreach (var cultistComponent in cultRuleComponent.CurrentCultists) { if (TryComp(cultistComponent.Owner, out var appearanceComponent)) { - appearanceComponent.EyeColor = CultRuleComponent.EyeColor; - Dirty(appearanceComponent); + appearanceComponent.EyeColor = cultRuleComponent.EyeColor; + Dirty(cultistComponent.Owner, appearanceComponent); } - if (totalCultMembers < CultRuleComponent.PentagramThreshold) + if (totalCultMembers < cultRuleComponent.PentagramThreshold) return; EnsureComp(cultistComponent.Owner); } } - private void OnRoundEndText(RoundEndTextAppendEvent ev) + private List FindPotentialTargets(List exclude = null!) { - var cultistsRule = EntityQuery().FirstOrDefault(); - if (cultistsRule is null) - { - return; - } - - var winText = Loc.GetString($"cult-cond-{cultistsRule.WinCondition.ToString().ToLower()}"); - ev.AddLine(winText); - - ev.AddLine(Loc.GetString("cultists-list-start")); - - foreach (var (entityName, ckey) in cultistsRule.CultistsCache) - { - var lising = Loc.GetString("cultists-list-name", ("name", entityName), ("user", ckey)); - ev.AddLine(lising); - } - } - - private void OnStartAttempt(RoundStartAttemptEvent ev) - { - var cultistsRule = EntityQuery().FirstOrDefault(); - if (cultistsRule is null) - { - return; - } - - var minPlayers = _cultGameRuleMinimapPlayers; - if (!ev.Forced && ev.Players.Length < minPlayers) - { - _chatManager.DispatchServerAnnouncement(Loc.GetString("traitor-not-enough-ready-players", - ("readyPlayersCount", ev.Players.Length), ("minimumPlayers", minPlayers))); - - ev.Cancel(); - return; - } - - if (ev.Players.Length != 0) - return; - - _chatManager.DispatchServerAnnouncement(Loc.GetString("traitor-no-one-ready")); - ev.Cancel(); - } - - private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev) - { - var cultistsRule = EntityQuery().FirstOrDefault(); - if (cultistsRule is null) - { - return; - } - - foreach (var player in ev.Players) - { - if (!ev.Profiles.ContainsKey(player.UserId)) - continue; - - cultistsRule.StarCandidates[player] = ev.Profiles[player.UserId]; - } - - var potentialCultists = FindPotentialCultist(cultistsRule.StarCandidates); - var pickedCultist = PickCultists(potentialCultists); - var potentialTargets = FindPotentialTargets(pickedCultist); - - cultistsRule.CultTarget = _random.PickAndTake(potentialTargets).Mind; - - foreach (var pickerCultist in pickedCultist) - { - MakeCultist(pickerCultist); - } - } - - private List FindPotentialTargets(List exclude = null!) - { - var querry = EntityManager.EntityQuery(); + var querry = + EntityManager.EntityQueryEnumerator(); var potentialTargets = new List(); - foreach (var (mind, _, actor) in querry) + while (querry.MoveNext(out var uid, out var mind, out _, out var actor)) { var entity = mind.Mind; @@ -310,7 +310,7 @@ public sealed class CultRuleSystem : GameRuleSystem if (_gulag.IsUserGulaged(actor.PlayerSession.UserId, out _)) continue; - if (exclude?.Contains(actor.PlayerSession) is true) + if (exclude?.Contains(uid) is true) { continue; } @@ -321,134 +321,38 @@ public sealed class CultRuleSystem : GameRuleSystem return potentialTargets; } - private List FindPotentialCultist( - in Dictionary candidates) + public bool MakeCultist(EntityUid cultist, CultRuleComponent rule) { - var list = new List(); - var pendingQuery = GetEntityQuery(); - - foreach (var player in candidates.Keys) - { - // Gulag - if (_gulag.IsUserGulaged(player.UserId, out _)) - continue; - - // Role prevents antag. - if (!_jobSystem.CanBeAntag(player)) - continue; - - // Chaplain - if (!_mindSystem.TryGetMind(player, out _, out var mind) || - mind.OwnedEntity is not { } ownedEntity || HasComp(ownedEntity)) - continue; - - // Latejoin - if (player.AttachedEntity != null && pendingQuery.HasComponent(player.AttachedEntity.Value)) - continue; - - list.Add(player); - } - - var prefList = new List(); - - foreach (var player in list) - { - var profile = candidates[player]; - - if (profile.AntagPreferences.Contains(CultRuleComponent.CultistPrototypeId)) - { - prefList.Add(player); - } - } - - if (prefList.Count == 0) - { - _sawmill.Info("Insufficient preferred cultists, picking at random."); - prefList = list; - return prefList; - } - - if (prefList.Count >= _minimalCultists) - { - return prefList; - } - - var playersToAdd = _minimalCultists - prefList.Count; - - foreach (var prefPlayer in prefList) - { - list.Remove(prefPlayer); - } - - for (var i = 0; i < playersToAdd; i++) - { - var randomPlayer = _random.PickAndTake(list); - prefList.Add(randomPlayer); - } - - return prefList; - } - - private List PickCultists(List prefList) - { - var result = new List(); - - var maxCultists = _cfg.GetCVar(WhiteCVars.CultMaxStartingPlayers); - - if (prefList.Count < _minimalCultists) - { - _sawmill.Info("Insufficient ready players to fill up with cultists, stopping the selection."); - return result; - } - - var actualCultistCount = prefList.Count > maxCultists ? maxCultists : _minimalCultists; - - for (var i = 0; i < actualCultistCount; i++) - { - result.Add(_random.PickAndTake(prefList)); - } - - return result; - } - - public bool MakeCultist(ICommonSession cultist) - { - var cultistRule = EntityQuery().FirstOrDefault(); - - if (cultistRule == null) - { - GameTicker.StartGameRule(CultRuleComponent.CultGamePresetPrototype, out var ruleEntity); - cultistRule = Comp(ruleEntity); - } - if (!_mindSystem.TryGetMind(cultist, out var mindId, out var mind)) { Log.Info("Failed getting mind for picked cultist."); return false; } - if (mind.OwnedEntity is not { } playerEntity) + if (HasComp(mindId)) { - Log.Error("Mind picked for cultist did not have an attached entity."); + Log.Error($"Player {mind.CharacterName} is already a cultist."); return false; } - var cultistComponent = new CultistRoleComponent + var briefing = Loc.GetString("cult-role-greeting"); + _antagSelection.SendBriefing(cultist, briefing, null, rule.GreetingsSound); + + _roleSystem.MindAddRole(mindId, new CultistRoleComponent { - PrototypeId = cultistRule.CultistRolePrototype - }; + PrototypeId = rule.CultistRolePrototype + }); - _roleSystem.MindAddRole(mindId, cultistComponent); - EnsureComp(playerEntity); + EnsureComp(cultist); - _factionSystem.RemoveFaction(playerEntity, "NanoTrasen", false); - _factionSystem.AddFaction(playerEntity, "Cultist"); + _factionSystem.RemoveFaction(cultist, "NanoTrasen", false); + _factionSystem.AddFaction(cultist, "Cultist"); - if (_inventorySystem.TryGetSlotEntity(playerEntity, "back", out var backPack)) + if (_inventorySystem.TryGetSlotEntity(cultist, "back", out var backPack)) { - foreach (var itemPrototype in cultistRule.StartingItems) + foreach (var itemPrototype in rule.StartingItems) { - var itemEntity = Spawn(itemPrototype, Transform(playerEntity).Coordinates); + var itemEntity = Spawn(itemPrototype, Transform(cultist).Coordinates); if (backPack != null) { @@ -457,43 +361,11 @@ public sealed class CultRuleSystem : GameRuleSystem } } - // Notificate player about new role assignment - if (_mindSystem.TryGetSession(mindId, out var session)) - { - _audioSystem.PlayGlobal(cultistRule.GreatingsSound, session); - _chatManager.DispatchServerMessage(session, Loc.GetString("cult-role-greeting")); - } - _mindSystem.TryAddObjective(mindId, mind, "KillCultTargetObjective"); return true; } - private void OnNarsieSummon(CultNarsieSummoned ev) - { - foreach (var rule in EntityQuery()) - { - rule.WinCondition = CultWinCondition.CultWin; - } - - _roundEndSystem.EndRound(); - - var query = EntityQueryEnumerator(); - - while (query.MoveNext(out var uid, out _, out var mindContainer, out _)) - { - if (!mindContainer.HasMind || mindContainer.Mind is null) - { - continue; - } - - var reaper = Spawn(CultRuleComponent.ReaperPrototype, Transform(uid).Coordinates); - _mindSystem.TransferTo(mindContainer.Mind.Value, reaper); - - _bodySystem.GibBody(uid); - } - } - public void TransferRole(EntityUid transferFrom, EntityUid transferTo) { if (HasComp(transferFrom)) @@ -507,7 +379,8 @@ public sealed class CultRuleSystem : GameRuleSystem { cultRule.CultistsCache.Remove(Name(transferFrom)); } + EnsureComp(transferTo); RemComp(transferFrom); } -} +} \ No newline at end of file diff --git a/Content.Server/_White/Cult/Items/Systems/TorchCultistsProviderSystem.cs b/Content.Server/_White/Cult/Items/Systems/TorchCultistsProviderSystem.cs index 755076f69c..01865c34c9 100644 --- a/Content.Server/_White/Cult/Items/Systems/TorchCultistsProviderSystem.cs +++ b/Content.Server/_White/Cult/Items/Systems/TorchCultistsProviderSystem.cs @@ -1,16 +1,16 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Popups; -using Content.Server.Pulling; using Content.Server.Station.Systems; using Content.Server.Station.Components; using Content.Server._White.Cult.Items.Components; +using Content.Shared._White.Cult.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Item; using Content.Shared.Mind.Components; using Content.Shared.Physics; -using Content.Shared._White.Cult; using Content.Shared._White.Cult.Items; +using Content.Shared.Movement.Pulling.Systems; using Robust.Server.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Physics; @@ -18,7 +18,6 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Timing; -using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent; namespace Content.Server._White.Cult.Items.Systems; @@ -36,6 +35,7 @@ public sealed class TorchCultistsProviderSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly PullingSystem _pulling = default!; + [Dependency] private readonly MapSystem _map = default!; public override void Initialize() { @@ -104,18 +104,18 @@ public sealed class TorchCultistsProviderSystem : EntitySystem provider.ItemSelected = args.Target; - var cultists = EntityQuery(); + var cultistsQuery = EntityQueryEnumerator(); var list = new Dictionary(); - foreach (var cultist in cultists) + while (cultistsQuery.MoveNext(out var cultistUid, out _)) { - if (!TryComp(cultist.Owner, out var meta)) + if (!TryComp(cultistUid, out var meta)) return; - if (cultist.Owner == args.User) + if (cultistUid == args.User) continue; - list.Add(meta.Owner.ToString(), meta.EntityName); + list.Add(cultistUid.ToString(), meta.EntityName); } if (list.Count == 0) @@ -138,12 +138,12 @@ public sealed class TorchCultistsProviderSystem : EntitySystem TorchWindowItemSelectedMessage args) { var entityUid = args.Session.AttachedEntity; - var cultists = EntityQuery(); + var cultistsQuery = EntityQueryEnumerator(); - foreach (var cultist in cultists) + while (cultistsQuery.MoveNext(out var cultistUid, out _)) { - if (cultist.Owner.ToString() == args.EntUid) - entityUid = cultist.Owner; + if (cultistUid.ToString() == args.EntUid) + entityUid = cultistUid; } if (entityUid == args.Session.AttachedEntity && entityUid != null) @@ -182,7 +182,7 @@ public sealed class TorchCultistsProviderSystem : EntitySystem return; } - if (_pulling.GetPulled(args.User) != args.Target.Value) + if (_pulling.TryGetPulledEntity(args.User, out var pulled) || pulled != args.Target.Value) { return; } @@ -226,7 +226,7 @@ public sealed class TorchCultistsProviderSystem : EntitySystem // don't spawn inside of solid objects var physQuery = GetEntityQuery(); var valid = true; - foreach (var ent in gridComp.GetAnchoredEntities(tile)) + foreach (var ent in _map.GetAnchoredEntities(grid, gridComp, tile)) { if (!physQuery.TryGetComponent(ent, out var body)) continue; @@ -243,7 +243,7 @@ public sealed class TorchCultistsProviderSystem : EntitySystem if (!valid) continue; - targetCoords = gridComp.GridTileToLocal(tile); + targetCoords = _map.GridTileToLocal(grid, gridComp, tile); break; } @@ -275,4 +275,4 @@ public sealed class TorchCultistsProviderSystem : EntitySystem _pointLight.SetEnabled(torch, false, light); } } -} +} \ No newline at end of file diff --git a/Content.Server/_White/Cult/Items/Systems/VoidTeleportSystem.cs b/Content.Server/_White/Cult/Items/Systems/VoidTeleportSystem.cs index 353c4d7f10..2276460aa3 100644 --- a/Content.Server/_White/Cult/Items/Systems/VoidTeleportSystem.cs +++ b/Content.Server/_White/Cult/Items/Systems/VoidTeleportSystem.cs @@ -1,18 +1,18 @@ using System.Threading; +using Content.Shared._White.Cult.Components; using Content.Shared.Coordinates.Helpers; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction.Events; using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Popups; -using Content.Shared.Pulling.Components; -using Content.Shared._White.Cult; using Content.Shared._White.Cult.Items; +using Content.Shared.Movement.Pulling.Systems; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; +using Robust.Shared.Random; using Robust.Shared.Timing; -using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent; using Timer = Robust.Shared.Timing.Timer; namespace Content.Server._White.Cult.Items.Systems; @@ -27,6 +27,10 @@ public sealed class VoidTeleportSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly IEntityManager _entMan = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly PointLightSystem _pointLight = default!; + [Dependency] private readonly PullingSystem _pulling = default!; + [Dependency] private readonly RobustRandom _random = default!; public override void Initialize() { @@ -75,7 +79,7 @@ public sealed class VoidTeleportSystem : EntitySystem { attempts--; //Get coords to where tp - var random = new Random().Next(component.MinRange, component.MaxRange); + var random = _random.Next(component.MinRange, component.MaxRange); var offset = transform.LocalRotation.ToWorldVec().Normalized(); var direction = transform.LocalRotation.GetDir().ToVec(); var newOffset = offset + direction * random; @@ -97,20 +101,17 @@ public sealed class VoidTeleportSystem : EntitySystem CreatePulse(uid, component); _xform.SetCoordinates(args.User, coords); - transform.AttachToGridOrMap(); + _transform.AttachToGridOrMap(args.User, transform); - var pulled = GetPulledEntity(args.User); - if (pulled != null) + if (_pulling.TryGetPulledEntity(args.User, out var pulled)) { _xform.SetCoordinates(pulled.Value, coords); - - if (TryComp(pulled.Value, out var pulledTransform)) - pulledTransform.AttachToGridOrMap(); + _transform.AttachToGridOrMap(pulled.Value); } //Play tp sound _audio.PlayPvs(component.TeleportInSound, coords); - _audio.PlayPvs(component.TeleportOutSound,oldCoords); + _audio.PlayPvs(component.TeleportOutSound, oldCoords); //Create tp effect _entMan.SpawnEntity(component.TeleportInEffect, coords); @@ -129,45 +130,31 @@ public sealed class VoidTeleportSystem : EntitySystem _appearance.SetData(uid, VeilVisuals.Activated, comp.Active, appearance); } - private EntityUid? GetPulledEntity(EntityUid user) - { - EntityUid? pulled = null; - - if (TryComp(user, out var puller)) - pulled = puller.Pulling; - - return pulled; - } - private void CreatePulse(EntityUid uid, VoidTeleportComponent component) { - if (TryComp(uid, out var light)) -#pragma warning disable RA0002 - light.Energy = 5f; -#pragma warning restore RA0002 + if (_pointLight.TryGetLight(uid, out var light)) + { + _pointLight.SetEnergy(uid, 5f, light); + } Timer.Spawn(component.TimerDelay, () => TurnOffPulse(uid, component), component.Token.Token); } - private void TurnOffPulse(EntityUid uid ,VoidTeleportComponent comp) + private void TurnOffPulse(EntityUid uid, VoidTeleportComponent comp) { - if (!TryComp(uid, out var light)) + if (!_pointLight.TryGetLight(uid, out var light)) return; -#pragma warning disable RA0002 - light.Energy = 1f; -#pragma warning restore RA0002 + _pointLight.SetEnergy(uid, 1f, light); comp.Token = new CancellationTokenSource(); if (comp.UsesLeft <= 0) { comp.Active = false; - UpdateAppearance(uid, comp); + _pointLight.SetEnabled(uid, false); -#pragma warning disable RA0002 - light.Enabled = false; -#pragma warning restore RA0002 + UpdateAppearance(uid, comp); } } -} +} \ No newline at end of file diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Narsie.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Narsie.cs index 1ed209bc12..95474a0751 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Narsie.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Narsie.cs @@ -1,5 +1,4 @@ -using System.Threading; -using Content.Shared._White.Cult; +using Content.Shared._White.Cult; using Robust.Server.GameObjects; using Timer = Robust.Shared.Timing.Timer; diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs index 740e086ff4..ca998c1375 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs @@ -29,7 +29,6 @@ using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Popups; using Content.Shared.Projectiles; -using Content.Shared.Pulling.Components; using Content.Shared.Rejuvenate; using Content.Shared._White.Cult; using Content.Shared._White.Cult.Components; @@ -37,7 +36,8 @@ using Content.Shared._White.Cult.Runes; using Content.Shared._White.Cult.UI; using Content.Shared.Cuffs; using Content.Shared.Mindshield.Components; -using Content.Shared.Pulling; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -66,11 +66,10 @@ public sealed partial class CultSystem : EntitySystem [Dependency] private readonly GunSystem _gunSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly FlammableSystem _flammableSystem = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedCuffableSystem _cuffable = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; - public override void Initialize() { base.Initialize(); @@ -113,8 +112,10 @@ public sealed partial class CultSystem : EntitySystem InitializeConstructs(); InitializeBarrierSystem(); InitializeConstructsAbilities(); + InitializeCultists(); InitializeActions(); InitializeVerb(); + } private float _timeToDraw; @@ -139,7 +140,7 @@ public sealed partial class CultSystem : EntitySystem private readonly SoundPathSpecifier _narsie40Sec = new("/Audio/White/Cult/40sec.ogg"); /* - * Rune draw start ---- + * Rune draw start ---- */ private void OnRuneDrawerUseInHand(EntityUid uid, RuneDrawerProviderComponent component, UseInHandEvent args) @@ -168,17 +169,13 @@ public sealed partial class CultSystem : EntitySystem var runePrototype = args.SelectedItem; var whoCalled = args.Session.AttachedEntity.Value; - if (!TryComp(whoCalled, out var actorComponent)) + if (!TryComp(whoCalled, out _)) return; - if (!TryDraw(whoCalled, runePrototype)) - return; - - /*if (component.UserInterface != null) - _ui.CloseUi(component.UserInterface, actorComponent.PlayerSession);*/ + TryDraw(whoCalled, runePrototype); } - private bool TryDraw(EntityUid whoCalled, string runePrototype) + private void TryDraw(EntityUid whoCalled, string runePrototype) { _timeToDraw = 4f; @@ -193,7 +190,7 @@ public sealed partial class CultSystem : EntitySystem } if (!IsAllowedToDraw(whoCalled)) - return false; + return; var ev = new CultDrawEvent { @@ -202,15 +199,14 @@ public sealed partial class CultSystem : EntitySystem var argsDoAfterEvent = new DoAfterArgs(_entityManager, whoCalled, _timeToDraw, ev, whoCalled) { - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }; if (!_doAfterSystem.TryStartDoAfter(argsDoAfterEvent)) - return false; + return; _audio.PlayPvs("/Audio/White/Cult/butcher.ogg", whoCalled, AudioParams.Default.WithMaxDistance(2f)); - return true; } private void OnDraw(EntityUid uid, CultistComponent comp, CultDrawEvent args) @@ -272,7 +268,7 @@ public sealed partial class CultSystem : EntitySystem return; } - var entityPrototype = _entityManager.GetComponent(args.Used).EntityPrototype; + var entityPrototype = MetaData(args.Used).EntityPrototype; if (entityPrototype == null) return; @@ -300,7 +296,7 @@ public sealed partial class CultSystem : EntitySystem var argsDoAfterEvent = new DoAfterArgs(_entityManager, user, time, ev, target) { - BreakOnUserMove = true, + BreakOnMove = true, NeedHand = true }; @@ -340,7 +336,7 @@ public sealed partial class CultSystem : EntitySystem //Erasing end /* - * Rune draw end ---- + * Rune draw end ---- */ //------------------------------------------// @@ -395,8 +391,8 @@ public sealed partial class CultSystem : EntitySystem } /* - * Base End ---- - */ + * Base End ---- + */ //------------------------------------------// @@ -432,7 +428,7 @@ public sealed partial class CultSystem : EntitySystem if (state.CurrentState != MobState.Dead) { var canBeConverted = _entityManager.TryGetComponent(victim.Value, out var mind) && - mind is {Mind: { }}; + mind is { Mind: { } }; // Проверка, является ли жертва целью _entityManager.TryGetComponent(target?.CurrentEntity, out var targetMind); @@ -551,7 +547,7 @@ public sealed partial class CultSystem : EntitySystem //------------------------------------------// /* - * Buff Rune Start ---- + * Buff Rune Start ---- */ private void OnInvokeBuff(EntityUid uid, CultRuneBuffComponent component, CultRuneInvokeEvent args) @@ -596,13 +592,13 @@ public sealed partial class CultSystem : EntitySystem } /* - * Empower Rune End ---- + * Empower Rune End ---- */ //------------------------------------------// /* - * Teleport rune start ---- + * Teleport rune start ---- */ private void OnInvokeTeleport(EntityUid uid, CultRuneTeleportComponent component, CultRuneInvokeEvent args) @@ -620,22 +616,19 @@ public sealed partial class CultSystem : EntitySystem private bool Teleport(EntityUid rune, EntityUid user, List? victims = null) { - var runes = EntityQuery(); + var runesQuery = EntityQueryEnumerator(); var list = new List(); var labels = new List(); - foreach (var teleportRune in runes) + while (runesQuery.MoveNext(out var runeUid, out var teleportComponent)) { - if (!TryComp(teleportRune.Owner, out var teleportComponent)) - continue; - if (teleportComponent.Label == null) continue; - if (teleportRune.Owner == rune) + if (runeUid == rune) continue; - if (!int.TryParse(teleportRune.Owner.ToString(), out var intValue)) + if (!int.TryParse(runeUid.ToString(), out var intValue)) continue; list.Add(intValue); @@ -707,13 +700,13 @@ public sealed partial class CultSystem : EntitySystem } /* - * Teleport rune end ---- + * Teleport rune end ---- */ //------------------------------------------// /* - * Apocalypse rune start ---- + * Apocalypse rune start ---- */ private void OnInvokeApocalypse(EntityUid uid, CultRuneApocalypseComponent component, CultRuneInvokeEvent args) @@ -756,7 +749,7 @@ public sealed partial class CultSystem : EntitySystem var argsDoAfterEvent = new DoAfterArgs(_entityManager, user, TimeSpan.FromSeconds(40), ev, user) { - BreakOnUserMove = true + BreakOnMove = true }; if (!_doAfterSystem.TryStartDoAfter(argsDoAfterEvent)) @@ -769,7 +762,8 @@ public sealed partial class CultSystem : EntitySystem _chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-started"), "CULT", false, colorOverride: Color.DarkRed); - _audio.PlayGlobal(_narsie40Sec, Filter.Broadcast(), false, AudioParams.Default.WithLoop(true).WithVolume(0.15f)); + _audio.PlayGlobal(_narsie40Sec, Filter.Broadcast(), false, + AudioParams.Default.WithLoop(true).WithVolume(0.15f)); return true; } @@ -800,14 +794,14 @@ public sealed partial class CultSystem : EntitySystem } /* - * Apocalypse rune end ---- + * Apocalypse rune end ---- */ //------------------------------------------// /* - * Revive rune start ---- - */ + * Revive rune start ---- + */ private void OnInvokeRevive(EntityUid uid, CultRuneReviveComponent component, CultRuneInvokeEvent args) { @@ -856,13 +850,13 @@ public sealed partial class CultSystem : EntitySystem } /* -* Revive rune end ---- - */ + * Revive rune end ---- + */ //------------------------------------------// /* - * Barrier rune start ---- + * Barrier rune start ---- */ private void OnInvokeBarrier(EntityUid uid, CultRuneBarrierComponent component, CultRuneInvokeEvent args) @@ -884,14 +878,14 @@ public sealed partial class CultSystem : EntitySystem } /* - * Barrier rune end ---- - */ + * Barrier rune end ---- + */ //------------------------------------------// /* - * Summoning rune start ---- - */ + * Summoning rune start ---- + */ private void OnInvokeSummoning(EntityUid uid, CultRuneSummoningComponent component, CultRuneInvokeEvent args) { @@ -904,7 +898,7 @@ public sealed partial class CultSystem : EntitySystem HashSet cultistHashSet, CultRuneSummoningComponent component) { - var cultists = EntityQuery(); + var cultistsQuery = EntityQueryEnumerator(); var list = new List(); var labels = new List(); @@ -914,15 +908,13 @@ public sealed partial class CultSystem : EntitySystem return false; } - foreach (var cultist in cultists) + while (cultistsQuery.MoveNext(out var cultistUid, out _)) { - if (!TryComp(cultist.Owner, out var meta)) + var meta = MetaData(cultistUid); + if (cultistHashSet.Contains(cultistUid)) continue; - if (cultistHashSet.Contains(cultist.Owner)) - continue; - - if (!int.TryParse(cultist.Owner.ToString(), out var intValue)) + if (!int.TryParse(cultistUid.ToString(), out var intValue)) continue; list.Add(intValue); @@ -964,7 +956,7 @@ public sealed partial class CultSystem : EntitySystem var target = new EntityUid(args.SelectedItem); var baseRune = component.BaseRune; - if (!TryComp(target, out var pullableComponent)) + if (!TryComp(target, out var pullableComponent)) return; if (!TryComp(target, out var cuffableComponent)) @@ -1004,14 +996,14 @@ public sealed partial class CultSystem : EntitySystem } /* - * Summoning rune end ---- - */ + * Summoning rune end ---- + */ //------------------------------------------// /* - * BloodBoil rune start ---- - */ + * BloodBoil rune start ---- + */ private void OnInvokeBloodBoil(EntityUid uid, CultRuneBloodBoilComponent component, CultRuneInvokeEvent args) { @@ -1126,14 +1118,14 @@ public sealed partial class CultSystem : EntitySystem } /* - * BloodBoil rune end ---- - */ + * BloodBoil rune end ---- + */ //------------------------------------------// /* - * Empower rune start ---- - */ + * Empower rune start ---- + */ private void OnActiveInWorld(EntityUid uid, CultEmpowerComponent component, ActivateInWorldEvent args) { @@ -1183,13 +1175,13 @@ public sealed partial class CultSystem : EntitySystem } /* - * Empower rune end ---- - */ + * Empower rune end ---- + */ //------------------------------------------// /* - * Helpers Start ---- + * Helpers Start ---- */ private EntityUid? FindNearestTarget(EntityUid uid, List targets) @@ -1265,7 +1257,7 @@ public sealed partial class CultSystem : EntitySystem return; } - var pos = transComp.MapPosition; + var pos = _transform.GetMapCoordinates(uid, transComp); var x = (int) pos.X; var y = (int) pos.Y; var posText = $"(x = {x}, y = {y})"; @@ -1339,20 +1331,19 @@ public sealed partial class CultSystem : EntitySystem private void StopPulling(EntityUid target, bool checkPullable = true) { // break pulls before portal enter so we dont break shit - if (checkPullable && TryComp(target, out var pullable) && pullable.BeingPulled) + if (checkPullable && TryComp(target, out var pullable) && pullable.BeingPulled) { - _pulling.TryStopPull(pullable); + _pulling.TryStopPull(target, pullable); } - if (TryComp(target, out var pulling) - && pulling.Pulling != null && - TryComp(pulling.Pulling.Value, out var subjectPulling)) + if (_pulling.TryGetPulledEntity(target, out var pulledEntity) + && TryComp(pulledEntity, out PullableComponent? subjectPulling)) { - _pulling.TryStopPull(subjectPulling); + _pulling.TryStopPull(pulledEntity.Value, subjectPulling); } } /* - * Helpers End ---- + * Helpers End ---- */ -} +} \ No newline at end of file diff --git a/Content.Server/_White/Cult/UI/TeleportSpellEui.cs b/Content.Server/_White/Cult/UI/TeleportSpellEui.cs index 4923740490..0947aec86a 100644 --- a/Content.Server/_White/Cult/UI/TeleportSpellEui.cs +++ b/Content.Server/_White/Cult/UI/TeleportSpellEui.cs @@ -1,14 +1,11 @@ using Content.Server.EUI; using Content.Server.Popups; using Content.Server._White.Cult.Runes.Comps; -using Content.Shared.Actions; using Content.Shared.Eui; using Content.Shared.Popups; using Content.Shared._White.Cult.UI; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; -using Robust.Server.GameObjects; -using Robust.Shared.Map; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Robust.Shared.Timing; namespace Content.Server._White.Cult.UI; @@ -16,45 +13,37 @@ namespace Content.Server._White.Cult.UI; public sealed class TeleportSpellEui : BaseEui { [Dependency] private readonly EntityManager _entityManager = default!; - private SharedTransformSystem _transformSystem; - private SharedPullingSystem _pulling; - private PopupSystem _popupSystem; + private readonly SharedTransformSystem _transformSystem; + private readonly PullingSystem _pulling; + private readonly PopupSystem _popupSystem; - - private EntityUid _performer; - private EntityUid _target; - - private EntityCoordinates _initialOwnerCoords; - private EntityCoordinates _initialTargetCoords; + private readonly EntityUid _performer; + private readonly EntityUid _target; private bool _used; - public TeleportSpellEui(EntityUid performer, EntityUid target) { IoCManager.InjectDependencies(this); _transformSystem = _entityManager.System(); - _pulling = _entityManager.System(); + _pulling = _entityManager.System(); _popupSystem = _entityManager.System(); _performer = performer; _target = target; - _initialOwnerCoords = _entityManager.GetComponent(_performer).Coordinates; - _initialTargetCoords = _entityManager.GetComponent(_target).Coordinates; - - Timer.Spawn(TimeSpan.FromSeconds(10), Close ); + Timer.Spawn(TimeSpan.FromSeconds(10), Close); } public override EuiStateBase GetNewState() { - var runes = _entityManager.EntityQuery(); + var runesQuery = _entityManager.EntityQueryEnumerator(); var state = new TeleportSpellEuiState(); - foreach (var rune in runes) + while (runesQuery.MoveNext(out var runeUid, out var rune)) { - state.Runes.Add((int)rune.Owner, rune.Label!); + state.Runes.Add((int) runeUid, rune.Label!); } return state; @@ -64,7 +53,10 @@ public sealed class TeleportSpellEui : BaseEui { base.HandleMessage(msg); - if(_used) return; + if (_used) + { + return; + } if (msg is not TeleportSpellTargetRuneSelected cast) { @@ -72,23 +64,24 @@ public sealed class TeleportSpellEui : BaseEui } var performerPosition = _entityManager.GetComponent(_performer).Coordinates; - var targetPosition = _entityManager.GetComponent(_target).Coordinates;; + var targetPosition = _entityManager.GetComponent(_target).Coordinates; performerPosition.TryDistance(_entityManager, targetPosition, out var distance); - if(distance > 1.5f) + if (distance > 1.5f) { _popupSystem.PopupEntity("Too far", _performer, PopupType.Medium); return; } - TransformComponent? runeTransform = null!; + TransformComponent? runeTransform = null; - foreach (var runeComponent in _entityManager.EntityQuery()) + var teleportRuneQuery = _entityManager.EntityQueryEnumerator(); + while (teleportRuneQuery.MoveNext(out var runeUid, out _, out var transformComponent)) { - if (runeComponent.Owner == new EntityUid(cast.RuneUid)) + if (runeUid == new EntityUid(cast.RuneUid)) { - runeTransform = _entityManager.GetComponent(runeComponent.Owner); + runeTransform = transformComponent; } } @@ -102,19 +95,19 @@ public sealed class TeleportSpellEui : BaseEui _used = true; // break pulls before portal enter so we dont break shit - if (_entityManager.TryGetComponent(_target, out var pullable) && pullable.BeingPulled) + if (_entityManager.TryGetComponent(_target, out var pullable) && pullable.BeingPulled) { - _pulling.TryStopPull(pullable); + _pulling.TryStopPull(_target, pullable); } - if (_entityManager.TryGetComponent(_target, out var pulling) - && pulling.Pulling != null && - _entityManager.TryGetComponent(pulling.Pulling.Value, out var subjectPulling)) + if (_entityManager.TryGetComponent(_target, out var pulling) + && pulling.Pulling != null + && _entityManager.TryGetComponent(pulling.Pulling.Value, out var subjectPulling)) { - _pulling.TryStopPull(subjectPulling); + _pulling.TryStopPull(pulling.Pulling.Value, subjectPulling); } _transformSystem.SetCoordinates(_target, runeTransform.Coordinates); Close(); } -} +} \ No newline at end of file diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index b347c6da16..a4f5e15fa9 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Numerics; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; @@ -273,6 +274,23 @@ public sealed class PullingSystem : EntitySystem return false; } + // WD ADDED + public bool TryGetPulledEntity( + EntityUid puller, + [NotNullWhen(true)] out EntityUid? pulled, + PullerComponent? component = null) + { + pulled = null; + if (!Resolve(puller, ref component)) + { + return false; + } + + pulled = component.Pulling; + return pulled is not null; + } + // WD ADDED END + public bool IsPulling(EntityUid puller, PullerComponent? component = null) { return Resolve(puller, ref component, false) && component.Pulling != null; diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/abilities.ftl b/Resources/Locale/ru-RU/White/stuff.ftl/abilities.ftl deleted file mode 100644 index 6f2533ab9b..0000000000 --- a/Resources/Locale/ru-RU/White/stuff.ftl/abilities.ftl +++ /dev/null @@ -1,14 +0,0 @@ -artificer-create-soul-stone-action-name = Создать камень души. -artificer-create-soul-stone-action-description = Это заклинание проникает в царство Нар-Си, вызывая один из легендарных фрагментов через время и пространство. -artificer-create-construct-shell-action-name = Создать оболочку конструкта. -artificer-create-construct-shell-action-description = Это заклинание проникает в царство Нар-Си, вызывая один из легендарных фрагментов через время и пространство. -artificer-convert-cultist-floor-action-name = Создать культистский пол. -convert-cultist-floor-action-description = Это заклинание возводит под вами культистский пол. -artificer-create-cultist-wall-action-name = Создать стену культа. -artificer-create-cultist-wall-action-description = Это заклинание возводит стену культа. -artificer-create-cultist-airlock-action-name = Создать шлюз культа. -artificer-create-cultist-airlock-action-description = Это заклинание возводит шлюз культа. -wraith-phase-action-name = Фазовый Сдвиг -wraith-phase-action-description = Это заклинание позволяет проходить сквозь стены, подобно бесплотному полету волшебника. -juggernaut-create-wall-action-name = Щит -juggernaut-create-wall-action-description = Это заклинание создает временное, невидимое, силовое поле для защиты себя и союзников от подавляющего огня. diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/archives.ftl b/Resources/Locale/ru-RU/White/stuff.ftl/archives.ftl deleted file mode 100644 index 2819715dbb..0000000000 --- a/Resources/Locale/ru-RU/White/stuff.ftl/archives.ftl +++ /dev/null @@ -1,2 +0,0 @@ -ent-CultClothingBlindfold = повязка Зилота - .desc = Повязка, наделенная странной силой. diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/construct.ftl b/Resources/Locale/ru-RU/White/stuff.ftl/construct.ftl deleted file mode 100644 index 1e8f78fd32..0000000000 --- a/Resources/Locale/ru-RU/White/stuff.ftl/construct.ftl +++ /dev/null @@ -1,3 +0,0 @@ -ghost-role-information-soul-shard-name = Осколок Души -ghost-role-information-soul-shard-description = Станьте слугой кровавого культа! -ghost-role-information-soul-shard-rules = Примите форму одной из конструкций культа и помогите вашим Хозяевам вернуть Нар'Си в этот мир! diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/cult-structure.ftl b/Resources/Locale/ru-RU/White/stuff.ftl/cult-structure.ftl deleted file mode 100644 index 7246b6dca5..0000000000 --- a/Resources/Locale/ru-RU/White/stuff.ftl/cult-structure.ftl +++ /dev/null @@ -1,21 +0,0 @@ -ent-CultRunicMetal = рунический металл - .desc = Необычный лист металла с пульсирующей руной. - .suffix = Полный -ent-CultRunicMetal1 = рунический металл - .desc = Необычный лист металла с пульсирующей руной. - .suffix = Один -ent-CultRunicMetal20 = рунический металл - .desc = Необычный лист металла с пульсирующей руной. - .suffix = 20 -ent-CultBloodAltar = алтарь - .desc = Кровавый алтарь, посвященный какому-то существу. -ent-CultForge = кузница - .desc = Кузница, в которой изготавливается нечестивое оружие. -ent-CultGirder = руническая балка - .desc = Большой конструктивный элемент, изготовленный из металла. На этом есть руна. -ent-AirlockGlassCult = рунический шлюз - .desc = Странный стеклянный шлюз с руной. -cult-structure-craft-not-enough-metal = Недостаточно металла. -cult-structure-craft-craft-failed = Не удалось начать постройку. -cult-structure-craft-blocked = Что-то мешает построить. -cult-structure-craft-another-structure-nearby = Слишком близко к другой такой постройке. diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/runes-entities.ftl b/Resources/Locale/ru-RU/White/stuff.ftl/runes-entities.ftl deleted file mode 100644 index 35a97e75af..0000000000 --- a/Resources/Locale/ru-RU/White/stuff.ftl/runes-entities.ftl +++ /dev/null @@ -1,11 +0,0 @@ -ent-OfferingRune = руна предпонесения -ent-BuffRune = руна усиления -ent-EmpoweringRune = руна могущества -ent-TeleportRune = руна телепортации -ent-SummoningRune = руна призыва -ent-ReviveRune = руна воскрешения -ent-BarrierRune = руна барьера -ent-BloodBoilRune = руна кипящей крови -ent-ApocalypseRune = ритуал пространственного разрыва - -runes-window-title = Руны diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/shuttle-curse.ftl b/Resources/Locale/ru-RU/White/stuff.ftl/shuttle-curse.ftl deleted file mode 100644 index f9f0254fac..0000000000 --- a/Resources/Locale/ru-RU/White/stuff.ftl/shuttle-curse.ftl +++ /dev/null @@ -1,8 +0,0 @@ -shuttle-curse-not-cultist = Сфера выпадает из ваших рук -shuttle-curse-shuttle-not-called = Сфера доносит вам, что шаттл не вызван -shuttle-curse-max-curses = Сфера доносит, что шаттл больше нельзя проклясть -shuttle-curse-cooldown = Сфера доносит, что ей нужно собрать силы на прокльяте -shuttle-curse-shuttle-arrived = Сфера доносит, что уже слишком поздно -shuttle-curse-shuttle-delayed = Сфера доносит, что прокльяте наложено -ent-CultShuttleCurse = сфера проклятия - .desc = Cтранная каменная сфера, пульсирующая красным светом. diff --git a/Resources/Locale/ru-RU/cult/abilities.ftl b/Resources/Locale/ru-RU/_white/cult/abilities.ftl similarity index 78% rename from Resources/Locale/ru-RU/cult/abilities.ftl rename to Resources/Locale/ru-RU/_white/cult/abilities.ftl index 0575e85db3..7a7ff88001 100644 --- a/Resources/Locale/ru-RU/cult/abilities.ftl +++ b/Resources/Locale/ru-RU/_white/cult/abilities.ftl @@ -1,16 +1,23 @@ artificer-create-soul-stone-action-name = Создать камень души. artificer-create-soul-stone-action-description = Это заклинание проникает в царство Нар-Си, вызывая один из легендарных фрагментов через время и пространство. + artificer-create-construct-shell-action-name = Создать оболочку конструкта. artificer-create-construct-shell-action-description = Это заклинание проникает в царство Нар-Си, вызывая один из легендарных фрагментов через время и пространство. -artificer-convert-cultist-floor-action-name = Создать культистский пол. -convert-cultist-floor-action-description = Это заклинание возводит под вами культистский пол. -artificer-create-cultist-wall-action-name = Создать культистскую стену. -artificer-create-cultist-wall-action-description = Это заклинание возводит культистскую стену. + +artificer-convert-cultist-floor-action-name = Создать пол культа. +artificer-convert-cultist-floor-action-description = Это заклинание возводит под вами пол культа. + +artificer-create-cultist-wall-action-name = Создать стену культа. +artificer-create-cultist-wall-action-description = Это заклинание возводит стену культа. + +artificer-create-cultist-airlock-action-name = Создать шлюз культа. +artificer-create-cultist-airlock-action-description = Это заклинание возводит шлюз культа. + wraith-phase-action-name = Фазовый Сдвиг wraith-phase-action-description = Это заклинание позволяет проходить сквозь стены, подобно бесплотному полету волшебника. -juggernaut-create-wall-action-name = Щит -juggernaut-create-wall-action-description = Это заклинание создает временное, невидимое, силовое поле для защиты себя и союзников от подавляющего огня. +juggernaut-create-wall-action-name = Щит +juggernaut-create-wall-action-description = Это заклинание создает временное невидимое силовое поле для защиты себя и союзников от подавляющего огня. ent-ActionCultTwistedConstruction = Искажённое Воздействие .desc = Зловещее заклинание, которое используют для превращения металла в рунический металл. diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/constructs.ftl b/Resources/Locale/ru-RU/_white/cult/constructs.ftl similarity index 100% rename from Resources/Locale/ru-RU/White/stuff.ftl/constructs.ftl rename to Resources/Locale/ru-RU/_white/cult/constructs.ftl diff --git a/Resources/Locale/ru-RU/White/cult.ftl b/Resources/Locale/ru-RU/_white/cult/cult.ftl similarity index 69% rename from Resources/Locale/ru-RU/White/cult.ftl rename to Resources/Locale/ru-RU/_white/cult/cult.ftl index e0229b6cd4..c7ad06153d 100644 --- a/Resources/Locale/ru-RU/White/cult.ftl +++ b/Resources/Locale/ru-RU/_white/cult/cult.ftl @@ -1,19 +1,11 @@ -cult-role-greeting = - Вы - член культа! - Ваши цели перечислены в меню персонажа. - В ваш рюкзак были добавлены предметы, которые помогут вам. - И помните - вы не единственный. - Слава Нар`си! - -cult-cond-cultwin = Культ одержал победу -cult-cond-cultfailure = Экипаж уничтожил культ - -cultists-list-start = Культистами были: -cultists-list-name = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) -soul-shard-name = Душа { $soul } +soul-shard-name = Душа { $soul } soul-shard-description = В этом камне заключена душа { $soul } cult-too-much-empowers = Слишком много способностей +ghost-role-information-soul-shard-name = Осколок Души +ghost-role-information-soul-shard-description = Станьте слугой кровавого культа! +ghost-role-information-soul-shard-rules = Примите форму одной из конструкций культа и помогите вашим Хозяевам вернуть Нар'Си в этот мир! + cult-started-drawing-rune-end = Культ начал рисовать руну призыва! cult-started-erasing-rune = Вы начали стирать руну. cult-erased-rune = Вы стёрли руну. @@ -42,17 +34,7 @@ cult-teleport-rune-default-label = безымянная метка cult-narsie-summon-drawn-position = Культ закончил рисовать руну ритуала разрыва измерений! Координаты: { $posText } cult-cant-draw-rune = Нельзя рисовать руну в космосе. -ent-SoulShard = камень душ - .desc = Мистический светящийся осколок. - -ent-SoulShardGhost = камень душ - .desc = Мистический светящийся осколок. - -ent-WetStone = точильный камень - .desc = Используется для заточки кромок стальных инструментов. - -ent-CultSharpener = древний точильный камень - .desc = Используется для заточки кромок стальных инструментов. +runes-window-title = Руны chat-manager-cult-channel-name = Культ chat-manager-send-cult-chat-wrap-message = [bold]\[{ $channelName }\] [BubbleHeader]{ $player }[/BubbleHeader]:[/bold] [BubbleContent]{ $message }[/BubbleContent] diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/effects.ftl b/Resources/Locale/ru-RU/_white/cult/effects.ftl similarity index 100% rename from Resources/Locale/ru-RU/White/stuff.ftl/effects.ftl rename to Resources/Locale/ru-RU/_white/cult/effects.ftl diff --git a/Resources/Locale/ru-RU/_white/cult/entities.ftl b/Resources/Locale/ru-RU/_white/cult/entities.ftl new file mode 100644 index 0000000000..3d3e27ff8f --- /dev/null +++ b/Resources/Locale/ru-RU/_white/cult/entities.ftl @@ -0,0 +1,57 @@ +ent-SoulShard = камень душ + .desc = Мистический светящийся осколок. + +ent-SoulShardGhost = камень душ + .desc = Мистический светящийся осколок. + .suffix = Роль призраков + +ent-WetStone = точильный камень + .desc = Используется для заточки кромок стальных инструментов. + +ent-CultSharpener = древний точильный камень + .desc = Используется для заточки кромок стальных инструментов. + +ent-CultRunicMetal = рунический металл + .desc = Необычный лист металла с пульсирующей руной. + .suffix = Полный + +ent-CultRunicMetal1 = рунический металл + .desc = Необычный лист металла с пульсирующей руной. + .suffix = Один + +ent-CultRunicMetal20 = рунический металл + .desc = Необычный лист металла с пульсирующей руной. + .suffix = 20 + +ent-CultBloodAltar = алтарь + .desc = Кровавый алтарь, посвященный какому-то существу. + +ent-CultForge = кузница + .desc = Кузница, в которой изготавливается нечестивое оружие. + +ent-CultGirder = руническая балка + .desc = Большой конструктивный элемент, изготовленный из металла. На этом есть руна. + +ent-AirlockGlassCult = рунический шлюз + .desc = Странный стеклянный шлюз с руной. + +ent-CultClothingBlindfold = повязка Зилота + .desc = Повязка, наделенная странной силой. + +ent-OfferingRune = руна предпонесения + +ent-BuffRune = руна усиления + +ent-EmpoweringRune = руна могущества + +ent-TeleportRune = руна телепортации + +ent-SummoningRune = руна призыва + +ent-ReviveRune = руна воскрешения + +ent-BarrierRune = руна барьера + +ent-BloodBoilRune = руна кипящей крови + +ent-ApocalypseRune = ритуал пространственного разрыва \ No newline at end of file diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/cultist-factory.ftl b/Resources/Locale/ru-RU/_white/cult/factory.ftl similarity index 99% rename from Resources/Locale/ru-RU/White/stuff.ftl/cultist-factory.ftl rename to Resources/Locale/ru-RU/_white/cult/factory.ftl index 7be764e835..fc1f5a8654 100644 --- a/Resources/Locale/ru-RU/White/stuff.ftl/cultist-factory.ftl +++ b/Resources/Locale/ru-RU/_white/cult/factory.ftl @@ -1,11 +1,15 @@ cultist-factory-charging = { $name } будут заряжаться ещё { $seconds } секунд cultist-factory-create = Создать { $itemName } cultist-factory-too-far = Слишком далеко + ent-AltarTome = архивы .desc = Стол, заваленный тайными рукописями и книгами на неизвестных языках. + ent-CultRobeModify = одеяние флагелланта .desc = Какая-то религиозная роба. + ent-CultMirrorShield = зеркальный щит .desc = Щит с зеркалом на лицевой стороне, на котором изображен какой-то религиозный знак. + ent-CultOuterArmor = бронированная мантия .desc = С первого взгляда кажется, что это простая мантия, но на ней имеется элементы брони. diff --git a/Resources/Locale/ru-RU/cult/cult-item.ftl b/Resources/Locale/ru-RU/_white/cult/item.ftl similarity index 100% rename from Resources/Locale/ru-RU/cult/cult-item.ftl rename to Resources/Locale/ru-RU/_white/cult/item.ftl diff --git a/Resources/Locale/ru-RU/_white/cult/preset.ftl b/Resources/Locale/ru-RU/_white/cult/preset.ftl new file mode 100644 index 0000000000..9767c7b0a4 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/cult/preset.ftl @@ -0,0 +1,14 @@ +cult-title = Культ +cult-description = Страх — самое древнее и сильное из человеческих чувств, а самый древний и самый сильный страх — страх неведомого +cult-role-greeting = + Вы - член культа! + Ваши цели перечислены в меню персонажа. + В ваш рюкзак были добавлены предметы, которые помогут вам. + И помните - вы не единственный. + Слава Нар`си! + +cult-condition-win = Культ одержал победу +cult-condition-failure = Экипаж уничтожил культ + +cultists-list-start = Культистами были: +cultists-list-name = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) \ No newline at end of file diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/pylon.ftl b/Resources/Locale/ru-RU/_white/cult/pylon.ftl similarity index 100% rename from Resources/Locale/ru-RU/White/stuff.ftl/pylon.ftl rename to Resources/Locale/ru-RU/_white/cult/pylon.ftl diff --git a/Resources/Locale/ru-RU/_white/cult/runes-entities.ftl b/Resources/Locale/ru-RU/_white/cult/runes-entities.ftl new file mode 100644 index 0000000000..c65238fb38 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/cult/runes-entities.ftl @@ -0,0 +1,2 @@ + + diff --git a/Resources/Locale/ru-RU/cult/shuttle-curse.ftl b/Resources/Locale/ru-RU/_white/cult/shuttle-curse.ftl similarity index 100% rename from Resources/Locale/ru-RU/cult/shuttle-curse.ftl rename to Resources/Locale/ru-RU/_white/cult/shuttle-curse.ftl diff --git a/Resources/Locale/ru-RU/_white/cult/structure.ftl b/Resources/Locale/ru-RU/_white/cult/structure.ftl new file mode 100644 index 0000000000..02b81c9bbd --- /dev/null +++ b/Resources/Locale/ru-RU/_white/cult/structure.ftl @@ -0,0 +1,4 @@ +cult-structure-craft-not-enough-metal = Недостаточно металла. +cult-structure-craft-craft-failed = Не удалось начать постройку. +cult-structure-craft-blocked = Что-то мешает построить. +cult-structure-craft-another-structure-nearby = Слишком близко к другой такой постройке. diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/tile.ftl b/Resources/Locale/ru-RU/_white/cult/tile.ftl similarity index 100% rename from Resources/Locale/ru-RU/White/stuff.ftl/tile.ftl rename to Resources/Locale/ru-RU/_white/cult/tile.ftl diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/veil-shifter.ftl b/Resources/Locale/ru-RU/_white/cult/veil-shifter.ftl similarity index 100% rename from Resources/Locale/ru-RU/White/stuff.ftl/veil-shifter.ftl rename to Resources/Locale/ru-RU/_white/cult/veil-shifter.ftl diff --git a/Resources/Locale/ru-RU/cult/verb.ftl b/Resources/Locale/ru-RU/_white/cult/verb.ftl similarity index 100% rename from Resources/Locale/ru-RU/cult/verb.ftl rename to Resources/Locale/ru-RU/_white/cult/verb.ftl diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/void-torch.ftl b/Resources/Locale/ru-RU/_white/cult/void-torch.ftl similarity index 100% rename from Resources/Locale/ru-RU/White/stuff.ftl/void-torch.ftl rename to Resources/Locale/ru-RU/_white/cult/void-torch.ftl diff --git a/Resources/Locale/ru-RU/cult/constructs.ftl b/Resources/Locale/ru-RU/cult/constructs.ftl deleted file mode 100644 index 8ead17cd65..0000000000 --- a/Resources/Locale/ru-RU/cult/constructs.ftl +++ /dev/null @@ -1,8 +0,0 @@ -ent-JuggernautConstruct = Джаггернаут - .desc = "" -ent-ArtificerConstruct = Ремесленник - .desc = "" -ent-WraithConstruct = Фантом - .desc = "" -ent-ReaperConstruct = Жнец - .desc = "" diff --git a/Resources/Locale/ru-RU/cult/cult-structure.ftl b/Resources/Locale/ru-RU/cult/cult-structure.ftl deleted file mode 100644 index 7246b6dca5..0000000000 --- a/Resources/Locale/ru-RU/cult/cult-structure.ftl +++ /dev/null @@ -1,21 +0,0 @@ -ent-CultRunicMetal = рунический металл - .desc = Необычный лист металла с пульсирующей руной. - .suffix = Полный -ent-CultRunicMetal1 = рунический металл - .desc = Необычный лист металла с пульсирующей руной. - .suffix = Один -ent-CultRunicMetal20 = рунический металл - .desc = Необычный лист металла с пульсирующей руной. - .suffix = 20 -ent-CultBloodAltar = алтарь - .desc = Кровавый алтарь, посвященный какому-то существу. -ent-CultForge = кузница - .desc = Кузница, в которой изготавливается нечестивое оружие. -ent-CultGirder = руническая балка - .desc = Большой конструктивный элемент, изготовленный из металла. На этом есть руна. -ent-AirlockGlassCult = рунический шлюз - .desc = Странный стеклянный шлюз с руной. -cult-structure-craft-not-enough-metal = Недостаточно металла. -cult-structure-craft-craft-failed = Не удалось начать постройку. -cult-structure-craft-blocked = Что-то мешает построить. -cult-structure-craft-another-structure-nearby = Слишком близко к другой такой постройке. diff --git a/Resources/Locale/ru-RU/cult/cultist-factory.ftl b/Resources/Locale/ru-RU/cult/cultist-factory.ftl deleted file mode 100644 index 7be764e835..0000000000 --- a/Resources/Locale/ru-RU/cult/cultist-factory.ftl +++ /dev/null @@ -1,11 +0,0 @@ -cultist-factory-charging = { $name } будут заряжаться ещё { $seconds } секунд -cultist-factory-create = Создать { $itemName } -cultist-factory-too-far = Слишком далеко -ent-AltarTome = архивы - .desc = Стол, заваленный тайными рукописями и книгами на неизвестных языках. -ent-CultRobeModify = одеяние флагелланта - .desc = Какая-то религиозная роба. -ent-CultMirrorShield = зеркальный щит - .desc = Щит с зеркалом на лицевой стороне, на котором изображен какой-то религиозный знак. -ent-CultOuterArmor = бронированная мантия - .desc = С первого взгляда кажется, что это простая мантия, но на ней имеется элементы брони. diff --git a/Resources/Locale/ru-RU/cult/effects.ftl b/Resources/Locale/ru-RU/cult/effects.ftl deleted file mode 100644 index 3d8f722cca..0000000000 --- a/Resources/Locale/ru-RU/cult/effects.ftl +++ /dev/null @@ -1 +0,0 @@ -reagent-effect-guidebook-deconvert-cultist = Деконвертирует культиста diff --git a/Resources/Locale/ru-RU/cult/pylon.ftl b/Resources/Locale/ru-RU/cult/pylon.ftl deleted file mode 100644 index b00cdb1f00..0000000000 --- a/Resources/Locale/ru-RU/cult/pylon.ftl +++ /dev/null @@ -1,4 +0,0 @@ -pylon-toggle-on = Кристалл воспаряет над пьедесталом, начиная пульсировать -pylon-toggle-off = Кристалл перестаёт пульсировать, опускаясь на пьедестал -ent-CultPylon = пилон - .desc = Мистический конструкция. diff --git a/Resources/Locale/ru-RU/cult/veil-shifter.ftl b/Resources/Locale/ru-RU/cult/veil-shifter.ftl deleted file mode 100644 index 8427b5176c..0000000000 --- a/Resources/Locale/ru-RU/cult/veil-shifter.ftl +++ /dev/null @@ -1,5 +0,0 @@ -void-teleport-not-cultist = Посох выпадает у вас из рук. -void-teleport-drained = В этом посохе больше нет энергии. -void-teleport-cooldown = Посох накапливает заряд. -ent-CultVeilShifter = преобразователь покрова - .desc = Посох, излучающий странную энергию. diff --git a/Resources/Locale/ru-RU/cult/void-torch.ftl b/Resources/Locale/ru-RU/cult/void-torch.ftl deleted file mode 100644 index 48cfdd7625..0000000000 --- a/Resources/Locale/ru-RU/cult/void-torch.ftl +++ /dev/null @@ -1,9 +0,0 @@ -cult-torch-window-title = Отправить предмет -cult-torch-not-cultist = Факел выпадает из ваших рук -cult-torch-drained = Факел опустошён -cult-torch-cooldown = Факел накапливает энергию -cult-torch-cultists-not-found = Факел не обнаружил приспешников -cult-torch-no-cultist = Факел потерял связь с приспешником -cult-torch-item-send = Факел отправил предмет -ent-CultTorch = факел пустоты - .desc = Странный факел. diff --git a/Resources/Locale/ru-RU/white/gamemodes/cult.ftl b/Resources/Locale/ru-RU/white/gamemodes/cult.ftl deleted file mode 100644 index 642b777e87..0000000000 --- a/Resources/Locale/ru-RU/white/gamemodes/cult.ftl +++ /dev/null @@ -1,7 +0,0 @@ -cult-cond-cultwin = Экипаж пососал -cult-cond-cultfailure = Экипаж не пососал -cultists-list-start = Культистами были: -cultists-list-name = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) -soul-shard-name = Душа { $soul } -soul-shard-description = В этом камне заключена душа { $soul } -cult-too-much-empowers = Слишком много способностей diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 7f5478cf94..6011dae695 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -146,7 +146,7 @@ noSpawn: true components: - type: CultRule - cultistStartingItems: + startingItems: - RitualDagger - CultRunicMetal20 diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index 1aff5e2f4e..8e0847e2b8 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -174,9 +174,10 @@ id: Cult alias: - cult - name: CULT - description: CULT + name: cult-title + description: cult-description showInVote: true + minPlayers: 20 rules: - Cult - SubGamemodesRule @@ -190,6 +191,7 @@ name: changeling-title description: changeling-description showInVote: true + minPlayers: 15 rules: - Changeling - SubGamemodesRule