main cult
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.White.BecomeDustOnDeathSystem;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class BecomeDustOnDeathComponent : Component
|
||||
{
|
||||
[DataField("sprite", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string SpawnOnDeathPrototype = "Ectoplasm";
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Content.Shared.Mobs;
|
||||
|
||||
namespace Content.Server.White.BecomeDustOnDeathSystem;
|
||||
|
||||
public sealed class BecomeDustOnDeathSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<BecomeDustOnDeathComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
}
|
||||
|
||||
private void OnMobStateChanged(EntityUid uid, BecomeDustOnDeathComponent component, MobStateChangedEvent args)
|
||||
{
|
||||
var xform = Transform(uid);
|
||||
Spawn(component.SpawnOnDeathPrototype, xform.Coordinates);
|
||||
|
||||
QueueDel(uid);
|
||||
}
|
||||
}
|
||||
11
Content.Server/_White/Cult/ConstructComponent.cs
Normal file
11
Content.Server/_White/Cult/ConstructComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Server.White.Cult;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ConstructComponent : Component
|
||||
{
|
||||
[DataField("actions", customTypeSerializer: typeof(PrototypeIdListSerializer<InstantActionPrototype>))]
|
||||
public List<string> Actions = new();
|
||||
}
|
||||
74
Content.Server/_White/Cult/GameRule/CultRuleComponent.cs
Normal file
74
Content.Server/_White/Cult/GameRule/CultRuleComponent.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Content.Server.GameTicking.Presets;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
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");
|
||||
|
||||
[DataField("cultPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<GamePresetPrototype>))]
|
||||
public static string CultGamePresetPrototype = "Cult";
|
||||
|
||||
[DataField("cultistPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
||||
public static string CultistPrototypeId = "Cultist";
|
||||
|
||||
[DataField("reaperPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public static string ReaperPrototype = "ReaperConstruct";
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly), DataField("tileId")]
|
||||
public static string CultFloor = "CultFloor";
|
||||
|
||||
[DataField("eyeColor")]
|
||||
public static Color EyeColor = Color.FromHex("#f80000");
|
||||
|
||||
public static string HolyWaterReagent = "HolyWater";
|
||||
|
||||
[DataField("redEyeThreshold")]
|
||||
public static int ReadEyeThreshold = 5;
|
||||
|
||||
[DataField("pentagramThreshold")]
|
||||
public static int PentagramThreshold = 8;
|
||||
|
||||
public Dictionary<IPlayerSession, HumanoidCharacterProfile> StarCandidates = new();
|
||||
|
||||
[DataField("cultistStartingItems", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
|
||||
public List<string> StartingItems = new();
|
||||
|
||||
[DataField("cultistRolePrototype", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
||||
public string CultistRolePrototype = "Cultist";
|
||||
|
||||
/// <summary>
|
||||
/// Players who played as an cultist at some point in the round.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> CultistsList = new();
|
||||
|
||||
public EntityUid? CultTarget;
|
||||
|
||||
public List<CultistComponent> Cultists = new();
|
||||
|
||||
public List<ConstructComponent> Constructs = new();
|
||||
|
||||
public CultWinCondition WinCondition;
|
||||
}
|
||||
|
||||
public enum CultWinCondition : byte
|
||||
{
|
||||
CultWin,
|
||||
CultFailure
|
||||
}
|
||||
|
||||
public sealed class CultNarsieSummoned : EntityEventArgs
|
||||
{
|
||||
}
|
||||
492
Content.Server/_White/Cult/GameRule/CultRuleSystem.cs
Normal file
492
Content.Server/_White/Cult/GameRule/CultRuleSystem.cs
Normal file
@@ -0,0 +1,492 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.GameTicking.Rules;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.NPC.Systems;
|
||||
using Content.Server.Roles;
|
||||
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;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Objectives;
|
||||
using Content.Shared.Players;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared._White;
|
||||
using Content.Shared.Mind;
|
||||
|
||||
namespace Content.Server.White.Cult.GameRule;
|
||||
|
||||
public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
|
||||
{
|
||||
[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 IPrototypeManager _prototypeManager = 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!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private int _minimalCultists;
|
||||
private int _cultGameRuleMinimapPlayers;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_sawmill = Logger.GetSawmill("preset");
|
||||
_minimalCultists = _cfg.GetCVar(WhiteCVars.CultMinStartingPlayers);
|
||||
_cultGameRuleMinimapPlayers = _cfg.GetCVar(WhiteCVars.CultMinPlayers);
|
||||
|
||||
SubscribeLocalEvent<RoundStartAttemptEvent>(OnStartAttempt);
|
||||
SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnPlayersSpawned);
|
||||
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndText);
|
||||
SubscribeLocalEvent<CultNarsieSummoned>(OnNarsieSummon);
|
||||
|
||||
SubscribeLocalEvent<CultistComponent, ComponentInit>(OnCultistComponentInit);
|
||||
SubscribeLocalEvent<CultistComponent, ComponentRemove>(OnCultistComponentRemoved);
|
||||
SubscribeLocalEvent<CultistComponent, MobStateChangedEvent>(OnCultistsStateChanged);
|
||||
}
|
||||
|
||||
private void OnCultistsStateChanged(EntityUid uid, CultistComponent component, MobStateChangedEvent ev)
|
||||
{
|
||||
if (ev.NewMobState == MobState.Dead)
|
||||
{
|
||||
CheckRoundShouldEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public MindComponent? GetTarget()
|
||||
{
|
||||
var querry = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
|
||||
|
||||
while (querry.MoveNext(out _, out var cultRuleComponent, out _))
|
||||
{
|
||||
if (cultRuleComponent.CultTarget.HasValue && TryComp<MindComponent>(cultRuleComponent.CultTarget.Value, out var mind))
|
||||
{
|
||||
return mind;
|
||||
}
|
||||
}
|
||||
|
||||
return null!;
|
||||
}
|
||||
|
||||
public bool CanSummonNarsie()
|
||||
{
|
||||
var querry = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
|
||||
|
||||
while (querry.MoveNext(out _, out var cultRuleComponent, out _))
|
||||
{
|
||||
var cultistsAmount = cultRuleComponent.Cultists.Count;
|
||||
var constructsAmount = cultRuleComponent.Constructs.Count;
|
||||
var enoughCultists = cultistsAmount + constructsAmount > 10;
|
||||
|
||||
if (!enoughCultists)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var target = GetTarget();
|
||||
var targetKilled = target == null || _mindSystem.IsCharacterDeadIc(target);
|
||||
|
||||
if (targetKilled)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CheckRoundShouldEnd()
|
||||
{
|
||||
var querry = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
|
||||
var aliveCultistsCount = 0;
|
||||
|
||||
while (querry.MoveNext(out _, out var cultRuleComponent, out _))
|
||||
{
|
||||
foreach (var cultistComponent in cultRuleComponent.Cultists)
|
||||
{
|
||||
var owner = cultistComponent.Owner;
|
||||
if (!TryComp<MobStateComponent>(owner, out var mobState))
|
||||
continue;
|
||||
|
||||
if (_mobStateSystem.IsAlive(owner, mobState))
|
||||
{
|
||||
aliveCultistsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aliveCultistsCount == 0)
|
||||
{
|
||||
_roundEndSystem.EndRound();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCultistComponentInit(EntityUid uid, CultistComponent component, ComponentInit args)
|
||||
{
|
||||
var query = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
|
||||
|
||||
while (query.MoveNext(out var ruleEnt, out var cultRuleComponent, out _))
|
||||
{
|
||||
if (!GameTicker.IsGameRuleAdded(ruleEnt))
|
||||
continue;
|
||||
|
||||
if (!TryComp<MindContainerComponent>(uid, out var mindComponent))
|
||||
return;
|
||||
|
||||
if (!mindComponent.HasMind)
|
||||
return;
|
||||
|
||||
cultRuleComponent.Cultists.Add(component);
|
||||
|
||||
if (TryComp<ActorComponent>(component.Owner, out var actor))
|
||||
{
|
||||
cultRuleComponent.CultistsList.Add(MetaData(component.Owner).EntityName, actor.PlayerSession.Name);
|
||||
}
|
||||
|
||||
var traitorRole = new TraitorRoleComponent()
|
||||
{
|
||||
PrototypeId = cultRuleComponent.CultistRolePrototype
|
||||
};
|
||||
|
||||
_roleSystem.MindAddRole(mindComponent.Mind.Value, traitorRole);
|
||||
|
||||
UpdateCultistsAppearance(cultRuleComponent);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCultistComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args)
|
||||
{
|
||||
var query = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
|
||||
|
||||
while (query.MoveNext(out var ruleEnt, out var cultRuleComponent, out _))
|
||||
{
|
||||
if (!GameTicker.IsGameRuleAdded(ruleEnt))
|
||||
continue;
|
||||
|
||||
cultRuleComponent.Cultists.Remove(component);
|
||||
|
||||
RemoveCultistAppearance(component);
|
||||
|
||||
CheckRoundShouldEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveCultistAppearance(CultistComponent component)
|
||||
{
|
||||
if (TryComp<HumanoidAppearanceComponent>(component.Owner, out var appearanceComponent))
|
||||
{
|
||||
//Потому что я так сказал
|
||||
appearanceComponent.EyeColor = Color.White;
|
||||
Dirty(appearanceComponent);
|
||||
}
|
||||
|
||||
RemComp<PentagramComponent>(component.Owner);
|
||||
}
|
||||
|
||||
private void UpdateCultistsAppearance(CultRuleComponent cultRuleComponent)
|
||||
{
|
||||
var cultistsCount = cultRuleComponent.Cultists.Count;
|
||||
var constructsCount = cultRuleComponent.Constructs.Count;
|
||||
var totalCultMembers = cultistsCount + constructsCount;
|
||||
if (totalCultMembers < CultRuleComponent.ReadEyeThreshold)
|
||||
return;
|
||||
|
||||
foreach (var cultistComponent in cultRuleComponent.Cultists)
|
||||
{
|
||||
if (TryComp<HumanoidAppearanceComponent>(cultistComponent.Owner, out var appearanceComponent))
|
||||
{
|
||||
appearanceComponent.EyeColor = CultRuleComponent.EyeColor;
|
||||
Dirty(appearanceComponent);
|
||||
}
|
||||
|
||||
if (totalCultMembers < CultRuleComponent.PentagramThreshold)
|
||||
return;
|
||||
|
||||
EnsureComp<PentagramComponent>(cultistComponent.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRoundEndText(RoundEndTextAppendEvent ev)
|
||||
{
|
||||
var querry = EntityQuery<CultRuleComponent>();
|
||||
|
||||
foreach (var cultRuleComponent in querry)
|
||||
{
|
||||
var winText = Loc.GetString($"cult-cond-{cultRuleComponent.WinCondition.ToString().ToLower()}");
|
||||
ev.AddLine(winText);
|
||||
|
||||
ev.AddLine(Loc.GetString("cultists-list-start"));
|
||||
|
||||
foreach (var (entityName, ckey) in cultRuleComponent.CultistsList)
|
||||
{
|
||||
var lising = Loc.GetString("cultists-list-name", ("name", entityName), ("user", ckey));
|
||||
ev.AddLine(lising);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStartAttempt(RoundStartAttemptEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _, out var gameRule))
|
||||
{
|
||||
if (!GameTicker.IsGameRuleAdded(uid, gameRule))
|
||||
continue;
|
||||
|
||||
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();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev.Players.Length == 0)
|
||||
{
|
||||
_chatManager.DispatchServerAnnouncement(Loc.GetString("traitor-no-one-ready"));
|
||||
ev.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
|
||||
while (query.MoveNext(out var uid, out var cultRule, out var gameRule))
|
||||
{
|
||||
if (!GameTicker.IsGameRuleAdded(uid, gameRule))
|
||||
continue;
|
||||
|
||||
foreach (var player in ev.Players)
|
||||
{
|
||||
if (!ev.Profiles.ContainsKey(player.UserId))
|
||||
continue;
|
||||
|
||||
cultRule.StarCandidates[player] = ev.Profiles[player.UserId];
|
||||
}
|
||||
|
||||
var potentialCultists = FindPotentialCultist(cultRule.StarCandidates);
|
||||
var pickedCultist = PickCultists(potentialCultists);
|
||||
var potentialTargets = FindPotentialTargets(pickedCultist);
|
||||
|
||||
cultRule.CultTarget = _random.PickAndTake(potentialTargets).Mind;
|
||||
|
||||
foreach (var pickerCultist in pickedCultist)
|
||||
{
|
||||
MakeCultist(pickerCultist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<MindContainerComponent> FindPotentialTargets(List<IPlayerSession> exclude = null!)
|
||||
{
|
||||
var querry = EntityManager.EntityQuery<MindContainerComponent, HumanoidAppearanceComponent, ActorComponent>();
|
||||
|
||||
var potentialTargets = new List<MindContainerComponent>();
|
||||
|
||||
foreach (var (mind, _, actor) in querry)
|
||||
{
|
||||
var entity = mind.Mind;
|
||||
|
||||
if (entity == default)
|
||||
continue;
|
||||
|
||||
if (exclude?.Contains(actor.PlayerSession) is true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
potentialTargets.Add(mind);
|
||||
}
|
||||
|
||||
return potentialTargets;
|
||||
}
|
||||
|
||||
private List<IPlayerSession> FindPotentialCultist(in Dictionary<IPlayerSession, HumanoidCharacterProfile> candidates)
|
||||
{
|
||||
var list = new List<IPlayerSession>();
|
||||
var pendingQuery = GetEntityQuery<PendingClockInComponent>();
|
||||
|
||||
foreach (var player in candidates.Keys)
|
||||
{
|
||||
// Role prevents antag.
|
||||
if (!_jobSystem.CanBeAntag(player)) continue;
|
||||
|
||||
// Latejoin
|
||||
if (player.AttachedEntity != null && pendingQuery.HasComponent(player.AttachedEntity.Value))
|
||||
continue;
|
||||
|
||||
list.Add(player);
|
||||
}
|
||||
|
||||
var prefList = new List<IPlayerSession>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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<IPlayerSession> PickCultists(List<IPlayerSession> prefList)
|
||||
{
|
||||
var result = new List<IPlayerSession>();
|
||||
if (prefList.Count == 0)
|
||||
{
|
||||
_sawmill.Info("Insufficient ready players to fill up with cultists, stopping the selection.");
|
||||
return result;
|
||||
}
|
||||
|
||||
var minCultists = _cfg.GetCVar(WhiteCVars.CultMinPlayers);
|
||||
var maxCultists = _cfg.GetCVar(WhiteCVars.CultMaxStartingPlayers);
|
||||
|
||||
var actualCultistCount = prefList.Count > maxCultists ? maxCultists : minCultists;
|
||||
|
||||
for (var i = 0; i < actualCultistCount; i++)
|
||||
{
|
||||
result.Add(_random.PickAndTake(prefList));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool MakeCultist(IPlayerSession cultist)
|
||||
{
|
||||
var cultistRule = EntityQuery<CultRuleComponent>().FirstOrDefault();
|
||||
|
||||
if (cultistRule == null)
|
||||
{
|
||||
GameTicker.StartGameRule(CultRuleComponent.CultGamePresetPrototype, out var ruleEntity);
|
||||
cultistRule = Comp<CultRuleComponent>(ruleEntity);
|
||||
}
|
||||
|
||||
var mind = cultist.Data.ContentData()?.Mind;
|
||||
|
||||
if (mind == null)
|
||||
{
|
||||
_sawmill.Info("Failed getting mind for picked cultist.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var playerEntity = cultist.AttachedEntity;
|
||||
|
||||
if (!playerEntity.HasValue)
|
||||
{
|
||||
_sawmill.Error("Mind picked for cultist did not have an attached entity.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var mindComponent = Comp<MindComponent>(mind.Value);
|
||||
|
||||
DebugTools.AssertNotNull(playerEntity.Value);
|
||||
EnsureComp<CultistComponent>(playerEntity.Value);
|
||||
|
||||
_factionSystem.RemoveFaction(playerEntity.Value, "NanoTrasen", false);
|
||||
_factionSystem.AddFaction(playerEntity.Value, "Cultist");
|
||||
|
||||
if (_inventorySystem.TryGetSlotEntity(playerEntity.Value, "back", out var backPack))
|
||||
{
|
||||
foreach (var itemPrototype in cultistRule.StartingItems)
|
||||
{
|
||||
var itemEntity = Spawn(itemPrototype, Transform(playerEntity.Value).Coordinates);
|
||||
|
||||
if (backPack != null)
|
||||
{
|
||||
_storageSystem.Insert(backPack.Value, itemEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_audioSystem.PlayGlobal(cultistRule.GreatingsSound, Filter.Empty().AddPlayer(cultist), false,
|
||||
AudioParams.Default);
|
||||
|
||||
_chatManager.DispatchServerMessage(cultist, Loc.GetString("cult-role-greeting"));
|
||||
|
||||
if (_prototypeManager.TryIndex<ObjectivePrototype>("CultistKillObjective", out var cultistObjective))
|
||||
{
|
||||
_mindSystem.TryAddObjective(mind.Value, mindComponent,cultistObjective);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnNarsieSummon(CultNarsieSummoned ev)
|
||||
{
|
||||
var query = EntityQuery<MobStateComponent, MindContainerComponent, CultistComponent>().ToList();
|
||||
|
||||
foreach (var (mobState, mindContainer, _) in query)
|
||||
{
|
||||
if (!mindContainer.HasMind || mindContainer.Mind is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var reaper = Spawn(CultRuleComponent.ReaperPrototype, Transform(mobState.Owner).Coordinates);
|
||||
_mindSystem.TransferTo(mindContainer.Mind.Value, reaper);
|
||||
|
||||
_bodySystem.GibBody(mobState.Owner);
|
||||
}
|
||||
|
||||
_roundEndSystem.EndRound();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Content.Server.White.Cult.HolyWater;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class BibleWaterConvertComponent : Component
|
||||
{
|
||||
[DataField("convertedId"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string ConvertedId = "Water";
|
||||
|
||||
[DataField("ConvertedToId"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string ConvertedToId = "HolyWater";
|
||||
}
|
||||
54
Content.Server/_White/Cult/HolyWater/DeconvertCultist.cs
Normal file
54
Content.Server/_White/Cult/HolyWater/DeconvertCultist.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Threading;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.White.Cult;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Content.Server.White.Cult.HolyWater;
|
||||
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
[MeansImplicitUse]
|
||||
public sealed partial class DeconvertCultist : ReagentEffect
|
||||
{
|
||||
public override bool ShouldLog => true;
|
||||
|
||||
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
return Loc.GetString("reagent-effect-guidebook-deconvert-cultist");
|
||||
}
|
||||
|
||||
public override void Effect(ReagentEffectArgs args)
|
||||
{
|
||||
var uid = args.SolutionEntity;
|
||||
|
||||
if (!args.EntityManager.TryGetComponent(uid, out CultistComponent? component))
|
||||
return;
|
||||
|
||||
if (component.HolyConvertToken != null)
|
||||
return;
|
||||
|
||||
args.EntityManager.System<StunSystem>()
|
||||
.TryParalyze(uid, TimeSpan.FromSeconds(component.HolyConvertTime + 5f), true);
|
||||
var target = Identity.Name(uid, args.EntityManager);
|
||||
args.EntityManager.System<PopupSystem>()
|
||||
.PopupEntity(Loc.GetString("holy-water-started-converting", ("target", target)), uid);
|
||||
|
||||
component.HolyConvertToken = new CancellationTokenSource();
|
||||
Timer.Spawn(TimeSpan.FromSeconds(component.HolyConvertTime), () => ConvertCultist(uid, args.EntityManager),
|
||||
component.HolyConvertToken.Token);
|
||||
}
|
||||
|
||||
private void ConvertCultist(EntityUid uid, IEntityManager entityManager)
|
||||
{
|
||||
if (!entityManager.TryGetComponent<CultistComponent>(uid, out var cultist))
|
||||
return;
|
||||
|
||||
cultist.HolyConvertToken = null;
|
||||
entityManager.RemoveComponent<CultistComponent>(uid);
|
||||
entityManager.RemoveComponent<PentagramComponent>(uid);
|
||||
}
|
||||
}
|
||||
54
Content.Server/_White/Cult/HolyWater/HolyWaterSystem.cs
Normal file
54
Content.Server/_White/Cult/HolyWater/HolyWaterSystem.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Chemistry.Components.SolutionManager;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.White.Cult.HolyWater;
|
||||
|
||||
public sealed class HolyWaterSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly StunSystem _stun = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BibleWaterConvertComponent, AfterInteractEvent>(OnBibleInteract);
|
||||
}
|
||||
|
||||
private void OnBibleInteract(EntityUid uid, BibleWaterConvertComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (HasComp<MobStateComponent>(uid))
|
||||
return;
|
||||
|
||||
if (!TryComp<SolutionContainerManagerComponent>(args.Target, out var container))
|
||||
return;
|
||||
|
||||
foreach (var solution in container.Solutions.Values.Where(solution => solution.ContainsReagent(component.ConvertedId, null)))
|
||||
{
|
||||
foreach (var reagent in solution.Contents)
|
||||
{
|
||||
if (reagent.Reagent.Prototype != component.ConvertedId)
|
||||
continue;
|
||||
|
||||
var amount = reagent.Quantity;
|
||||
|
||||
solution.RemoveReagent(reagent.Reagent.Prototype, reagent.Quantity);
|
||||
solution.AddReagent(component.ConvertedToId, amount);
|
||||
|
||||
if (args.Target == null)
|
||||
return;
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("holy-water-converted"), args.Target.Value, args.User);
|
||||
_audio.PlayPvs("/Audio/Effects/holy.ogg", args.Target.Value);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Content.Server.White.Cult.Items.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRobeModifierComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("speedModifier")]
|
||||
public float SpeedModifier = 1.45f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly), DataField("damageModifierSetId")]
|
||||
public string DamageModifierSetId = "CultRobe";
|
||||
|
||||
public string? StoredDamageSetId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Server.White.Cult.Items.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ReturnItemOnThrowComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("stunTime")]
|
||||
public float StunTime = 1f;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Content.Server.White.Cult.Items.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ShuttleCurseComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("delayTime")]
|
||||
public TimeSpan DelayTime = TimeSpan.FromSeconds(120);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("cooldown")]
|
||||
public TimeSpan Cooldown = TimeSpan.FromSeconds(180);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.White.Cult.Items;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.White.Cult.Items.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class TorchCultistsProviderComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public BoundUserInterface? UserInterface => Owner.GetUIOrNull(CultTeleporterUiKey.Key);
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public EntityUid? ItemSelected;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("cooldown")]
|
||||
public TimeSpan Cooldown = TimeSpan.FromSeconds(30);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("usesLeft")]
|
||||
public int UsesLeft = 3;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public TimeSpan NextUse = TimeSpan.Zero;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool Active = true;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using Content.Server.White.Cult.Items.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.White.Cult;
|
||||
|
||||
namespace Content.Server.White.Cult.Items.Systems;
|
||||
|
||||
public sealed class CultRobeModifierSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CultRobeModifierComponent, GotEquippedEvent>(OnEquip);
|
||||
SubscribeLocalEvent<CultRobeModifierComponent, GotUnequippedEvent>(OnUnequip);
|
||||
}
|
||||
|
||||
private void OnEquip(EntityUid uid, CultRobeModifierComponent component, GotEquippedEvent args)
|
||||
{
|
||||
if (!HasComp<CultistComponent>(args.Equipee))
|
||||
return;
|
||||
|
||||
if (args.Slot != "outerClothing")
|
||||
return;
|
||||
|
||||
ModifySpeed(args.Equipee, component, true);
|
||||
ModifyDamage(args.Equipee, component, true);
|
||||
}
|
||||
|
||||
private void OnUnequip(EntityUid uid, CultRobeModifierComponent component, GotUnequippedEvent args)
|
||||
{
|
||||
if (!HasComp<CultistComponent>(args.Equipee))
|
||||
return;
|
||||
|
||||
if (args.Slot != "outerClothing")
|
||||
return;
|
||||
|
||||
ModifySpeed(args.Equipee, component, false);
|
||||
ModifyDamage(args.Equipee, component, false);
|
||||
}
|
||||
|
||||
private void ModifySpeed(EntityUid uid, CultRobeModifierComponent comp, bool increase)
|
||||
{
|
||||
if (!TryComp<MovementSpeedModifierComponent>(uid, out var move))
|
||||
return;
|
||||
|
||||
var walkSpeed = increase ? move.BaseWalkSpeed * comp.SpeedModifier : move.BaseWalkSpeed / comp.SpeedModifier;
|
||||
|
||||
var sprintSpeed =
|
||||
increase ? move.BaseSprintSpeed * comp.SpeedModifier : move.BaseSprintSpeed / comp.SpeedModifier;
|
||||
|
||||
_movement.ChangeBaseSpeed(uid, walkSpeed, sprintSpeed, move.Acceleration, move);
|
||||
}
|
||||
|
||||
private void ModifyDamage(EntityUid uid, CultRobeModifierComponent comp, bool increase)
|
||||
{
|
||||
var damageSet = string.Empty;
|
||||
if (increase)
|
||||
{
|
||||
if (!TryComp<DamageableComponent>(uid, out var damage))
|
||||
return;
|
||||
|
||||
comp.StoredDamageSetId = damage.DamageModifierSetId;
|
||||
damageSet = comp.DamageModifierSetId;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (comp.StoredDamageSetId != null)
|
||||
damageSet = comp.StoredDamageSetId;
|
||||
|
||||
comp.StoredDamageSetId = null;
|
||||
}
|
||||
|
||||
_damageable.SetDamageModifierSetId(uid, damageSet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Server.White.Cult.Items.Components;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.White.Cult;
|
||||
|
||||
namespace Content.Server.White.Cult.Items.Systems;
|
||||
|
||||
public sealed class ReturnItemOnThrowSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly StunSystem _stun = default!;
|
||||
[Dependency] private readonly HandsSystem _hands = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ReturnItemOnThrowComponent, ThrowDoHitEvent>(OnThrowHit);
|
||||
}
|
||||
|
||||
private void OnThrowHit(EntityUid uid, ReturnItemOnThrowComponent component, ThrowDoHitEvent args)
|
||||
{
|
||||
var isCultist = HasComp<CultistComponent>(args.Target);
|
||||
var thrower = args.Component.Thrower;
|
||||
if (!HasComp<CultistComponent>(thrower))
|
||||
return;
|
||||
|
||||
if (!HasComp<MobStateComponent>(args.Target))
|
||||
return;
|
||||
|
||||
if (!_stun.IsParalyzed(args.Target))
|
||||
{
|
||||
if (!isCultist)
|
||||
{
|
||||
_stun.TryParalyze(args.Target, TimeSpan.FromSeconds(component.StunTime), true);
|
||||
}
|
||||
}
|
||||
|
||||
_hands.PickupOrDrop(thrower, uid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.White.Cult.Items.Components;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.White.Cult.Items.Systems;
|
||||
|
||||
public sealed class ShuttleCurseSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
private const int MaxCurses = 3;
|
||||
private int _currentCurses = 0;
|
||||
private TimeSpan? _nextCurse = TimeSpan.Zero;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ShuttleCurseComponent, UseInHandEvent>(OnUse);
|
||||
SubscribeLocalEvent<RoundEndedEvent>(OnRoundEnd);
|
||||
}
|
||||
|
||||
private void OnRoundEnd(RoundEndedEvent ev)
|
||||
{
|
||||
_currentCurses = 0;
|
||||
_nextCurse = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
private void OnUse(EntityUid uid, ShuttleCurseComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (!HasComp<CultistComponent>(args.User))
|
||||
{
|
||||
_hands.TryDrop(args.User);
|
||||
_popup.PopupEntity(Loc.GetString("shuttle-curse-not-cultist"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_roundEnd.ShuttleCalled())
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("shuttle-curse-shuttle-not-called"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentCurses >= MaxCurses)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("shuttle-curse-max-curses"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_nextCurse > _gameTiming.CurTime)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("shuttle-curse-cooldown"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
var shuttle = _entMan.System<EmergencyShuttleSystem>();
|
||||
|
||||
if (shuttle.EmergencyShuttleArrived)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("shuttle-curse-shuttle-arrived"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
_roundEnd.DelayCursedShuttle(component.DelayTime);
|
||||
_popup.PopupEntity(Loc.GetString("shuttle-curse-shuttle-delayed"), args.User, args.User);
|
||||
|
||||
_currentCurses++;
|
||||
_nextCurse = _gameTiming.CurTime + component.Cooldown;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
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.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 Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.White.Cult.Items.Systems;
|
||||
|
||||
public sealed class TorchCultistsProviderSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _pointLight = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly PullingSystem _pulling = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TorchCultistsProviderComponent, AfterInteractEvent>(OnInteract);
|
||||
SubscribeLocalEvent<TorchCultistsProviderComponent, TorchWindowItemSelectedMessage>(OnCultistSelected);
|
||||
|
||||
SubscribeLocalEvent<TorchCultistsProviderComponent, ComponentInit>(OnInit);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, TorchCultistsProviderComponent component, ComponentInit args)
|
||||
{
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, TorchCultistsProviderComponent comp, AfterInteractEvent args)
|
||||
{
|
||||
if (!args.Target.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target.Value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<TorchCultistsProviderComponent>(uid, out var provider))
|
||||
return;
|
||||
|
||||
if (!HasComp<CultistComponent>(args.User))
|
||||
{
|
||||
_hands.TryDrop(args.User);
|
||||
_popup.PopupEntity(Loc.GetString("cult-torch-not-cultist"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!provider.Active || provider.UsesLeft <= 0)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("cult-torch-drained"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (provider.NextUse > _timing.CurTime)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("cult-torch-cooldown"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasComp<MindContainerComponent>(args.Target))
|
||||
{
|
||||
TeleportToRandomLocation(uid, args, comp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasComp<ItemComponent>(args.Target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (provider.UserInterface == null)
|
||||
return;
|
||||
|
||||
provider.ItemSelected = args.Target;
|
||||
|
||||
var cultists = EntityQuery<CultistComponent>();
|
||||
var list = new Dictionary<string, string>();
|
||||
|
||||
foreach (var cultist in cultists)
|
||||
{
|
||||
if (!TryComp<MetaDataComponent>(cultist.Owner, out var meta))
|
||||
return;
|
||||
|
||||
if (cultist.Owner == args.User)
|
||||
continue;
|
||||
|
||||
list.Add(meta.Owner.ToString(), meta.EntityName);
|
||||
}
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("cult-torch-cultists-not-found"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
UserInterfaceSystem.SetUiState(provider.UserInterface, new TorchWindowBUIState(list));
|
||||
|
||||
if (!TryComp<ActorComponent>(args.User, out var actorComponent))
|
||||
return;
|
||||
|
||||
_ui.ToggleUi(provider.UserInterface, actorComponent.PlayerSession);
|
||||
}
|
||||
|
||||
private void OnCultistSelected(
|
||||
EntityUid uid,
|
||||
TorchCultistsProviderComponent component,
|
||||
TorchWindowItemSelectedMessage args)
|
||||
{
|
||||
var entityUid = args.Session.AttachedEntity;
|
||||
var cultists = EntityQuery<CultistComponent>();
|
||||
|
||||
foreach (var cultist in cultists)
|
||||
{
|
||||
if (cultist.Owner.ToString() == args.EntUid)
|
||||
entityUid = cultist.Owner;
|
||||
}
|
||||
|
||||
if (entityUid == args.Session.AttachedEntity && entityUid != null)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("cult-torch-no-cultist"), entityUid.Value, entityUid.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.ItemSelected != null)
|
||||
{
|
||||
var item = component.ItemSelected.Value;
|
||||
|
||||
if (!TryComp<TransformComponent>(entityUid, out var xForm))
|
||||
return;
|
||||
|
||||
_xform.SetCoordinates(item, xForm.Coordinates);
|
||||
_hands.PickupOrDrop(entityUid, item);
|
||||
}
|
||||
|
||||
UpdateUsesCount(uid, args.Session.AttachedEntity, component);
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, TorchCultistsProviderComponent component)
|
||||
{
|
||||
AppearanceComponent? appearance = null;
|
||||
if (!Resolve(uid, ref appearance, false))
|
||||
return;
|
||||
|
||||
_appearance.SetData(uid, VoidTorchVisuals.Activated, component.Active, appearance);
|
||||
}
|
||||
|
||||
private void TeleportToRandomLocation(EntityUid torch, InteractEvent args, TorchCultistsProviderComponent component)
|
||||
{
|
||||
if (!args.Target.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pulling.GetPulled(args.User) != args.Target.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ownerTransform = Transform(args.User);
|
||||
|
||||
if (_station.GetStationInMap(ownerTransform.MapID) is not { } station ||
|
||||
!TryComp<StationDataComponent>(station, out var data) ||
|
||||
_station.GetLargestGrid(data) is not { } grid)
|
||||
{
|
||||
if (ownerTransform.GridUid == null)
|
||||
return;
|
||||
|
||||
grid = ownerTransform.GridUid.Value;
|
||||
}
|
||||
|
||||
if (!TryComp<MapGridComponent>(grid, out var gridComp))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var gridTransform = Transform(grid);
|
||||
var gridBounds = gridComp.LocalAABB.Scale(0.7f); // чтобы не заспавнить на самом краю станции
|
||||
|
||||
var targetCoords = gridTransform.Coordinates;
|
||||
|
||||
for (var i = 0; i < 25; i++)
|
||||
{
|
||||
var randomX = _random.Next((int) gridBounds.Left, (int) gridBounds.Right);
|
||||
var randomY = _random.Next((int) gridBounds.Bottom, (int) gridBounds.Top);
|
||||
|
||||
var tile = new Vector2i(randomX, randomY);
|
||||
|
||||
// no air-blocked areas.
|
||||
if (_atmosphere.IsTileSpace(grid, gridTransform.MapUid, tile, mapGridComp: gridComp) ||
|
||||
_atmosphere.IsTileAirBlocked(grid, tile, mapGridComp: gridComp))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't spawn inside of solid objects
|
||||
var physQuery = GetEntityQuery<PhysicsComponent>();
|
||||
var valid = true;
|
||||
foreach (var ent in gridComp.GetAnchoredEntities(tile))
|
||||
{
|
||||
if (!physQuery.TryGetComponent(ent, out var body))
|
||||
continue;
|
||||
|
||||
if (body.BodyType != BodyType.Static ||
|
||||
!body.Hard ||
|
||||
(body.CollisionLayer & (int) CollisionGroup.LargeMobMask) == 0)
|
||||
continue;
|
||||
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
continue;
|
||||
|
||||
targetCoords = gridComp.GridTileToLocal(tile);
|
||||
break;
|
||||
}
|
||||
|
||||
_xform.SetCoordinates(args.User, targetCoords);
|
||||
_xform.SetCoordinates(args.Target.Value, targetCoords);
|
||||
|
||||
UpdateUsesCount(torch, args.User, component);
|
||||
}
|
||||
|
||||
private void UpdateUsesCount(EntityUid torch, EntityUid? user, TorchCultistsProviderComponent component)
|
||||
{
|
||||
component.ItemSelected = null;
|
||||
component.NextUse = _timing.CurTime + component.Cooldown;
|
||||
component.UsesLeft--;
|
||||
|
||||
if (user.HasValue)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("cult-torch-item-send"), user.Value);
|
||||
}
|
||||
|
||||
if (component.UsesLeft <= 0)
|
||||
{
|
||||
component.Active = false;
|
||||
UpdateAppearance(torch, component);
|
||||
|
||||
if (!TryComp<PointLightComponent>(torch, out var light))
|
||||
return;
|
||||
|
||||
_pointLight.SetEnabled(torch, false, light);
|
||||
}
|
||||
}
|
||||
}
|
||||
160
Content.Server/_White/Cult/Items/Systems/VoidTeleportSystem.cs
Normal file
160
Content.Server/_White/Cult/Items/Systems/VoidTeleportSystem.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System.Threading;
|
||||
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 Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Content.Server.White.Cult.Items.Systems;
|
||||
|
||||
public sealed class VoidTeleportSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<VoidTeleportComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<VoidTeleportComponent, ComponentInit>(OnInit);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, VoidTeleportComponent component, ComponentInit args)
|
||||
{
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, VoidTeleportComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (!HasComp<CultistComponent>(args.User))
|
||||
{
|
||||
_hands.TryDrop(args.User);
|
||||
_popup.PopupEntity(Loc.GetString("void-teleport-not-cultist"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!component.Active || component.UsesLeft <= 0)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("void-teleport-drained"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.NextUse > _timing.CurTime)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("void-teleport-cooldown"), args.User, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<TransformComponent>(args.User, out var transform))
|
||||
return;
|
||||
|
||||
var oldCoords = transform.Coordinates;
|
||||
|
||||
EntityCoordinates coords = default;
|
||||
var attempts = 10;
|
||||
//Repeat until proper place for tp is found
|
||||
while (attempts <= 10)
|
||||
{
|
||||
attempts--;
|
||||
//Get coords to where tp
|
||||
var random = new Random().Next(component.MinRange, component.MaxRange);
|
||||
var offset = transform.LocalRotation.ToWorldVec().Normalized();
|
||||
var direction = transform.LocalRotation.GetDir().ToVec();
|
||||
var newOffset = offset + direction * random;
|
||||
coords = transform.Coordinates.Offset(newOffset).SnapToGrid(EntityManager);
|
||||
|
||||
var tile = coords.GetTileRef();
|
||||
|
||||
//Check for walls
|
||||
if (tile != null && _turf.IsTileBlocked(tile.Value, CollisionGroup.AllMask))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
CreatePulse(uid, component);
|
||||
|
||||
_xform.SetCoordinates(args.User, coords);
|
||||
transform.AttachToGridOrMap();
|
||||
|
||||
var pulled = GetPulledEntity(args.User);
|
||||
if (pulled != null)
|
||||
{
|
||||
_xform.SetCoordinates(pulled.Value, coords);
|
||||
|
||||
if (TryComp<TransformComponent>(pulled.Value, out var pulledTransform))
|
||||
pulledTransform.AttachToGridOrMap();
|
||||
}
|
||||
|
||||
//Play tp sound
|
||||
_audio.PlayPvs(component.TeleportInSound, coords);
|
||||
_audio.PlayPvs(component.TeleportOutSound,oldCoords);
|
||||
|
||||
//Create tp effect
|
||||
_entMan.SpawnEntity(component.TeleportInEffect, coords);
|
||||
_entMan.SpawnEntity(component.TeleportOutEffect, oldCoords);
|
||||
|
||||
component.UsesLeft--;
|
||||
component.NextUse = _timing.CurTime + component.Cooldown;
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, VoidTeleportComponent comp)
|
||||
{
|
||||
AppearanceComponent? appearance = null;
|
||||
if (!Resolve(uid, ref appearance, false))
|
||||
return;
|
||||
|
||||
_appearance.SetData(uid, VeilVisuals.Activated, comp.Active, appearance);
|
||||
}
|
||||
|
||||
private EntityUid? GetPulledEntity(EntityUid user)
|
||||
{
|
||||
EntityUid? pulled = null;
|
||||
|
||||
if (TryComp<SharedPullerComponent>(user, out var puller))
|
||||
pulled = puller.Pulling;
|
||||
|
||||
return pulled;
|
||||
}
|
||||
|
||||
private void CreatePulse(EntityUid uid, VoidTeleportComponent component)
|
||||
{
|
||||
if (TryComp<PointLightComponent>(uid, out var light))
|
||||
light.Energy = 5f;
|
||||
|
||||
Timer.Spawn(component.TimerDelay, () => TurnOffPulse(uid, component), component.Token.Token);
|
||||
}
|
||||
|
||||
private void TurnOffPulse(EntityUid uid ,VoidTeleportComponent comp)
|
||||
{
|
||||
if (!TryComp<PointLightComponent>(uid, out var light))
|
||||
return;
|
||||
|
||||
light.Energy = 1f;
|
||||
|
||||
comp.Token = new CancellationTokenSource();
|
||||
|
||||
if (comp.UsesLeft <= 0)
|
||||
{
|
||||
comp.Active = false;
|
||||
UpdateAppearance(uid, comp);
|
||||
|
||||
light.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Content.Server/_White/Cult/PentagramComponent.cs
Normal file
9
Content.Server/_White/Cult/PentagramComponent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Content.Shared.White.Cult.Pentagram;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server.White.Cult;
|
||||
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class PentagramComponent : SharedPentagramComponent
|
||||
{
|
||||
}
|
||||
264
Content.Server/_White/Cult/Pylon/PylonSystem.cs
Normal file
264
Content.Server/_White/Cult/Pylon/PylonSystem.cs
Normal file
@@ -0,0 +1,264 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Maps;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.White.Cult;
|
||||
using Content.Shared.White.Cult.Pylon;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.White.Cult.Pylon;
|
||||
|
||||
public sealed class PylonSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DamageableSystem _damageSystem = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinition = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly TileSystem _tile = default!;
|
||||
[Dependency] private readonly BloodstreamSystem _blood = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SharedPylonComponent, InteractHandEvent>(OnInteract);
|
||||
SubscribeLocalEvent<SharedPylonComponent, ComponentInit>(OnInit);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, SharedPylonComponent component, ComponentInit args)
|
||||
{
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var pylonsQuery = EntityQuery<SharedPylonComponent>();
|
||||
|
||||
foreach (var comp in pylonsQuery)
|
||||
{
|
||||
if (comp.NextTileConvert == TimeSpan.Zero)
|
||||
comp.NextTileConvert = _timing.CurTime + TimeSpan.FromSeconds(comp.TileConvertCooldown);
|
||||
|
||||
if (comp.NextHealTime == TimeSpan.Zero)
|
||||
comp.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(comp.HealingAuraCooldown);
|
||||
|
||||
if (_timing.CurTime >= comp.NextHealTime)
|
||||
{
|
||||
comp.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(comp.HealingAuraCooldown);
|
||||
|
||||
if (comp.Activated)
|
||||
HealPlayersInRange(comp);
|
||||
}
|
||||
|
||||
if (_timing.CurTime >= comp.NextTileConvert)
|
||||
{
|
||||
comp.NextTileConvert = _timing.CurTime + TimeSpan.FromSeconds(comp.TileConvertCooldown);
|
||||
|
||||
if (comp.Activated)
|
||||
ConvertNearbyTiles(comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ConvertNearbyTiles(SharedPylonComponent comp)
|
||||
{
|
||||
var tilesConverted = 0;
|
||||
var random = new Random().Next(1, 3);
|
||||
|
||||
var uid = comp.Owner;
|
||||
var gridUid = Transform(uid).GridUid;
|
||||
var pylonPos = Transform(uid).Coordinates;
|
||||
|
||||
if (!_mapManager.TryGetGrid(gridUid, out var grid))
|
||||
return;
|
||||
|
||||
var radius = comp.TileConvertRange;
|
||||
var tilesRefs = grid.GetLocalTilesIntersecting(new Box2(pylonPos.Position + new Vector2(-radius, -radius),
|
||||
pylonPos.Position + new Vector2(radius, radius)));
|
||||
var tiles = ShuffleTiles(tilesRefs);
|
||||
|
||||
if (comp.ConvertEverything)
|
||||
ConvertEverything(comp, tiles);
|
||||
|
||||
var cultTileDef = (ContentTileDefinition) _tileDefinition[$"{comp.TileId}"];
|
||||
var cultTile = new Tile(cultTileDef.TileId);
|
||||
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
if (tilesConverted >= random)
|
||||
return;
|
||||
|
||||
var tilePos = _turf.GetTileCenter(tile);
|
||||
|
||||
if (pylonPos.InRange(EntityManager, tilePos, comp.TileConvertRange))
|
||||
{
|
||||
if (tile.Tile.TypeId == cultTile.TypeId)
|
||||
continue;
|
||||
|
||||
_tile.ReplaceTile(tile, cultTileDef);
|
||||
_entMan.SpawnEntity(comp.TileConvertEffect, tilePos);
|
||||
_audio.PlayPvs(comp.ConvertTileSound, tilePos, AudioParams.Default.WithVolume(-5));
|
||||
|
||||
tilesConverted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ConvertEverything(SharedPylonComponent comp, IEnumerable<TileRef> tiles)
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
if (!_turf.IsTileBlocked(tile, CollisionGroup.WallLayer)
|
||||
|| !_turf.IsTileBlocked(tile, CollisionGroup.AirlockLayer))
|
||||
continue;
|
||||
|
||||
var posss = _turf.GetTileCenter(tile);
|
||||
|
||||
foreach (var entity in _lookup.GetEntitiesIntersecting(posss))
|
||||
{
|
||||
if (TryComp<TagComponent>(entity, out var tag)
|
||||
&& tag.Tags.Contains("Wall")
|
||||
&& MetaData(entity).EntityPrototype?.ID != comp.WallId)
|
||||
{
|
||||
_entMan.SpawnEntity(comp.WallId, Transform(entity).Coordinates);
|
||||
_entMan.SpawnEntity(comp.WallConvertEffect, Transform(entity).Coordinates);
|
||||
_entMan.DeleteEntity(entity);
|
||||
_audio.PlayPvs(comp.ConvertTileSound, posss, AudioParams.Default.WithVolume(-10));
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasComp<AirlockComponent>(entity) && MetaData(entity).EntityPrototype?.ID != comp.AirlockId)
|
||||
{
|
||||
_entMan.SpawnEntity(comp.AirlockId, Transform(entity).Coordinates);
|
||||
_entMan.SpawnEntity(comp.AirlockConvertEffect, Transform(entity).Coordinates);
|
||||
_entMan.DeleteEntity(entity);
|
||||
_audio.PlayPvs(comp.ConvertTileSound, posss, AudioParams.Default.WithVolume(-10));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HealPlayersInRange(SharedPylonComponent comp)
|
||||
{
|
||||
foreach (var player in _playerManager.Sessions)
|
||||
{
|
||||
if (player.AttachedEntity is not { Valid: true } playerEntity)
|
||||
continue;
|
||||
|
||||
if (!EntityManager.TryGetComponent<CultistComponent>(playerEntity, out _))
|
||||
continue;
|
||||
|
||||
if (_mobStateSystem.IsDead(playerEntity))
|
||||
continue;
|
||||
|
||||
var playerDamageComp = EntityManager.TryGetComponent<DamageableComponent>(playerEntity, out var damageComp)
|
||||
? damageComp
|
||||
: null;
|
||||
|
||||
if (playerDamageComp == null || playerDamageComp.Damage.Total == 0)
|
||||
continue;
|
||||
|
||||
var uid = comp.Owner;
|
||||
var pylonXForm = Transform(uid);
|
||||
var playerXForm = Transform(playerEntity);
|
||||
|
||||
if (pylonXForm.Coordinates.InRange(EntityManager, playerXForm.Coordinates, comp.HealingAuraRange))
|
||||
{
|
||||
var damage = comp.HealingAuraDamage;
|
||||
_damageSystem.TryChangeDamage(playerEntity, damage, true);
|
||||
|
||||
if (!TryComp<BloodstreamComponent>(playerEntity, out var bloodstream))
|
||||
continue;
|
||||
|
||||
if (bloodstream.IsBleeding)
|
||||
{
|
||||
_blood.TryModifyBleedAmount(playerEntity, -comp.BleedReductionAmount, bloodstream);
|
||||
}
|
||||
|
||||
if (_blood.GetBloodLevelPercentage(playerEntity, bloodstream) < bloodstream.BloodMaxVolume)
|
||||
{
|
||||
_blood.TryModifyBloodLevel(playerEntity, comp.BloodRefreshAmount, bloodstream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, SharedPylonComponent comp, InteractHandEvent args)
|
||||
{
|
||||
var user = args.User;
|
||||
var pylon = args.Target;
|
||||
|
||||
if (HasComp<CultistComponent>(user))
|
||||
{
|
||||
comp.Activated = !comp.Activated;
|
||||
|
||||
UpdateAppearance(uid, comp);
|
||||
|
||||
if (!TryComp<PointLightComponent>(uid, out var light))
|
||||
return;
|
||||
|
||||
light.Enabled = comp.Activated;
|
||||
|
||||
var toggleMsg = Loc.GetString(comp.Activated ? "pylon-toggle-on" : "pylon-toggle-off");
|
||||
_popupSystem.PopupEntity(toggleMsg, uid);
|
||||
return;
|
||||
}
|
||||
|
||||
var damage = comp.BurnDamageOnInteract;
|
||||
var burnMsg = Loc.GetString("powered-light-component-burn-hand");
|
||||
|
||||
_audio.Play(comp.BurnHandSound, Filter.Pvs(pylon), pylon, true);
|
||||
_popupSystem.PopupEntity(burnMsg, pylon, user);
|
||||
_damageSystem.TryChangeDamage(user, damage, true);
|
||||
}
|
||||
|
||||
private IEnumerable<TileRef> ShuffleTiles(IEnumerable<TileRef> collection)
|
||||
{
|
||||
var random = new Random();
|
||||
var shuffledList = collection.ToList();
|
||||
|
||||
var n = shuffledList.Count;
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
var k = random.Next(n + 1);
|
||||
(shuffledList[k], shuffledList[n]) = (shuffledList[n], shuffledList[k]);
|
||||
}
|
||||
|
||||
return shuffledList;
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, SharedPylonComponent comp)
|
||||
{
|
||||
AppearanceComponent? appearance = null;
|
||||
if (!Resolve(uid, ref appearance, false))
|
||||
return;
|
||||
|
||||
_appearance.SetData(uid, PylonVisuals.Activated, comp.Activated, appearance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultBarrierComponent : Component
|
||||
{
|
||||
[DataField("activated")] public bool Activated;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneApocalypseComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("rangeTarget")]
|
||||
public float RangeTarget = 1.2f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("summonMinCount")]
|
||||
public uint SummonMinCount = 10;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneBarrierComponent : Component { }
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneBaseComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("invokersMinCount")]
|
||||
public uint InvokersMinCount = 1;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("gatherInvokers")]
|
||||
public bool GatherInvokers = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("cultistGatheringRange")]
|
||||
public float CultistGatheringRange = 0.7f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("invokePhrase")]
|
||||
public string InvokePhrase = "";
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneBloodBoilComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("summonMinCount")]
|
||||
public uint SummonMinCount = 3;
|
||||
|
||||
[DataField("projectilePrototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>)), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string ProjectilePrototype = default!;
|
||||
|
||||
[DataField("projectileSpeed"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float ProjectileSpeed = 20f;
|
||||
|
||||
[DataField("minProjectiles"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public int MinProjectiles = 3;
|
||||
|
||||
[DataField("maxProjectiles"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public int MaxProjectiles = 9;
|
||||
|
||||
[DataField("projectileRange"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float ProjectileRange = 50f;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneBuffComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("rangeTarget")]
|
||||
public float RangeTarget = 0.3f;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneOfferingComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("sacrificeDeadMinCount")]
|
||||
public uint SacrificeDeadMinCount = 1;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("convertMinCount")]
|
||||
public uint ConvertMinCount = 2;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("sacrificeMinCount")]
|
||||
public uint SacrificeMinCount = 3;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("rangeTarget")]
|
||||
public float RangeTarget = 0.3f;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneReviveComponent : Component
|
||||
{
|
||||
public static uint ChargesLeft = 3;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("rangeTarget")]
|
||||
public float RangeTarget = 0.3f;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneSummoningComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("summonMinCount")]
|
||||
public uint SummonMinCount = 2;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneSummoningProviderComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public EntityUid? BaseRune;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultRuneTeleportComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("rangeTarget")]
|
||||
public float RangeTarget = 0.3f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("label")]
|
||||
public string? Label;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultTeleportRuneProviderComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public EntityUid? BaseRune;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public List<EntityUid>? Targets;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Shared.White.Cult;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class RuneDrawerProviderComponent : Component
|
||||
{
|
||||
[ViewVariables]
|
||||
public BoundUserInterface? UserInterface => Owner.GetUIOrNull(ListViewSelectorUiKey.Key);
|
||||
|
||||
[DataField("runePrototypes")]
|
||||
public List<string> RunePrototypes = new();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Content.Server.White.Cult.Runes.Comps;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class SoulShardComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
244
Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs
Normal file
244
Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Server.Emp;
|
||||
using Content.Server.EUI;
|
||||
using Content.Server.White.Cult.UI;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Fluids.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.White.Cult;
|
||||
using Content.Shared.White.Cult.Actions;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server.White.Cult.Runes.Systems;
|
||||
|
||||
public partial class CultSystem
|
||||
{
|
||||
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
|
||||
[Dependency] private readonly EmpSystem _empSystem = default!;
|
||||
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
|
||||
public void InitializeActions()
|
||||
{
|
||||
SubscribeLocalEvent<CultistComponent, CultTwistedConstructionActionEvent>(OnTwistedConstructionAction);
|
||||
SubscribeLocalEvent<CultistComponent, CultSummonDaggerActionEvent>(OnSummonDaggerAction);
|
||||
SubscribeLocalEvent<CultistComponent, CultShadowShacklesTargetActionEvent>(OnShadowShackles);
|
||||
SubscribeLocalEvent<CultistComponent, CultElectromagneticPulseTargetActionEvent>(OnElectromagneticPulse);
|
||||
SubscribeLocalEvent<CultistComponent, CultSummonCombatEquipmentTargetActionEvent>(OnSummonCombatEquipment);
|
||||
SubscribeLocalEvent<CultistComponent, CultConcealPresenceWorldActionEvent>(OnConcealPresence);
|
||||
SubscribeLocalEvent<CultistComponent, CultBloodRitesInstantActionEvent>(OnBloodRites);
|
||||
SubscribeLocalEvent<CultistComponent, CultTeleportTargetActionEvent>(OnTeleport);
|
||||
SubscribeLocalEvent<CultistComponent, CultStunTargetActionEvent>(OnStunTarget);
|
||||
}
|
||||
|
||||
private void OnStunTarget(EntityUid uid, CultistComponent component, CultStunTargetActionEvent args)
|
||||
{
|
||||
if (args.Target == uid || !HasComp<StatusEffectsComponent>(args.Target))
|
||||
return;
|
||||
|
||||
if (_stunSystem.TryStun(args.Target, TimeSpan.FromSeconds(6), true))
|
||||
{
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args)
|
||||
{
|
||||
if (!TryComp<BloodstreamComponent>(args.Performer, out _) || !TryComp<ActorComponent>(uid, out var actor))
|
||||
return;
|
||||
|
||||
var eui = new TeleportSpellEui(args.Performer, args.Target);
|
||||
_euiManager.OpenEui(eui, actor.PlayerSession);
|
||||
eui.StateDirty();
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnBloodRites(EntityUid uid, CultistComponent component, CultBloodRitesInstantActionEvent args)
|
||||
{
|
||||
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstreamComponent))
|
||||
return;
|
||||
|
||||
var bruteDamageGroup = _prototypeManager.Index<DamageGroupPrototype>("Brute");
|
||||
var burnDamageGroup = _prototypeManager.Index<DamageGroupPrototype>("Burn");
|
||||
|
||||
var xform = Transform(uid);
|
||||
|
||||
var entitiesInRange = _lookup.GetEntitiesInRange(xform.MapPosition, 1.5f);
|
||||
|
||||
FixedPoint2 totalBloodAmount = 0f;
|
||||
|
||||
var breakLoop = false;
|
||||
foreach (var solutionEntity in entitiesInRange.ToList())
|
||||
{
|
||||
if (breakLoop)
|
||||
break;
|
||||
|
||||
if (!TryComp<PuddleComponent>(solutionEntity, out var puddleComponent))
|
||||
continue;
|
||||
|
||||
if (!_solutionSystem.TryGetSolution(solutionEntity, puddleComponent.SolutionName, out var solution))
|
||||
continue;
|
||||
|
||||
foreach (var solutionContent in solution.Contents.ToList())
|
||||
{
|
||||
if (solutionContent.Reagent.Prototype != "Blood")
|
||||
continue;
|
||||
|
||||
totalBloodAmount += solutionContent.Quantity;
|
||||
|
||||
_bloodstreamSystem.TryModifyBloodLevel(uid, solutionContent.Quantity / 6f);
|
||||
_solutionSystem.RemoveReagent(solutionEntity, solution, "Blood", FixedPoint2.MaxValue);
|
||||
|
||||
if (GetMissingBloodValue(bloodstreamComponent) == 0)
|
||||
{
|
||||
breakLoop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalBloodAmount == 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_audio.PlayPvs("/Audio/White/Cult/enter_blood.ogg", uid, AudioParams.Default);
|
||||
_damageableSystem.TryChangeDamage(uid, new DamageSpecifier(bruteDamageGroup, -20));
|
||||
_damageableSystem.TryChangeDamage(uid, new DamageSpecifier(burnDamageGroup, -20));
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private static FixedPoint2 GetMissingBloodValue(BloodstreamComponent bloodstreamComponent)
|
||||
{
|
||||
return bloodstreamComponent.BloodMaxVolume - bloodstreamComponent.BloodSolution.Volume;
|
||||
}
|
||||
|
||||
private void OnConcealPresence(EntityUid uid, CultistComponent component, CultConcealPresenceWorldActionEvent args)
|
||||
{
|
||||
if (!TryComp<BloodstreamComponent>(args.Performer, out _))
|
||||
return;
|
||||
}
|
||||
|
||||
private void OnSummonCombatEquipment(
|
||||
EntityUid uid,
|
||||
CultistComponent component,
|
||||
CultSummonCombatEquipmentTargetActionEvent args)
|
||||
{
|
||||
if (!TryComp<BloodstreamComponent>(args.Performer, out _))
|
||||
return;
|
||||
|
||||
_bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false);
|
||||
|
||||
var coordinates = Transform(uid).Coordinates;
|
||||
var helmet = Spawn("ClothingHeadHelmetCult", coordinates);
|
||||
var armor = Spawn("ClothingOuterArmorCult", coordinates);
|
||||
var shoes = Spawn("ClothingShoesCult", coordinates);
|
||||
var blade = Spawn("EldritchBlade", coordinates);
|
||||
var bola = Spawn("CultBola", coordinates);
|
||||
|
||||
_inventorySystem.TryUnequip(uid, "head");
|
||||
_inventorySystem.TryUnequip(uid, "outerClothing");
|
||||
_inventorySystem.TryUnequip(uid, "shoes");
|
||||
|
||||
_inventorySystem.TryEquip(uid, helmet, "head", force: true);
|
||||
_inventorySystem.TryEquip(uid, armor, "outerClothing", force: true);
|
||||
_inventorySystem.TryEquip(uid, shoes, "shoes", force: true);
|
||||
|
||||
_handsSystem.PickupOrDrop(uid, blade);
|
||||
_handsSystem.PickupOrDrop(uid, bola);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnElectromagneticPulse(
|
||||
EntityUid uid,
|
||||
CultistComponent component,
|
||||
CultElectromagneticPulseTargetActionEvent args)
|
||||
{
|
||||
if (!TryComp<BloodstreamComponent>(args.Performer, out _))
|
||||
return;
|
||||
|
||||
_bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false);
|
||||
|
||||
var xform = Transform(uid);
|
||||
|
||||
_empSystem.EmpPulse(xform.MapPosition, 10, 100000, 10f);
|
||||
_bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnShadowShackles(EntityUid uid, CultistComponent component, CultShadowShacklesTargetActionEvent args)
|
||||
{
|
||||
if (!TryComp<BloodstreamComponent>(args.Performer, out _))
|
||||
return;
|
||||
|
||||
_bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false);
|
||||
|
||||
var cuffs = Spawn("CultistCuffs", Transform(uid).Coordinates);
|
||||
_handsSystem.TryPickupAnyHand(uid, cuffs);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnTwistedConstructionAction(
|
||||
EntityUid uid,
|
||||
CultistComponent component,
|
||||
CultTwistedConstructionActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstreamComponent))
|
||||
return;
|
||||
|
||||
if (!_entityManager.TryGetComponent<StackComponent>(args.Target, out var stack))
|
||||
return;
|
||||
|
||||
if (stack.StackTypeId != SteelPrototypeId)
|
||||
return;
|
||||
|
||||
var transform = Transform(args.Target).Coordinates;
|
||||
var count = stack.Count;
|
||||
|
||||
_entityManager.DeleteEntity(args.Target);
|
||||
|
||||
var material = _entityManager.SpawnEntity(RunicMetalPrototypeId, transform);
|
||||
|
||||
_bloodstreamSystem.TryModifyBloodLevel(args.Performer, -15, bloodstreamComponent, false);
|
||||
|
||||
if (!_entityManager.TryGetComponent<StackComponent>(material, out var stackNew))
|
||||
return;
|
||||
|
||||
stackNew.Count = count;
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("Конвертируем сталь в руиник металл!"), args.Performer, args.Performer);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnSummonDaggerAction(EntityUid uid, CultistComponent component, CultSummonDaggerActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstreamComponent))
|
||||
return;
|
||||
|
||||
var xform = Transform(args.Performer).Coordinates;
|
||||
var dagger = _entityManager.SpawnEntity(RitualDaggerPrototypeId, xform);
|
||||
|
||||
_bloodstreamSystem.TryModifyBloodLevel(args.Performer, -30, bloodstreamComponent, false);
|
||||
_handsSystem.TryPickupAnyHand(args.Performer, dagger);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using Content.Server.White.Cult.Runes.Comps;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.White.Cult.Runes.Systems;
|
||||
|
||||
public partial class CultSystem
|
||||
{
|
||||
public void InitializeBarrierSystem()
|
||||
{
|
||||
SubscribeLocalEvent<CultBarrierComponent, ActivateInWorldEvent>(OnActivateBarrier);
|
||||
SubscribeLocalEvent<CultBarrierComponent, InteractUsingEvent>(OnInteract);
|
||||
}
|
||||
|
||||
[Dependency] private readonly PhysicsSystem _physicsSystem = default!;
|
||||
|
||||
|
||||
private void OnActivateBarrier(EntityUid uid, CultBarrierComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
|
||||
if (!HasComp<CultistComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (component.Activated)
|
||||
Deactivate(args.Target);
|
||||
else
|
||||
Activate(args.Target);
|
||||
|
||||
}
|
||||
|
||||
private void Activate(EntityUid barrier)
|
||||
{
|
||||
if (!TryComp<CultBarrierComponent>(barrier, out var barrierComponent))
|
||||
return;
|
||||
|
||||
if (HasComp<StealthOnMoveComponent>(barrier))
|
||||
RemComp<StealthOnMoveComponent>(barrier);
|
||||
|
||||
if (HasComp<StealthComponent>(barrier))
|
||||
RemComp<StealthComponent>(barrier);
|
||||
|
||||
_physicsSystem.SetCanCollide(barrier, true);
|
||||
|
||||
barrierComponent.Activated = true;
|
||||
}
|
||||
|
||||
private void Deactivate(EntityUid barrier)
|
||||
{
|
||||
if (!TryComp<CultBarrierComponent>(barrier, out var barrierComponent))
|
||||
return;
|
||||
|
||||
EnsureComp<StealthComponent>(barrier);
|
||||
EnsureComp<StealthOnMoveComponent>(barrier);
|
||||
|
||||
_physicsSystem.SetCanCollide(barrier, false);
|
||||
|
||||
barrierComponent.Activated = false;
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, CultBarrierComponent component, InteractUsingEvent args)
|
||||
{
|
||||
var entityPrototype = _entityManager.GetComponent<MetaDataComponent>(args.Used).EntityPrototype;
|
||||
|
||||
if (entityPrototype == null)
|
||||
return;
|
||||
|
||||
var used = entityPrototype.ID;
|
||||
var user = args.User;
|
||||
var target = args.Target;
|
||||
|
||||
if (used != RitualDaggerPrototypeId)
|
||||
return;
|
||||
|
||||
if (!HasComp<CultistComponent>(user))
|
||||
return;
|
||||
|
||||
_popupSystem.PopupEntity("Вы уничтожаете барьер", user, user);
|
||||
|
||||
_entityManager.DeleteEntity(target);
|
||||
}
|
||||
}
|
||||
113
Content.Server/_White/Cult/Runes/Systems/CultSystem.Buff.cs
Normal file
113
Content.Server/_White/Cult/Runes/Systems/CultSystem.Buff.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.White.Cult.GameRule;
|
||||
using Content.Server.White.Cult.Runes.Comps;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.White.Cult;
|
||||
using Content.Shared.White.Cult.Components;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.White.Cult.Runes.Systems;
|
||||
|
||||
public partial class CultSystem
|
||||
{
|
||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinition = default!;
|
||||
|
||||
public void InitializeBuffSystem()
|
||||
{
|
||||
SubscribeLocalEvent<CultBuffComponent, ComponentAdd>(OnAdd);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
UpdateBuffTimers(frameTime);
|
||||
AnyCultistNearTile();
|
||||
RemoveExpiredBuffs();
|
||||
}
|
||||
|
||||
private void AnyCultistNearTile()
|
||||
{
|
||||
var cultists = EntityQuery<CultistComponent>();
|
||||
|
||||
foreach (var cultist in cultists)
|
||||
{
|
||||
var uid = cultist.Owner;
|
||||
|
||||
if (HasComp<CultBuffComponent>(uid))
|
||||
continue;
|
||||
|
||||
if (!AnyCultTilesNearby(uid))
|
||||
continue;
|
||||
|
||||
var comp = EnsureComp<CultBuffComponent>(uid);
|
||||
comp.BuffTime = CultBuffComponent.CultTileBuffTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAdd(EntityUid uid, CultBuffComponent comp, ComponentAdd args)
|
||||
{
|
||||
_alertsSystem.ShowAlert(uid, AlertType.CultBuffed);
|
||||
}
|
||||
|
||||
private void UpdateBuffTimers(float frameTime)
|
||||
{
|
||||
var buffs = EntityQuery<CultBuffComponent>();
|
||||
|
||||
foreach (var buff in buffs)
|
||||
{
|
||||
var uid = buff.Owner;
|
||||
var remainingTime = buff.BuffTime;
|
||||
|
||||
remainingTime -= TimeSpan.FromSeconds(frameTime);
|
||||
|
||||
if (TryComp<CultistComponent>(uid, out var cultist))
|
||||
{
|
||||
if (remainingTime < CultBuffComponent.CultTileBuffTime && AnyCultTilesNearby(uid))
|
||||
remainingTime = CultBuffComponent.CultTileBuffTime;
|
||||
}
|
||||
|
||||
buff.BuffTime = remainingTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool AnyCultTilesNearby(EntityUid uid)
|
||||
{
|
||||
var localpos = Transform(uid).Coordinates.Position;
|
||||
|
||||
if (!TryComp<CultistComponent>(uid, out var cultist))
|
||||
return false;
|
||||
|
||||
var radius = CultBuffComponent.NearbyTilesBuffRadius;
|
||||
|
||||
if (!_mapManager.TryGetGrid(Transform(uid).GridUid, out var grid))
|
||||
return false;
|
||||
|
||||
var tilesRefs = grid.GetLocalTilesIntersecting(new Box2(localpos + new Vector2(-radius, -radius), localpos + new Vector2(radius, radius)));
|
||||
var cultTileDef = (ContentTileDefinition) _tileDefinition[$"{CultRuleComponent.CultFloor}"];
|
||||
var cultTile = new Tile(cultTileDef.TileId);
|
||||
|
||||
return tilesRefs.Any(tileRef => tileRef.Tile.TypeId == cultTile.TypeId);
|
||||
}
|
||||
|
||||
private void RemoveExpiredBuffs()
|
||||
{
|
||||
var buffs = EntityQuery<CultBuffComponent>();
|
||||
|
||||
foreach (var buff in buffs)
|
||||
{
|
||||
var uid = buff.Owner;
|
||||
var remainingTime = buff.BuffTime;
|
||||
|
||||
if (remainingTime <= TimeSpan.Zero)
|
||||
{
|
||||
RemComp<CultBuffComponent>(uid);
|
||||
_alertsSystem.ClearAlert(uid, AlertType.CultBuffed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.White.Cult.Runes.Components;
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.White.Cult.Runes.Systems;
|
||||
|
||||
public partial class CultSystem
|
||||
{
|
||||
[Dependency] private readonly ItemSlotsSystem _slotsSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public void InitializeConstructs()
|
||||
{
|
||||
SubscribeLocalEvent<ConstructShellComponent, ContainerIsInsertingAttemptEvent>(OnShardInsertAttempt);
|
||||
SubscribeLocalEvent<ConstructShellComponent, ComponentInit>(OnShellInit);
|
||||
SubscribeLocalEvent<ConstructShellComponent, ComponentRemove>(OnShellRemove);
|
||||
SubscribeLocalEvent<ConstructShellComponent, ConstructFormSelectedEvent>(OnShellSelected);
|
||||
}
|
||||
|
||||
private void OnShellSelected(EntityUid uid, ConstructShellComponent component, ConstructFormSelectedEvent args)
|
||||
{
|
||||
var construct = Spawn(args.SelectedForm, Transform(args.Entity).MapPosition);
|
||||
var mind = Comp<MindContainerComponent>(args.Session.AttachedEntity!.Value);
|
||||
|
||||
if(!mind.HasMind)
|
||||
return;
|
||||
|
||||
_mindSystem.TransferTo(mind.Mind.Value, construct);
|
||||
Del(args.Entity);
|
||||
}
|
||||
|
||||
private void OnShellInit(EntityUid uid, ConstructShellComponent component, ComponentInit args)
|
||||
{
|
||||
_slotsSystem.AddItemSlot(uid, component.ShardSlotId, component.ShardSlot);
|
||||
}
|
||||
|
||||
private void OnShellRemove(EntityUid uid, ConstructShellComponent component, ComponentRemove args)
|
||||
{
|
||||
_slotsSystem.RemoveItemSlot(uid, component.ShardSlot);
|
||||
}
|
||||
|
||||
private void OnShardInsertAttempt(EntityUid uid, ConstructShellComponent component, ContainerIsInsertingAttemptEvent args)
|
||||
{
|
||||
if (!TryComp<MindContainerComponent>(args.EntityUid, out var mindContainer) || !mindContainer.HasMind || !TryComp<ActorComponent>(args.EntityUid, out var actor))
|
||||
{
|
||||
_popupSystem.PopupEntity("Нет души", uid);
|
||||
args.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
_slotsSystem.SetLock(uid, component.ShardSlotId, true);
|
||||
_ui.TryOpen(uid, SelectConstructUi.Key, actor.PlayerSession);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
using System.Linq;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Maps;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.White.Cult.GameRule;
|
||||
using Content.Server.White.IncorporealSystem;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.White.Cult;
|
||||
|
||||
namespace Content.Server.White.Cult.Runes.Systems;
|
||||
|
||||
public partial class CultSystem
|
||||
{
|
||||
[Dependency] private readonly TileSystem _tileSystem = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookupSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
|
||||
public void InitializeConstructsAbilities()
|
||||
{
|
||||
SubscribeLocalEvent<ArtificerCreateSoulStoneActionEvent>(OnArtificerCreateSoulStone);
|
||||
SubscribeLocalEvent<ArtificerCreateConstructShellActionEvent>(OnArtificerCreateConstructShell);
|
||||
SubscribeLocalEvent<ArtificerConvertCultistFloorActionEvent>(OnArtificerConvertCultistFloor);
|
||||
SubscribeLocalEvent<ArtificerCreateCultistWallActionEvent>(OnArtificerCreateCultistWall);
|
||||
SubscribeLocalEvent<ArtificerCreateCultistAirlockActionEvent>(OnArtificerCreateCultistAirlock);
|
||||
|
||||
SubscribeLocalEvent<WraithPhaseActionEvent>(OnWraithPhase);
|
||||
SubscribeLocalEvent<IncorporealComponent, AttackAttemptEvent>(OnAttackAttempt);
|
||||
|
||||
SubscribeLocalEvent<JuggernautCreateWallActionEvent>(OnJuggernautCreateWall);
|
||||
|
||||
SubscribeLocalEvent<ConstructComponent, ComponentInit>(OnConstructInit);
|
||||
SubscribeLocalEvent<ConstructComponent, ComponentRemove>(OnConstructComponentRemoved);
|
||||
}
|
||||
|
||||
private void OnConstructInit(EntityUid uid, ConstructComponent component, ComponentInit args)
|
||||
{
|
||||
foreach (var action in component.Actions)
|
||||
{
|
||||
var actionPrototype = _prototypeManager.Index<InstantActionPrototype>(action);
|
||||
_actionsSystem.AddAction(uid, new InstantAction(actionPrototype), uid);
|
||||
}
|
||||
|
||||
var query = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
|
||||
|
||||
while (query.MoveNext(out var ruleEnt, out var cultRuleComponent, out _))
|
||||
{
|
||||
if (!_gameTicker.IsGameRuleAdded(ruleEnt))
|
||||
continue;
|
||||
|
||||
cultRuleComponent.Constructs.Add(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConstructComponentRemoved(EntityUid uid, ConstructComponent component, ComponentRemove args)
|
||||
{
|
||||
var query = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
|
||||
while (query.MoveNext(out var ruleEnt, out var cultRuleComponent, out _))
|
||||
{
|
||||
if (!_gameTicker.IsGameRuleAdded(ruleEnt))
|
||||
continue;
|
||||
|
||||
cultRuleComponent.Constructs.Remove(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnArtificerCreateSoulStone(ArtificerCreateSoulStoneActionEvent ev)
|
||||
{
|
||||
var transform = Transform(ev.Performer);
|
||||
Spawn(ev.SoulStonePrototypeId, transform.Coordinates);
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
|
||||
private void OnArtificerCreateConstructShell(ArtificerCreateConstructShellActionEvent ev)
|
||||
{
|
||||
var transform = Transform(ev.Performer);
|
||||
Spawn(ev.ShellPrototypeId, transform.Coordinates);
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
|
||||
private void OnArtificerConvertCultistFloor(ArtificerConvertCultistFloorActionEvent ev)
|
||||
{
|
||||
var transform = Transform(ev.Performer);
|
||||
var gridUid = transform.GridUid;
|
||||
|
||||
if (!gridUid.HasValue)
|
||||
{
|
||||
_popupSystem.PopupEntity("Нельзя строить в космосе...", ev.Performer, ev.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
var tileRef = transform.Coordinates.GetTileRef();
|
||||
|
||||
if (!tileRef.HasValue)
|
||||
{
|
||||
_popupSystem.PopupEntity("Нельзя строить в космосе...", ev.Performer, ev.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
var cultistTileDefinition = (ContentTileDefinition) _tileDefinition[ev.FloorTileId];
|
||||
_tileSystem.ReplaceTile(tileRef.Value, cultistTileDefinition);
|
||||
Spawn("CultTileSpawnEffect", transform.Coordinates);
|
||||
ev.Handled = true;
|
||||
}
|
||||
|
||||
private void OnArtificerCreateCultistWall(ArtificerCreateCultistWallActionEvent ev)
|
||||
{
|
||||
if (!TrySpawnWall(ev.Performer, ev.WallPrototypeId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
|
||||
private void OnArtificerCreateCultistAirlock(ArtificerCreateCultistAirlockActionEvent ev)
|
||||
{
|
||||
if (!TrySpawnWall(ev.Performer, ev.AirlockPrototypeId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
|
||||
private void OnWraithPhase(WraithPhaseActionEvent ev)
|
||||
{
|
||||
if (_statusEffectsSystem.HasStatusEffect(ev.Performer, ev.StatusEffectId))
|
||||
{
|
||||
_popupSystem.PopupEntity("Вы уже в потустороннем мире", ev.Performer, ev.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
_statusEffectsSystem.TryAddStatusEffect<IncorporealComponent>(ev.Performer, ev.StatusEffectId,
|
||||
TimeSpan.FromSeconds(ev.Duration), false);
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
|
||||
private void OnAttackAttempt(EntityUid uid, IncorporealComponent component, AttackAttemptEvent args)
|
||||
{
|
||||
if (_statusEffectsSystem.HasStatusEffect(args.Uid, "Incorporeal"))
|
||||
{
|
||||
_statusEffectsSystem.TryRemoveStatusEffect(args.Uid, "Incorporeal");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnJuggernautCreateWall(JuggernautCreateWallActionEvent ev)
|
||||
{
|
||||
if (!TrySpawnWall(ev.Performer, ev.WallPrototypeId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
|
||||
private bool TrySpawnWall(EntityUid performer, string wallPrototypeId)
|
||||
{
|
||||
var xform = Transform(performer);
|
||||
|
||||
var offsetValue = xform.LocalRotation.ToWorldVec().Normalized();
|
||||
var coords = xform.Coordinates.Offset(offsetValue).SnapToGrid(EntityManager);
|
||||
var tile = coords.GetTileRef();
|
||||
if (tile == null)
|
||||
return false;
|
||||
|
||||
// Check there are no walls there
|
||||
if (_turf.IsTileBlocked(tile.Value, CollisionGroup.Impassable))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("mime-invisible-wall-failed"), performer, performer);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check there are no mobs there
|
||||
foreach (var entity in _lookupSystem.GetEntitiesIntersecting(tile.Value))
|
||||
{
|
||||
if (HasComp<MobStateComponent>(entity) && entity != performer)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("mime-invisible-wall-failed"), performer, performer);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("mime-invisible-wall-popup", ("mime", performer)), performer);
|
||||
// Make sure we set the invisible wall to despawn properly
|
||||
Spawn(wallPrototypeId, coords);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Threading;
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Server.GameObjects;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Content.Server.White.Cult.Runes.Systems;
|
||||
|
||||
public partial class CultSystem
|
||||
{
|
||||
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
|
||||
|
||||
private void InitializeNarsie()
|
||||
{
|
||||
SubscribeLocalEvent<NarsieComponent, ComponentInit>(OnNarsieComponentInit);
|
||||
}
|
||||
|
||||
private void OnNarsieComponentInit(EntityUid uid, NarsieComponent component, ComponentInit args)
|
||||
{
|
||||
_appearanceSystem.SetData(uid, NarsieVisualState.VisualState, NarsieVisuals.Spawning);
|
||||
|
||||
Timer.Spawn(TimeSpan.FromSeconds(6), () =>
|
||||
{
|
||||
_appearanceSystem.SetData(uid, NarsieVisualState.VisualState, NarsieVisuals.Spawned);
|
||||
});
|
||||
}
|
||||
}
|
||||
1301
Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs
Normal file
1301
Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.White.Cult.Runes.Comps;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.White.Cult;
|
||||
using Content.Shared.White.Cult.Items;
|
||||
|
||||
namespace Content.Server.White.Cult.Runes.Systems;
|
||||
|
||||
public partial class CultSystem
|
||||
{
|
||||
[Dependency] private readonly SharedPointLightSystem _lightSystem = default!;
|
||||
[Dependency] private readonly MindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roleSystem = default!;
|
||||
|
||||
|
||||
public void InitializeSoulShard()
|
||||
{
|
||||
SubscribeLocalEvent<SoulShardComponent, AfterInteractEvent>(OnShardInteractUse);
|
||||
SubscribeLocalEvent<SoulShardComponent, MindAddedMessage>(OnShardMindAdded);
|
||||
SubscribeLocalEvent<SoulShardComponent, MindRemovedMessage>(OnShardMindRemoved);
|
||||
}
|
||||
|
||||
private void OnShardInteractUse(EntityUid uid, SoulShardComponent component, AfterInteractEvent args)
|
||||
{
|
||||
var target = args.Target;
|
||||
|
||||
if (!HasComp<CultistComponent>(args.User)) return;
|
||||
|
||||
if (!TryComp<MobStateComponent>(target, out var state) || state.CurrentState != MobState.Dead) return;
|
||||
|
||||
if (!TryComp<MindContainerComponent>(target, out var mindComponent) || !mindComponent.Mind.HasValue || !TryComp<HumanoidAppearanceComponent>(target, out _)) return;
|
||||
|
||||
_mindSystem.TransferTo(mindComponent.Mind.Value, uid);
|
||||
|
||||
var targetName = MetaData(target.Value).EntityName;
|
||||
|
||||
_metaDataSystem.SetEntityName(uid, Loc.GetString("soul-shard-description", ("soul", targetName)));
|
||||
_metaDataSystem.SetEntityDescription(uid, Loc.GetString("soul-shard-description", ("soul", targetName)));
|
||||
}
|
||||
|
||||
private void OnShardMindAdded(EntityUid uid, SoulShardComponent component, MindAddedMessage args)
|
||||
{
|
||||
if (!TryComp<MindContainerComponent>(uid, out var mindContainer) || !mindContainer.HasMind)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_roleSystem.MindHasRole<TraitorRoleComponent>(mindContainer.Mind.Value))
|
||||
{
|
||||
_roleSystem.MindRemoveRole<TraitorRoleComponent>(mindContainer.Mind.Value);
|
||||
}
|
||||
|
||||
_appearanceSystem.SetData(uid, SoulShardVisualState.State, true);
|
||||
_lightSystem.SetEnabled(uid, true);
|
||||
}
|
||||
|
||||
private void OnShardMindRemoved(EntityUid uid, SoulShardComponent component, MindRemovedMessage args)
|
||||
{
|
||||
_appearanceSystem.SetData(uid, SoulShardVisualState.State, false);
|
||||
_lightSystem.SetEnabled(uid, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
|
||||
namespace Content.Server.White.Cult.Structures;
|
||||
|
||||
public sealed class CultStructureCraftSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RunicMetalComponent, UseInHandEvent>(OnUseInHand);
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, RunicMetalComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (!HasComp<CultistComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (!_playerManager.TryGetSessionByEntity(args.User, out var session) || session is not IPlayerSession playerSession)
|
||||
return;
|
||||
|
||||
if (component.UserInterface != null)
|
||||
{
|
||||
_uiSystem.CloseUi(component.UserInterface, playerSession);
|
||||
_uiSystem.OpenUi(component.UserInterface, playerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.Cult.Structures;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class RunicDoorComponent : Component
|
||||
{
|
||||
}
|
||||
74
Content.Server/_White/Cult/Structures/RunicDoorSystem.cs
Normal file
74
Content.Server/_White/Cult/Structures/RunicDoorSystem.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Content.Server.Doors.Systems;
|
||||
using Content.Shared.Doors;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.White.Cult;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
|
||||
namespace Content.Server.White.Cult.Structures;
|
||||
|
||||
public sealed class RunicDoorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RunicDoorComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
||||
SubscribeLocalEvent<RunicDoorComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
|
||||
}
|
||||
|
||||
private void OnBeforeDoorOpened(EntityUid uid, RunicDoorComponent component, BeforeDoorOpenedEvent args)
|
||||
{
|
||||
args.Uncancel();
|
||||
|
||||
if (!args.User.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Process(uid, args.User.Value))
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeforeDoorClosed(EntityUid uid, RunicDoorComponent component, BeforeDoorClosedEvent args)
|
||||
{
|
||||
args.Uncancel();
|
||||
|
||||
if (!args.User.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Process(uid, args.User.Value))
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private bool Process(EntityUid airlock, EntityUid user)
|
||||
{
|
||||
if (HasComp<CultistComponent>(user) || HasComp<ConstructComponent>(user))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
_doorSystem.Deny(airlock);
|
||||
|
||||
if (!HasComp<HumanoidAppearanceComponent>(user))
|
||||
return false;
|
||||
|
||||
var direction = Transform(user).MapPosition.Position - Transform(airlock).MapPosition.Position;
|
||||
var impulseVector = direction * 7000;
|
||||
|
||||
_physics.ApplyLinearImpulse(user, impulseVector);
|
||||
|
||||
_stunSystem.TryParalyze(user, TimeSpan.FromSeconds(3), true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Content.Server.White.Cult.Structures;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class RunicGirderComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public string UsedItemID = "RitualDagger";
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public string DropItemID = "CultRunicMetal1";
|
||||
}
|
||||
30
Content.Server/_White/Cult/Structures/RunicGirderSystem.cs
Normal file
30
Content.Server/_White/Cult/Structures/RunicGirderSystem.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.White.Cult;
|
||||
|
||||
namespace Content.Server.White.Cult.Structures;
|
||||
|
||||
public sealed class RunicGirderSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entMan = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RunicGirderComponent, AfterInteractUsingEvent>(OnInteract);
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, RunicGirderComponent component, AfterInteractUsingEvent args)
|
||||
{
|
||||
if (!HasComp<CultistComponent>(args.User))
|
||||
return;
|
||||
if (MetaData(args.Used).EntityPrototype?.ID != component.UsedItemID)
|
||||
return;
|
||||
if (args.Target == null)
|
||||
return;
|
||||
|
||||
var pos = Transform(args.Target.Value).Coordinates;
|
||||
_entMan.DeleteEntity(args.Target.Value);
|
||||
_entMan.SpawnEntity(component.DropItemID, pos);
|
||||
}
|
||||
}
|
||||
15
Content.Server/_White/Cult/Structures/RunicMetalComponent.cs
Normal file
15
Content.Server/_White/Cult/Structures/RunicMetalComponent.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.White.Cult.Structures;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.White.Cult.Structures;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class RunicMetalComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public BoundUserInterface? UserInterface => Owner.GetUIOrNull(CultStructureCraftUiKey.Key);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("delay")]
|
||||
public float Delay = 1;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.White.Cult;
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Server.White.Cult.TimedProduction;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CultistFactoryComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
[DataField("cooldown")]
|
||||
public int Cooldown = 240;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public TimeSpan? NextTimeUse;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
[DataField("products", customTypeSerializer: typeof(PrototypeIdListSerializer<CultistFactoryProductionPrototype>))]
|
||||
public IReadOnlyCollection<string> Products = ArraySegment<string>.Empty;
|
||||
|
||||
public BoundUserInterface? UserInterface => Owner.GetUIOrNull(CultistAltarUiKey.Key);
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool Active = true;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.White.Cult;
|
||||
using Content.Shared.White.Cult.Structures;
|
||||
using Content.Shared.White.Cult.UI;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.White.Cult.TimedProduction;
|
||||
|
||||
public sealed class CultistFactorySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
||||
private const string RitualDaggerPrototypeId = "RitualDagger";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CultistFactoryComponent, InteractHandEvent>(OnInteract);
|
||||
SubscribeLocalEvent<CultistFactoryComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<CultistFactoryComponent, CultistFactoryItemSelectedMessage>(OnSelected);
|
||||
|
||||
SubscribeLocalEvent<CultistFactoryComponent, InteractUsingEvent>(TryToggleAnchor);
|
||||
SubscribeLocalEvent<CultistFactoryComponent, CultAnchorDoAfterEvent>(OnAnchorDoAfter);
|
||||
SubscribeLocalEvent<CultistFactoryComponent, ExaminedEvent>(OnExamine);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var structures = EntityQuery<CultistFactoryComponent>();
|
||||
foreach (var structure in structures)
|
||||
{
|
||||
if (structure.Active)
|
||||
continue;
|
||||
|
||||
if (_gameTiming.CurTime > structure.NextTimeUse)
|
||||
{
|
||||
structure.Active = true;
|
||||
UpdateAppearance(structure.Owner, structure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, CultistFactoryComponent component, ComponentInit args)
|
||||
{
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, CultistFactoryComponent component, InteractHandEvent args)
|
||||
{
|
||||
if (!TryComp<ActorComponent>(args.User, out var actor))
|
||||
return;
|
||||
|
||||
if (!HasComp<CultistComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (!CanCraft(uid, component, args.User))
|
||||
return;
|
||||
|
||||
var xform = Transform(uid);
|
||||
if (!xform.Anchored)
|
||||
return;
|
||||
|
||||
if (_ui.TryGetUi(uid, CultistAltarUiKey.Key, out var bui))
|
||||
{
|
||||
UserInterfaceSystem.SetUiState(bui, new CultistFactoryBUIState(component.Products));
|
||||
_ui.OpenUi(bui, actor.PlayerSession);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelected(EntityUid uid, CultistFactoryComponent component, CultistFactoryItemSelectedMessage args)
|
||||
{
|
||||
var user = args.Session.AttachedEntity;
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
if (!CanCraft(uid, component, user.Value))
|
||||
return;
|
||||
|
||||
if (!_prototypeManager.TryIndex<CultistFactoryProductionPrototype>(args.Item, out var prototype))
|
||||
return;
|
||||
|
||||
foreach (var item in prototype.Item)
|
||||
{
|
||||
var entity = Spawn(item, Transform(user.Value).Coordinates);
|
||||
_handsSystem.TryPickupAnyHand(user.Value, entity);
|
||||
}
|
||||
|
||||
component.NextTimeUse = _gameTiming.CurTime + TimeSpan.FromSeconds(component.Cooldown);
|
||||
component.Active = false;
|
||||
UpdateAppearance(uid, component);
|
||||
}
|
||||
|
||||
private void TryToggleAnchor(EntityUid uid, CultistFactoryComponent component, InteractUsingEvent args)
|
||||
{
|
||||
var entityPrototype = _entityManager.GetComponent<MetaDataComponent>(args.Used).EntityPrototype;
|
||||
|
||||
if (entityPrototype == null)
|
||||
return;
|
||||
|
||||
var used = entityPrototype.ID;
|
||||
var user = args.User;
|
||||
const int time = 2;
|
||||
|
||||
if (used != RitualDaggerPrototypeId)
|
||||
return;
|
||||
|
||||
if (!HasComp<CultistComponent>(user))
|
||||
return;
|
||||
|
||||
var xform = Transform(uid);
|
||||
var ev = new CultAnchorDoAfterEvent(xform.Anchored);
|
||||
var doArgs = new DoAfterArgs(user, time, ev, uid, uid);
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(doArgs);
|
||||
}
|
||||
|
||||
private void OnAnchorDoAfter(EntityUid uid, CultistFactoryComponent component, CultAnchorDoAfterEvent args)
|
||||
{
|
||||
if (!args.Target.HasValue)
|
||||
return;
|
||||
|
||||
var target = args.Target.Value;
|
||||
var xform = Transform(target);
|
||||
|
||||
if (args.IsAnchored)
|
||||
{
|
||||
_transform.Unanchor(target, xform);
|
||||
_popup.PopupClient(Loc.GetString("anchorable-unanchored"), uid, args.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
_transform.AnchorEntity(target, xform);
|
||||
_popup.PopupClient(Loc.GetString("anchorable-anchored"), uid, args.User);
|
||||
}
|
||||
|
||||
_audio.PlayPvs("/Audio/Items/ratchet.ogg", uid);
|
||||
}
|
||||
|
||||
private void OnExamine(EntityUid uid, CultistFactoryComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!HasComp<CultistComponent>(args.Examiner))
|
||||
return;
|
||||
|
||||
var isAnchored = Comp<TransformComponent>(uid).Anchored;
|
||||
var messageId = isAnchored ? "examinable-anchored" : "examinable-unanchored";
|
||||
args.PushMarkup(Loc.GetString(messageId, ("target", uid)));
|
||||
}
|
||||
|
||||
private bool CanCraft(EntityUid uid, CultistFactoryComponent component, EntityUid user)
|
||||
{
|
||||
if (component.NextTimeUse == null || _gameTiming.CurTime > component.NextTimeUse)
|
||||
{
|
||||
component.Active = true;
|
||||
UpdateAppearance(uid, component);
|
||||
return true;
|
||||
}
|
||||
|
||||
var name = MetaData(uid).EntityName;
|
||||
var totalSeconds = (component.NextTimeUse - _gameTiming.CurTime).Value.TotalSeconds;
|
||||
var seconds = Convert.ToInt32(totalSeconds);
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("cultist-factory-charging", ("name", name),
|
||||
("seconds", seconds)), uid, user);
|
||||
|
||||
UpdateAppearance(uid, component);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, CultistFactoryComponent component)
|
||||
{
|
||||
AppearanceComponent? appearance = null;
|
||||
if (!Resolve(uid, ref appearance, false))
|
||||
return;
|
||||
|
||||
_appearance.SetData(uid, CultCraftStructureVisuals.Activated, component.Active);
|
||||
}
|
||||
}
|
||||
103
Content.Server/_White/Cult/UI/TeleportSpellEui.cs
Normal file
103
Content.Server/_White/Cult/UI/TeleportSpellEui.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
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 Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.White.Cult.UI;
|
||||
|
||||
public sealed class TeleportSpellEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
private SharedTransformSystem _transformSystem;
|
||||
private PopupSystem _popupSystem;
|
||||
|
||||
|
||||
private EntityUid _performer;
|
||||
private EntityUid _target;
|
||||
|
||||
private EntityCoordinates _initialOwnerCoords;
|
||||
private EntityCoordinates _initialTargetCoords;
|
||||
|
||||
private bool _used;
|
||||
|
||||
|
||||
public TeleportSpellEui(EntityUid performer, EntityUid target)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_transformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
_popupSystem = _entityManager.System<PopupSystem>();
|
||||
|
||||
_performer = performer;
|
||||
_target = target;
|
||||
|
||||
_initialOwnerCoords = _entityManager.GetComponent<TransformComponent>(_performer).Coordinates;
|
||||
_initialTargetCoords = _entityManager.GetComponent<TransformComponent>(_target).Coordinates;
|
||||
|
||||
Timer.Spawn(TimeSpan.FromSeconds(10), Close );
|
||||
}
|
||||
|
||||
public override EuiStateBase GetNewState()
|
||||
{
|
||||
var runes = _entityManager.EntityQuery<CultRuneTeleportComponent>();
|
||||
var state = new TeleportSpellEuiState();
|
||||
|
||||
foreach (var rune in runes)
|
||||
{
|
||||
state.Runes.Add((int)rune.Owner, rune.Label!);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public override void HandleMessage(EuiMessageBase msg)
|
||||
{
|
||||
base.HandleMessage(msg);
|
||||
|
||||
if(_used) return;
|
||||
|
||||
if (msg is not TeleportSpellTargetRuneSelected cast)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var performerPosition = _entityManager.GetComponent<TransformComponent>(_performer).Coordinates;
|
||||
var targetPosition = _entityManager.GetComponent<TransformComponent>(_target).Coordinates;;
|
||||
|
||||
performerPosition.TryDistance(_entityManager, targetPosition, out var distance);
|
||||
|
||||
if(distance > 1.5f)
|
||||
{
|
||||
_popupSystem.PopupEntity("Too far", _performer, PopupType.Medium);
|
||||
return;
|
||||
}
|
||||
|
||||
TransformComponent? runeTransform = null!;
|
||||
|
||||
foreach (var runeComponent in _entityManager.EntityQuery<CultRuneTeleportComponent>())
|
||||
{
|
||||
if (runeComponent.Owner == new EntityUid(cast.RuneUid))
|
||||
{
|
||||
runeTransform = _entityManager.GetComponent<TransformComponent>(runeComponent.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
if (runeTransform is null)
|
||||
{
|
||||
_popupSystem.PopupEntity("Rune is gone", _performer);
|
||||
DoStateUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
_used = true;
|
||||
|
||||
_transformSystem.SetCoordinates(_target, runeTransform.Coordinates);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Server.White.IncorporealSystem;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class IncorporealComponent : Component
|
||||
{
|
||||
[DataField("movementSpeedBuff")]
|
||||
public float MovementSpeedBuff = 1.5f;
|
||||
}
|
||||
72
Content.Server/_White/IncorporealSystem/IncorporealSystem.cs
Normal file
72
Content.Server/_White/IncorporealSystem/IncorporealSystem.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Visible;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
|
||||
namespace Content.Server.White.IncorporealSystem;
|
||||
|
||||
public sealed class IncorporealSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly VisibilitySystem _visibilitySystem = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<IncorporealComponent, ComponentStartup>(OnComponentInit);
|
||||
SubscribeLocalEvent<IncorporealComponent, ComponentShutdown>(OnComponentRemoved);
|
||||
SubscribeLocalEvent<IncorporealComponent, RefreshMovementSpeedModifiersEvent>(OnRefresh);
|
||||
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, IncorporealComponent component, ComponentStartup args)
|
||||
{
|
||||
if (TryComp<FixturesComponent>(uid, out var fixtures) && fixtures.FixtureCount >= 1)
|
||||
{
|
||||
var fixture = fixtures.Fixtures.First();
|
||||
|
||||
_physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) CollisionGroup.GhostImpassable, fixtures);
|
||||
_physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, 0, fixtures);
|
||||
}
|
||||
|
||||
if (TryComp<VisibilityComponent>(uid, out var visibility))
|
||||
{
|
||||
_visibilitySystem.AddLayer(uid, visibility, (int) VisibilityFlags.Ghost, false);
|
||||
_visibilitySystem.RemoveLayer(uid, visibility, (int) VisibilityFlags.Normal, false);
|
||||
_visibilitySystem.RefreshVisibility(uid);
|
||||
}
|
||||
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
private void OnComponentRemoved(EntityUid uid, IncorporealComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (TryComp<FixturesComponent>(uid, out var fixtures) && fixtures.FixtureCount >= 1)
|
||||
{
|
||||
var fixture = fixtures.Fixtures.First();
|
||||
|
||||
_physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) (CollisionGroup.FlyingMobMask | CollisionGroup.GhostImpassable), fixtures);
|
||||
_physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, (int) CollisionGroup.FlyingMobLayer, fixtures);
|
||||
}
|
||||
|
||||
if (TryComp<VisibilityComponent>(uid, out var visibility))
|
||||
{
|
||||
_visibilitySystem.RemoveLayer(uid, visibility, (int) VisibilityFlags.Ghost, false);
|
||||
_visibilitySystem.AddLayer(uid, visibility, (int) VisibilityFlags.Normal, false);
|
||||
_visibilitySystem.RefreshVisibility(uid);
|
||||
}
|
||||
|
||||
component.MovementSpeedBuff = 1;
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
private void OnRefresh(EntityUid uid, IncorporealComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||
{
|
||||
args.ModifySpeed(component.MovementSpeedBuff, component.MovementSpeedBuff);
|
||||
}
|
||||
}
|
||||
24
Content.Server/_White/SharpeningSystem/SharpenerComponent.cs
Normal file
24
Content.Server/_White/SharpeningSystem/SharpenerComponent.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Content.Server.White.SharpeningSystem;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class SharpenerComponent : Component
|
||||
{
|
||||
//rn gonna support only slash damage
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("damageModifier")]
|
||||
public int DamageModifier;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("usages")]
|
||||
public int Usages = 1;
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class SharpenedComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int DamageModifier = 0;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int AttacksLeft = 50;
|
||||
}
|
||||
99
Content.Server/_White/SharpeningSystem/SharpeningSystem.cs
Normal file
99
Content.Server/_White/SharpeningSystem/SharpeningSystem.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.White.SharpeningSystem;
|
||||
|
||||
public sealed class SharpeningSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SharpenerComponent, AfterInteractEvent>(OnSharping);
|
||||
|
||||
SubscribeLocalEvent<SharpenedComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<SharpenedComponent, ComponentRemove>(OnSharpenedComponentRemove);
|
||||
}
|
||||
|
||||
private void OnSharping(EntityUid uid, SharpenerComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (!args.Target.HasValue)
|
||||
return;
|
||||
|
||||
var target = args.Target.Value;
|
||||
|
||||
if (!TryComp<ItemComponent>(target, out _))
|
||||
{
|
||||
_popupSystem.PopupEntity("Вы не можете заточить это", target, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<MeleeWeaponComponent>(target, out var meleeWeaponComponent))
|
||||
{
|
||||
_popupSystem.PopupEntity("Вы не можете заточить это", target, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!meleeWeaponComponent.Damage.DamageDict.ContainsKey("Slash"))
|
||||
{
|
||||
_popupSystem.PopupEntity("У оружия должно быть остреё", target, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasComp<SharpenedComponent>(target))
|
||||
{
|
||||
_popupSystem.PopupEntity("Клинок уже заточен", target, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureComp<SharpenedComponent>(target).DamageModifier = component.DamageModifier;
|
||||
|
||||
meleeWeaponComponent.Damage.ExclusiveAdd(
|
||||
new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"), component.DamageModifier));
|
||||
|
||||
component.Usages -= 1;
|
||||
|
||||
if (component.Usages <= 0)
|
||||
{
|
||||
Del(uid);
|
||||
}
|
||||
|
||||
_popupSystem.PopupEntity("Клинок успешно заточен", target, args.User);
|
||||
}
|
||||
|
||||
private void OnMeleeHit(EntityUid uid, SharpenedComponent component, MeleeHitEvent args)
|
||||
{
|
||||
component.AttacksLeft--;
|
||||
|
||||
if (component.AttacksLeft == 10)
|
||||
{
|
||||
_popupSystem.PopupEntity("Клинок начал затупляться", uid, args.User);
|
||||
}
|
||||
|
||||
if (component.AttacksLeft > 0)
|
||||
return;
|
||||
|
||||
_popupSystem.PopupEntity("Клинок потерял свою заточку", uid, args.User);
|
||||
RemComp(uid, component);
|
||||
}
|
||||
|
||||
private void OnSharpenedComponentRemove(EntityUid uid, SharpenedComponent component, ComponentRemove args)
|
||||
{
|
||||
if (!TryComp(uid, out MeleeWeaponComponent? meleeWeapon))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
meleeWeapon.Damage.ExclusiveAdd(
|
||||
new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"), -component.DamageModifier));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user