Tweaks: разные мелкие исправления и корректировки (#22)

* add: система улучшения зрения для слепых

* tweak: повышен урон дробовиков, повышен разброс

* tweak: скорость снарядов лазеров увеличена вдвое

* fix: фикс отображение веревки крюка-кошки

* fix: исправлено отображение воспоминаний

* tweak: перевод геймпресета революции

* fix: фикс отображения цели и рефактор правила культа

* add: Теперь помповые ружья нужно перезаряжать вручную

* tweak: повышен урон других снарядов дробовиков

* tweak: вещмешок синдиката больше не замедляет

* fix: исправлено отображение слота хранилища костюма в инвентаре
This commit is contained in:
Remuchi
2024-02-03 17:49:33 +07:00
committed by GitHub
parent 66e628e476
commit 3cfa1890b0
35 changed files with 432 additions and 424 deletions

View File

@@ -20,7 +20,7 @@ public sealed class CharacterInfoSystem : EntitySystem
public void RequestCharacterInfo()
{
var entity = _players.LocalPlayer?.ControlledEntity;
var entity = _players.LocalSession?.AttachedEntity;
if (entity == null)
{
return;

View File

@@ -1,12 +1,8 @@
using Content.Client.Movement.Systems;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Content.Shared.Eye.Blinding;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
namespace Content.Client.Eye.Blinding
{
@@ -18,7 +14,9 @@ namespace Content.Client.Eye.Blinding
[Dependency] private readonly ILightManager _lightManager = default!;
public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly ShaderInstance _greyscaleShader;
private readonly ShaderInstance _circleMaskShader;
@@ -30,6 +28,7 @@ namespace Content.Client.Eye.Blinding
_greyscaleShader = _prototypeManager.Index<ShaderPrototype>("GreyscaleFullscreen").InstanceUnique();
_circleMaskShader = _prototypeManager.Index<ShaderPrototype>("CircleMask").InstanceUnique();
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (!_entityManager.TryGetComponent(_playerManager.LocalSession?.AttachedEntity, out EyeComponent? eyeComp))
@@ -50,15 +49,13 @@ namespace Content.Client.Eye.Blinding
var blind = _blindableComponent.IsBlind;
if (!blind && _blindableComponent.LightSetup) // Turn FOV back on if we can see again
{
_lightManager.Enabled = true;
_blindableComponent.LightSetup = false;
_blindableComponent.GraceFrame = true;
return true;
}
if (blind || !_blindableComponent.LightSetup) // Turn FOV back on if we can see again
return blind;
return blind;
_lightManager.Enabled = true;
_blindableComponent.LightSetup = false;
_blindableComponent.GraceFrame = true;
return true;
}
protected override void Draw(in OverlayDrawArgs args)
@@ -75,7 +72,8 @@ namespace Content.Client.Eye.Blinding
{
_blindableComponent.LightSetup = true; // Ok we touched the lights
_lightManager.Enabled = false;
} else
}
else
{
_blindableComponent.GraceFrame = false;
}

View File

@@ -10,8 +10,7 @@ public sealed class BlindingSystem : EntitySystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] ILightManager _lightManager = default!;
[Dependency] private readonly ILightManager _lightManager = default!;
private BlindOverlay _overlay = default!;
@@ -27,7 +26,7 @@ public sealed class BlindingSystem : EntitySystem
SubscribeNetworkEvent<RoundRestartCleanupEvent>(RoundRestartCleanup);
_overlay = new();
_overlay = new BlindOverlay();
}
private void OnPlayerAttached(EntityUid uid, BlindableComponent component, LocalPlayerAttachedEvent args)
@@ -43,13 +42,13 @@ public sealed class BlindingSystem : EntitySystem
private void OnBlindInit(EntityUid uid, BlindableComponent component, ComponentInit args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
if (_player.LocalSession?.AttachedEntity == uid)
_overlayMan.AddOverlay(_overlay);
}
private void OnBlindShutdown(EntityUid uid, BlindableComponent component, ComponentShutdown args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
if (_player.LocalSession?.AttachedEntity == uid)
{
_overlayMan.RemoveOverlay(_overlay);
}

View File

@@ -21,7 +21,7 @@ public sealed class BlurryVisionSystem : EntitySystem
SubscribeLocalEvent<BlurryVisionComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<BlurryVisionComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
_overlay = new();
_overlay = new BlurryVisionOverlay();
}
private void OnPlayerAttached(EntityUid uid, BlurryVisionComponent component, LocalPlayerAttachedEvent args)
@@ -36,13 +36,13 @@ public sealed class BlurryVisionSystem : EntitySystem
private void OnBlurryInit(EntityUid uid, BlurryVisionComponent component, ComponentInit args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
if (_player.LocalSession?.AttachedEntity == uid)
_overlayMan.AddOverlay(_overlay);
}
private void OnBlurryShutdown(EntityUid uid, BlurryVisionComponent component, ComponentShutdown args)
{
if (_player.LocalPlayer?.ControlledEntity == uid)
if (_player.LocalSession?.AttachedEntity == uid)
{
_overlayMan.RemoveOverlay(_overlay);
}

View File

@@ -112,7 +112,6 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
_window.SubText.Text = job;
_window.Objectives.RemoveAllChildren();
_window.Memories.RemoveAllChildren();
_window.ObjectivesLabel.Visible = objectives.Any();
foreach (var (groupId, conditions) in objectives)
{
@@ -183,7 +182,8 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
_window.Objectives.AddChild(control);
}
_window.RolePlaceholder.Visible = briefing == null && !controls.Any() && !objectives.Any();
_window.RolePlaceholder.Visible = briefing == null && controls.Count == 0 && objectives.Count == 0;
_window.MemoriesPlaceholder.Visible = memories.Count == 0;
}
private void CharacterDetached(EntityUid uid)

View File

@@ -8,22 +8,25 @@
<ScrollContainer>
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<SpriteView OverrideDirection="South" Scale="2 2" Name="SpriteView" Access="Public" SetSize="64 64"/>
<SpriteView OverrideDirection="South" Scale="2 2" Name="SpriteView" Access="Public" SetSize="64 64" />
<BoxContainer Orientation="Vertical" VerticalAlignment="Top">
<Label Name="NameLabel" Access="Public"/>
<Label Name="SubText" VerticalAlignment="Top" StyleClasses="LabelSubText" Access="Public"/>
<Label Name="NameLabel" Access="Public" />
<Label Name="SubText" VerticalAlignment="Top" StyleClasses="LabelSubText" Access="Public" />
</BoxContainer>
</BoxContainer>
<!-- WD EDIT -->
<Label Text="{Loc 'character-info-memories-label'}" HorizontalAlignment="Center"/>
<BoxContainer Orientation="Vertical" Name="Memories" Access="Public"/>
<cc:Placeholder PlaceholderText="{Loc 'character-info-memories-placeholder-text'}"/>
<Label Text="{Loc 'character-info-memories-label'}" HorizontalAlignment="Center" />
<BoxContainer Orientation="Vertical" Name="Memories" Access="Public" />
<cc:Placeholder Name="MemoriesPlaceholder" Access="Public"
PlaceholderText="{Loc 'character-info-memories-placeholder-text'}" />
<!-- WD EDIT END -->
<Label Name="ObjectivesLabel" Access="Public" Text="{Loc 'character-info-objectives-label'}" HorizontalAlignment="Center"/>
<BoxContainer Orientation="Vertical" Name="Objectives" Access="Public"/>
<cc:Placeholder Name="RolePlaceholder" Access="Public" PlaceholderText="{Loc 'character-info-roles-antagonist-text'}"/>
<Label Name="ObjectivesLabel" Access="Public" Text="{Loc 'character-info-objectives-label'}"
HorizontalAlignment="Center" />
<BoxContainer Orientation="Vertical" Name="Objectives" Access="Public" />
<cc:Placeholder Name="RolePlaceholder" Access="Public"
PlaceholderText="{Loc 'character-info-roles-antagonist-text'}" />
</BoxContainer>
</ScrollContainer>
</windows:CharacterWindow>

View File

@@ -12,7 +12,10 @@ public sealed partial class GunSystem
SubscribeLocalEvent<BallisticAmmoProviderComponent, UpdateAmmoCounterEvent>(OnBallisticAmmoCount);
}
private void OnBallisticAmmoCount(EntityUid uid, BallisticAmmoProviderComponent component, UpdateAmmoCounterEvent args)
private void OnBallisticAmmoCount(
EntityUid uid,
BallisticAmmoProviderComponent component,
UpdateAmmoCounterEvent args)
{
if (args.Control is DefaultStatusControl control)
{
@@ -25,6 +28,9 @@ public sealed partial class GunSystem
if (!Timing.IsFirstTimePredicted)
return;
if (!component.IsCycled)
return;
EntityUid? ent = null;
// TODO: Combine with TakeAmmo
@@ -48,5 +54,6 @@ public sealed partial class GunSystem
var cycledEvent = new GunCycledEvent();
RaiseLocalEvent(uid, ref cycledEvent);
}
}

View File

@@ -172,14 +172,13 @@ public sealed partial class AdminVerbSystem
{
Text = "Сделать культистом.",
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new("/Textures/White/Cult/interface.rsi"), "icon"),
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/White/Cult/interface.rsi"), "icon"),
Act = () =>
{
if (!_minds.TryGetSession(targetMindComp.Mind, out var session))
return;
var playerSession = session;
_cultRule.MakeCultist(playerSession!);
_cultRule.MakeCultist(session);
}
};
args.Verbs.Add(cultist);

View File

@@ -1,8 +0,0 @@
using Content.Server.Objectives.Systems;
namespace Content.Server.Objectives.Components;
[RegisterComponent, Access(typeof(KillCultistTargetConditionSystem))]
public sealed partial class KillCultistTargetConditionComponent : Component
{
}

View File

@@ -0,0 +1,9 @@
using Content.Server.Objectives.Systems;
namespace Content.Server.Objectives.Components;
[RegisterComponent, Access(typeof(KillPersonConditionSystem))]
public sealed partial class PickCultTargetComponent : Component
{
}

View File

@@ -1,87 +0,0 @@
/*using System.Diagnostics;
using System.Linq;
using Content.Server.Mind;
using Content.Server._White.Cult.GameRule;
using Content.Shared.Mind;
using Content.Shared.Roles.Jobs;
using Robust.Shared.Utility;
namespace Content.Server.Objectives.Conditions;
[DataDefinition]
public sealed partial class KillCultistTarget : IObjectiveCondition
{
private IEntityManager EntityManager => IoCManager.Resolve<IEntityManager>();
protected EntityUid? TargetMindId;
protected MindComponent? TargetMind => EntityManager.GetComponentOrNull<MindComponent>(TargetMindId);
protected SharedJobSystem Jobs => EntityManager.System<SharedJobSystem>();
public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind)
{
var cultistRule = EntityManager.EntityQuery<CultRuleComponent>().FirstOrDefault();
Debug.Assert(cultistRule != null, nameof(cultistRule) + " != null");
var target = cultistRule.CultTarget;
return new KillCultistTarget()
{
TargetMindId = target
};
}
public string Title
{
get
{
var targetName = string.Empty;
var jobName = Jobs.MindTryGetJobName(TargetMindId) ?? "Unknown";
if (TargetMindId == null)
return Loc.GetString("objective-condition-kill-person-title", ("targetName", targetName), ("job", jobName));
if (TargetMind?.OwnedEntity is {Valid: true} owned)
targetName = EntityManager.GetComponent<MetaDataComponent>(owned).EntityName;
return Loc.GetString("objective-condition-kill-person-title", ("targetName", targetName), ("job", jobName));
}
}
public string Description => Loc.GetString("objective-condition-kill-person-description");
public SpriteSpecifier Icon => new SpriteSpecifier.Rsi(new ("Objects/Weapons/Guns/Pistols/viper.rsi"), "icon");
public float Progress
{
get
{
var entityManager = IoCManager.Resolve<EntityManager>();
var mindSystem = entityManager.System<MindSystem>();
return TargetMindId == null || TargetMind == null || mindSystem.IsCharacterDeadIc(TargetMind!) ? 1f : 0f;
}
}
public float Difficulty => 2f;
public bool Equals(IObjectiveCondition? other)
{
return other is KillCultistTarget kpc && Equals(TargetMindId, kpc.TargetMindId);
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
return obj.GetType() == GetType() && Equals((KillCultistTarget) obj);
}
public override int GetHashCode()
{
return TargetMindId?.GetHashCode() ?? 0;
}
}*/

View File

@@ -1,61 +0,0 @@
using System.Diagnostics;
using System.Linq;
using Content.Server.Objectives.Components;
using Content.Server._White.Cult.GameRule;
using Content.Shared.Mind;
using Content.Shared.Objectives.Components;
namespace Content.Server.Objectives.Systems;
public sealed class KillCultistTargetConditionSystem : EntitySystem
{
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly TargetObjectiveSystem _target = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<KillCultistTargetConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
SubscribeLocalEvent<KillCultistTargetConditionComponent, ObjectiveAssignedEvent>(OnPersonAssigned);
}
private void OnGetProgress(EntityUid uid, KillCultistTargetConditionComponent comp, ref ObjectiveGetProgressEvent args)
{
if (!_target.GetTarget(uid, out var target))
return;
args.Progress = GetProgress(target.Value);
}
private void OnPersonAssigned(EntityUid uid, KillCultistTargetConditionComponent component, ref ObjectiveAssignedEvent args)
{
if (!TryComp<TargetObjectiveComponent>(uid, out var target))
{
args.Cancelled = true;
return;
}
// target already assigned
if (target.Target != null)
return;
var cultistRule = EntityManager.EntityQuery<CultRuleComponent>().FirstOrDefault();
Debug.Assert(cultistRule != null, nameof(cultistRule) + " != null");
var cultTarget = cultistRule.CultTarget;
if (cultTarget != null)
_target.SetTarget(uid, cultTarget.Value, target);
}
private float GetProgress(EntityUid target)
{
// deleted or gibbed or something, counts as dead
if (!TryComp<MindComponent>(target, out var mind) || mind.OwnedEntity == null)
return 1f;
// dead is success
return _mind.IsCharacterDeadIc(mind) ? 1f : 0f;
}
}

View File

@@ -1,3 +1,5 @@
using System.Linq;
using Content.Server._White.Cult.GameRule;
using Content.Server.Objectives.Components;
using Content.Server.Shuttles.Systems;
using Content.Shared.CCVar;
@@ -30,6 +32,8 @@ public sealed class KillPersonConditionSystem : EntitySystem
SubscribeLocalEvent<PickRandomPersonComponent, ObjectiveAssignedEvent>(OnPersonAssigned);
SubscribeLocalEvent<PickRandomHeadComponent, ObjectiveAssignedEvent>(OnHeadAssigned);
SubscribeLocalEvent<PickCultTargetComponent, ObjectiveAssignedEvent>(OnCultTargetAssigned);
}
private void OnGetProgress(EntityUid uid, KillPersonConditionComponent comp, ref ObjectiveGetProgressEvent args)
@@ -99,6 +103,28 @@ public sealed class KillPersonConditionSystem : EntitySystem
_target.SetTarget(uid, _random.Pick(allHeads), target);
}
private void OnCultTargetAssigned(Entity<PickCultTargetComponent> ent, ref ObjectiveAssignedEvent args)
{
// invalid prototype
if (!TryComp<TargetObjectiveComponent>(ent.Owner, out var target))
{
args.Cancelled = true;
return;
}
// target already assigned
if (target.Target != null)
return;
var cultistRule = EntityManager.EntityQuery<CultRuleComponent>().FirstOrDefault();
if (cultistRule?.CultTarget is null)
{
return;
}
_target.SetTarget(ent.Owner, cultistRule.CultTarget.Value);
}
private float GetProgress(EntityUid target, bool requireDead)
{
// deleted or gibbed or something, counts as dead

View File

@@ -12,6 +12,9 @@ public sealed partial class GunSystem
protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates)
{
if (!component.IsCycled)
return;
EntityUid? ent = null;
// TODO: Combine with TakeAmmo

View File

@@ -54,11 +54,11 @@ public sealed partial class CultRuleComponent : Component
/// <summary>
/// Players who played as an cultist at some point in the round.
/// </summary>
public Dictionary<string, string> CultistsList = new();
public Dictionary<string, string> CultistsCache = new();
public EntityUid? CultTarget;
public List<CultistComponent> Cultists = new();
public List<CultistComponent> CurrentCultists = new();
public List<ConstructComponent> Constructs = new();

View File

@@ -2,9 +2,7 @@
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;
@@ -16,23 +14,15 @@ 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._White.Cult.Components;
using Content.Shared.Mind;
using Robust.Shared.Audio.Systems;
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent;
namespace Content.Server._White.Cult.GameRule;
@@ -43,7 +33,6 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
[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!;
@@ -86,147 +75,127 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
public MindComponent? GetTarget()
{
var querry = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
var cultistsRule = EntityQuery<CultRuleComponent>().FirstOrDefault();
while (querry.MoveNext(out _, out var cultRuleComponent, out _))
if (cultistsRule?.CultTarget == null || !TryComp<MindComponent>(cultistsRule.CultTarget.Value, out var mind))
{
if (cultRuleComponent.CultTarget.HasValue && TryComp<MindComponent>(cultRuleComponent.CultTarget.Value, out var mind))
{
return mind;
}
return null;
}
return null!;
return mind;
}
public bool CanSummonNarsie()
{
var querry = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
while (querry.MoveNext(out _, out var cultRuleComponent, out _))
var cultistsRule = EntityQuery<CultRuleComponent>().FirstOrDefault();
if (cultistsRule is null)
{
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;
}
return false;
var cultistsAmount = cultistsRule.CurrentCultists.Count;
var constructsAmount = cultistsRule.Constructs.Count;
var enoughCultists = cultistsAmount + constructsAmount > 10;
if (!enoughCultists)
{
return false;
}
var target = GetTarget();
var targetKilled = target == null || _mindSystem.IsCharacterDeadIc(target);
return targetKilled;
}
private void CheckRoundShouldEnd()
{
var querry = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
var aliveCultistsCount = 0;
while (querry.MoveNext(out _, out var cultRuleComponent, out _))
var cultistsRule = EntityQuery<CultRuleComponent>().FirstOrDefault();
if (cultistsRule is null)
{
var cultists = 0;
foreach (var cultistComponent in cultRuleComponent.Cultists)
return;
}
var aliveCultists = 0;
foreach (var cultistComponent in cultistsRule.CurrentCultists)
{
var owner = cultistComponent.Owner;
if (!TryComp<MobStateComponent>(owner, out var mobState))
continue;
if (_mobStateSystem.IsAlive(owner, mobState))
{
var owner = cultistComponent.Owner;
if (!TryComp<MobStateComponent>(owner, out var mobState))
continue;
if (_mobStateSystem.IsAlive(owner, mobState))
{
cultists++;
}
aliveCultists++;
}
if (cultists == 0)
cultRuleComponent.WinCondition = CultWinCondition.CultFailure;
aliveCultistsCount += cultists;
}
if (aliveCultistsCount == 0)
{
_roundEndSystem.EndRound();
}
if (aliveCultists != 0)
return;
cultistsRule.WinCondition = CultWinCondition.CultFailure;
_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 _))
var cultistsRule = EntityQuery<CultRuleComponent>().FirstOrDefault();
if (cultistsRule is null)
{
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);
return;
}
if (!TryComp<MindContainerComponent>(uid, out var mindComponent))
return;
if (!mindComponent.HasMind)
return;
cultistsRule.CurrentCultists.Add(component);
if (TryComp<ActorComponent>(uid, out var actor))
{
cultistsRule.CultistsCache.Add(MetaData(uid).EntityName, actor.PlayerSession.Name);
}
UpdateCultistsAppearance(cultistsRule);
}
private void OnCultistComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args)
{
var query = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
while (query.MoveNext(out var ruleEnt, out var cultRuleComponent, out _))
var cultistsRule = EntityQuery<CultRuleComponent>().FirstOrDefault();
if (cultistsRule is null)
{
if (!GameTicker.IsGameRuleAdded(ruleEnt))
continue;
cultRuleComponent.Cultists.Remove(component);
RemoveCultistAppearance(component);
CheckRoundShouldEnd();
return;
}
cultistsRule.CurrentCultists.Remove(component);
RemoveCultistAppearance(uid);
CheckRoundShouldEnd();
}
private void RemoveCultistAppearance(CultistComponent component)
private void RemoveCultistAppearance(EntityUid cultist)
{
if (TryComp<HumanoidAppearanceComponent>(component.Owner, out var appearanceComponent))
if (TryComp<HumanoidAppearanceComponent>(cultist, out var appearanceComponent))
{
//Потому что я так сказал
appearanceComponent.EyeColor = Color.White;
Dirty(appearanceComponent);
Dirty(cultist, appearanceComponent);
}
RemComp<PentagramComponent>(component.Owner);
RemComp<PentagramComponent>(cultist);
}
private void UpdateCultistsAppearance(CultRuleComponent cultRuleComponent)
{
var cultistsCount = cultRuleComponent.Cultists.Count;
var cultistsCount = cultRuleComponent.CurrentCultists.Count;
var constructsCount = cultRuleComponent.Constructs.Count;
var totalCultMembers = cultistsCount + constructsCount;
if (totalCultMembers < CultRuleComponent.ReadEyeThreshold)
return;
foreach (var cultistComponent in cultRuleComponent.Cultists)
foreach (var cultistComponent in cultRuleComponent.CurrentCultists)
{
if (TryComp<HumanoidAppearanceComponent>(cultistComponent.Owner, out var appearanceComponent))
{
@@ -243,76 +212,74 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
private void OnRoundEndText(RoundEndTextAppendEvent ev)
{
var querry = EntityQuery<CultRuleComponent>();
foreach (var cultRuleComponent in querry)
var cultistsRule = EntityQuery<CultRuleComponent>().FirstOrDefault();
if (cultistsRule is null)
{
var winText = Loc.GetString($"cult-cond-{cultRuleComponent.WinCondition.ToString().ToLower()}");
ev.AddLine(winText);
return;
}
ev.AddLine(Loc.GetString("cultists-list-start"));
var winText = Loc.GetString($"cult-cond-{cultistsRule.WinCondition.ToString().ToLower()}");
ev.AddLine(winText);
foreach (var (entityName, ckey) in cultRuleComponent.CultistsList)
{
var lising = Loc.GetString("cultists-list-name", ("name", entityName), ("user", ckey));
ev.AddLine(lising);
}
ev.AddLine(Loc.GetString("cultists-list-start"));
foreach (var (entityName, ckey) in cultistsRule.CultistsCache)
{
var lising = Loc.GetString("cultists-list-name", ("name", entityName), ("user", ckey));
ev.AddLine(lising);
}
}
private void OnStartAttempt(RoundStartAttemptEvent ev)
{
var query = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
while (query.MoveNext(out var uid, out _, out var gameRule))
var cultistsRule = EntityQuery<CultRuleComponent>().FirstOrDefault();
if (cultistsRule is null)
{
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();
}
return;
}
var minPlayers = _cultGameRuleMinimapPlayers;
if (!ev.Forced && ev.Players.Length < minPlayers)
{
_chatManager.DispatchServerAnnouncement(Loc.GetString("traitor-not-enough-ready-players",
("readyPlayersCount", ev.Players.Length), ("minimumPlayers", minPlayers)));
ev.Cancel();
return;
}
if (ev.Players.Length != 0)
return;
_chatManager.DispatchServerAnnouncement(Loc.GetString("traitor-no-one-ready"));
ev.Cancel();
}
private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev)
{
var query = EntityQueryEnumerator<CultRuleComponent, GameRuleComponent>();
while (query.MoveNext(out var uid, out var cultRule, out var gameRule))
var cultistsRule = EntityQuery<CultRuleComponent>().FirstOrDefault();
if (cultistsRule is null)
{
if (!GameTicker.IsGameRuleAdded(uid, gameRule))
return;
}
foreach (var player in ev.Players)
{
if (!ev.Profiles.ContainsKey(player.UserId))
continue;
foreach (var player in ev.Players)
{
if (!ev.Profiles.ContainsKey(player.UserId))
continue;
cultistsRule.StarCandidates[player] = ev.Profiles[player.UserId];
}
cultRule.StarCandidates[player] = ev.Profiles[player.UserId];
}
var potentialCultists = FindPotentialCultist(cultistsRule.StarCandidates);
var pickedCultist = PickCultists(potentialCultists);
var potentialTargets = FindPotentialTargets(pickedCultist);
var potentialCultists = FindPotentialCultist(cultRule.StarCandidates);
var pickedCultist = PickCultists(potentialCultists);
var potentialTargets = FindPotentialTargets(pickedCultist);
cultistsRule.CultTarget = _random.PickAndTake(potentialTargets).Mind;
cultRule.CultTarget = _random.PickAndTake(potentialTargets).Mind;
foreach (var pickerCultist in pickedCultist)
{
MakeCultist(pickerCultist);
}
foreach (var pickerCultist in pickedCultist)
{
MakeCultist(pickerCultist);
}
}
@@ -340,7 +307,8 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
return potentialTargets;
}
private List<ICommonSession> FindPotentialCultist(in Dictionary<ICommonSession, HumanoidCharacterProfile> candidates)
private List<ICommonSession> FindPotentialCultist(
in Dictionary<ICommonSession, HumanoidCharacterProfile> candidates)
{
var list = new List<ICommonSession>();
var pendingQuery = GetEntityQuery<PendingClockInComponent>();
@@ -348,7 +316,8 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
foreach (var player in candidates.Keys)
{
// Role prevents antag.
if (!_jobSystem.CanBeAntag(player)) continue;
if (!_jobSystem.CanBeAntag(player))
continue;
// Latejoin
if (player.AttachedEntity != null && pendingQuery.HasComponent(player.AttachedEntity.Value))
@@ -428,35 +397,34 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
cultistRule = Comp<CultRuleComponent>(ruleEntity);
}
var mind = cultist.Data.ContentData()?.Mind;
if (mind == null)
if (!_mindSystem.TryGetMind(cultist, out var mindId, out var mind))
{
_sawmill.Info("Failed getting mind for picked cultist.");
Log.Info("Failed getting mind for picked thief.");
return false;
}
var playerEntity = cultist.AttachedEntity;
if (!playerEntity.HasValue)
if (mind.OwnedEntity is not { } playerEntity)
{
_sawmill.Error("Mind picked for cultist did not have an attached entity.");
Log.Error("Mind picked for cultist did not have an attached entity.");
return false;
}
var mindComponent = Comp<MindComponent>(mind.Value);
var cultistComponent = new CultistRoleComponent
{
PrototypeId = cultistRule.CultistRolePrototype
};
DebugTools.AssertNotNull(playerEntity.Value);
EnsureComp<CultistComponent>(playerEntity.Value);
_roleSystem.MindAddRole(mindId, cultistComponent);
EnsureComp<CultistComponent>(playerEntity);
_factionSystem.RemoveFaction(playerEntity.Value, "NanoTrasen", false);
_factionSystem.AddFaction(playerEntity.Value, "Cultist");
_factionSystem.RemoveFaction(playerEntity, "NanoTrasen", false);
_factionSystem.AddFaction(playerEntity, "Cultist");
if (_inventorySystem.TryGetSlotEntity(playerEntity.Value, "back", out var backPack))
if (_inventorySystem.TryGetSlotEntity(playerEntity, "back", out var backPack))
{
foreach (var itemPrototype in cultistRule.StartingItems)
{
var itemEntity = Spawn(itemPrototype, Transform(playerEntity.Value).Coordinates);
var itemEntity = Spawn(itemPrototype, Transform(playerEntity).Coordinates);
if (backPack != null)
{
@@ -465,12 +433,14 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
}
}
_audioSystem.PlayGlobal(cultistRule.GreatingsSound, Filter.Empty().AddPlayer(cultist), false,
AudioParams.Default);
// Notificate player about new role assignment
if (_mindSystem.TryGetSession(mindId, out var session))
{
_audioSystem.PlayGlobal(cultistRule.GreatingsSound, session);
_chatManager.DispatchServerMessage(session, Loc.GetString("cult-role-greeting"));
}
_chatManager.DispatchServerMessage(cultist, Loc.GetString("cult-role-greeting"));
_mindSystem.TryAddObjective(mind.Value, mindComponent, "CultistKillObjective");
_mindSystem.TryAddObjective(mindId, mind, "KillCultTargetObjective");
return true;
}
@@ -484,19 +454,19 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
_roundEndSystem.EndRound();
var query = EntityQuery<MobStateComponent, MindContainerComponent, CultistComponent>().ToList();
var query = EntityQueryEnumerator<MobStateComponent, MindContainerComponent, CultistComponent>();
foreach (var (mobState, mindContainer, _) in query)
while (query.MoveNext(out var uid, out _, out var mindContainer, out _))
{
if (!mindContainer.HasMind || mindContainer.Mind is null)
{
continue;
}
var reaper = Spawn(CultRuleComponent.ReaperPrototype, Transform(mobState.Owner).Coordinates);
var reaper = Spawn(CultRuleComponent.ReaperPrototype, Transform(uid).Coordinates);
_mindSystem.TransferTo(mindContainer.Mind.Value, reaper);
_bodySystem.GibBody(mobState.Owner);
_bodySystem.GibBody(uid);
}
}
}

View File

@@ -67,6 +67,9 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
visuals.Sprite = component.RopeSprite;
visuals.OffsetA = new Vector2(0f, 0.5f);
visuals.Target = uid;
component.Joint = visuals.Target;
Dirty(shotUid.Value, visuals);
}
@@ -91,7 +94,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
if (msg.Reeling &&
(!TryComp<CombatModeComponent>(player, out var combatMode) ||
!combatMode.IsInCombatMode))
!combatMode.IsInCombatMode))
{
return;
}
@@ -132,7 +135,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
component.Projectile = null;
SetReeling(uid, component, false, args.User);
_gun.ChangeBasicEntityAmmoCount(uid, 1);
_gun.ChangeBasicEntityAmmoCount(uid, 1);
args.Handled = true;
}

View File

@@ -44,6 +44,24 @@ public sealed partial class BallisticAmmoProviderComponent : Component
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
public bool Cycleable = true;
/// <summary>
/// Is the firearm currently cycled?
/// It cannot fire if is it not cycled.
/// Must be manually cycled if it is not cycled.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField]
[AutoNetworkedField]
public bool? Cycled = true;
public bool IsCycled => Cycled is true or null;
/// <summary>
/// Automatically cycles the firearm after firing a round
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField]
[AutoNetworkedField]
public bool AutoCycle = true;
/// <summary>
/// Is it okay for this entity to directly transfer its valid ammunition into another provider?
/// </summary>

View File

@@ -9,7 +9,7 @@ namespace Content.Shared.Weapons.Ranged.Components;
public sealed partial class GrapplingGunComponent : Component
{
[DataField("jointId"), AutoNetworkedField]
public string Joint = string.Empty;
public EntityUid? Joint;
[DataField, AutoNetworkedField]
public EntityUid? Projectile;

View File

@@ -206,6 +206,7 @@ public abstract partial class SharedGunSystem
var shots = GetBallisticShots(component);
Cycle(uid, component, coordinates);
component.Cycled = true;
var text = Loc.GetString(shots == 0 ? "gun-ballistic-cycled-empty" : "gun-ballistic-cycled");
@@ -243,6 +244,9 @@ public abstract partial class SharedGunSystem
private void OnBallisticTakeAmmo(EntityUid uid, BallisticAmmoProviderComponent component, TakeAmmoEvent args)
{
if (!component.IsCycled)
return;
for (var i = 0; i < args.Shots; i++)
{
EntityUid entity;
@@ -263,6 +267,10 @@ public abstract partial class SharedGunSystem
}
}
//un-cycle the firearm
if (!component.AutoCycle)
component.Cycled = false;
UpdateBallisticAppearance(uid, component);
Dirty(uid, component);
}

View File

@@ -0,0 +1,9 @@
using Content.Shared.Roles;
namespace Content.Shared._White.Cult.Components;
[RegisterComponent]
public sealed partial class CultistRoleComponent : AntagonistRoleComponent
{
}

View File

@@ -0,0 +1,8 @@
namespace Content.Shared._White.ReduceBlindness;
[RegisterComponent, AutoGenerateComponentState]
public sealed partial class ReduceBlindnessComponent : Component
{
[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
public float ReduceAmount { get; set; } = 1.5f;
}

View File

@@ -0,0 +1,35 @@
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Hands;
namespace Content.Shared._White.ReduceBlindness;
public sealed class ReduceBlindnessSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ReduceBlindnessComponent, GotEquippedHandEvent>(OnEquipepd);
SubscribeLocalEvent<ReduceBlindnessComponent, GotUnequippedHandEvent>(OnUnequipped);
}
private void OnEquipepd(Entity<ReduceBlindnessComponent> ent, ref GotEquippedHandEvent args)
{
if (!TryComp(args.User, out BlurryVisionComponent? blurryVisionComponent))
{
return;
}
blurryVisionComponent.Magnitude -= ent.Comp.ReduceAmount;
}
private void OnUnequipped(Entity<ReduceBlindnessComponent> ent, ref GotUnequippedHandEvent args)
{
if (!TryComp(args.User, out BlurryVisionComponent? blurryVisionComponent))
{
return;
}
blurryVisionComponent.Magnitude += ent.Comp.ReduceAmount;
}
}

View File

@@ -1,24 +1,75 @@
rev-title = Революция
rev-name = Глава революции
rev-description = Группа бунтовщиков тайком пробралась на станцию для того чтобы убить всех глав и захватить её. Они вербуют новых сторонников при помощи устройства-вспышки. Постарайтесь выследить и уничтожить их!
rev-welcome-headrev =
Вы - главный революционер. Ваша задача - завербовать как можно больше членов экипажа в свои ряды и захватить станцию, убив глав всех отделов и капитана.
В вашем кармане находится вспышка. Используйте её на другом члене экипажа для того чтобы путем гипноза передать ему свои революционные идеалы.
Работайте сообща с другими зачинщиками мятежа и постарайтесь не попасться Службе Безопасности.
Да здравствует Революция! За старый космос
rev-welcome-rev =
В свете вспышки вы увидели объятую пламенем штаб-квартиру НаноТрейзен, и вам понравилось это зрелище. Хотите вы того или нет, но теперь вы - революционер.
Вы загипнотизированы и обязаны слепо подчиняться приказам главных революционеров. Ваши новые идеалы запрещают вам мешать ходу революции, сдаваться Службе Безопасности или сдавать ей своих товарищей. Ваша задача - захватить станцию, убив глав всех отделов и капитана.
Да здравствует Революция! За старый космос!
rev-revmajor = Крупная победа Революции!
rev-neutral = Ничейный исход!
rev-crewmajor = Крупная победа экипажа!
rev-cond-allheadrevsdead = Все лидеры революции погибли.
rev-cond-allcrewheadsdead = Все главы станции погибли.
rev-list-revs-start = Революционерами были:
rev-list = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) { $headrev }
rev-list-heads-start = Главами станции были:
rev-not-enough-ready-players = Недостаточно игроков готовы к игре! { $readyPlayersCount } игроков из необходимых { $minimumPlayers } готовы. Нельзя начать режим: Revolution.
rev-no-one-ready = Нет готовых игроков! Нельзя начать Revolution.
rev-list-headrevbool = [color=Red]Глава революции[/color]
rev-objective = Убить глав всех отделов и капитана!
## Rev Head
roles-antag-rev-head-name = Глава Революции
roles-antag-rev-head-objective = Группа бунтовщиков тайком пробралась на станцию для того чтобы убить всех глав и захватить её. Они вербуют новых сторонников при помощи устройства-вспышки. Постарайтесь выследить и уничтожить их!
head-rev-role-greeting =
Вы - Глава Революции.
Вашей целью является убрать весь коммандный состав станции - убив их или изгнав.
Синдикат снабдил вас вспышками, которые могут быть использованы для вербовки персонала станции на свою сторону.
Однако, это не сработает на Службу Безопасности, Командный состав или на тех, кто носит защиту от вспышек.
Да здравствует Революция! За старый космос
head-rev-briefing =
Используйте вспышку чтобы вербовать людей на свою сторону.
Убейте всех глав, чтобы захватить станцию.
head-rev-break-mindshield = Имплант защиты разума сломался!
## Rev
roles-antag-rev-name = Революционер
roles-antag-rev-objective = Ваша задача - обеспечить безопасность и выполнять приказы Глав Революции, а также убить весь командный состав станции.
rev-break-control = {$name} вспомнили о своей истинной преданности!
rev-role-greeting =
Вы - революционер.
Вам поручено захватить станцию и защитить Глав Революции.
Уничтожьте весь командный состав.
Да здравствует революция!
rev-briefing = Помогите Главам Революции убить весь командный состав, чтобы захватить станцию.
## General
rev-title = Революционеры
rev-description = Революционеры среди нас.
rev-not-enough-ready-players = Недостаточно игроков чтобы запустить режим. Готово {$readyPlayersCount} игроков из {$minimumPlayers} необходимых. Невозможно запустить режим Революция.
rev-no-one-ready = Нет готовых игроков! Невозможно запустить режим Революция.
rev-no-heads = Не удалось выбрать Глав Революции. Невозможно запустить режим Революция.
rev-all-heads-dead = Все главы мертвы, теперь прикончите остальных членов экипажа!
rev-won = Главы Революции выжили и убили весь командный состав.
rev-lost = Командный состав выжил и убили всех Глав Революции.
rev-stalemate = И Глав Революции, и командный состав погибли. Ничья.
rev-reverse-stalemate = Обе команды выжили. Ничья.
rev-headrev-count = {$initialCount ->
[one] Был один Глава Революции:
*[other] Было {$initialCount} Глав Революции:
}
rev-headrev-name-user = [color=#5e9cff]{$name}[/color] ([color=gray]{$username}[/color]) завербовал {$count} {$count ->
[one] человека
*[other] человек
}
rev-headrev-name = [color=#5e9cff]{$name}[/color] завербовал {$count} {$count ->
[one] человека
*[other] человек
}
## Deconverted window
rev-deconverted-title = Деконвертирован!
rev-deconverted-text =
Поскольку последний Глава Революции умер, революция закончилась.
Вы больше не революционер, так что будьте спокойны.
rev-deconverted-confirm = Принять

View File

@@ -169,6 +169,9 @@
sprite: Clothing/Back/Duffels/syndicate.rsi
- type: ExplosionResistance
damageCoefficient: 0.1
- type: ClothingSpeedModifier
walkModifier: 1
sprintModifier: 1
- type: entity
parent: ClothingBackpackDuffelSyndicate

View File

@@ -10,7 +10,7 @@
- ShellShotgun
- type: CartridgeAmmo
count: 6
spread: 15
spread: 22
soundEject:
collection: ShellEject
- type: Sprite

View File

@@ -10,7 +10,7 @@
- type: Projectile
damage:
types:
Piercing: 28
Piercing: 34
- type: entity
id: PelletShotgunBeanbag
@@ -40,7 +40,7 @@
- type: Projectile
damage:
types:
Piercing: 10
Piercing: 13
- type: entity
id: PelletShotgunIncendiary
@@ -54,7 +54,7 @@
- type: Projectile
damage:
types:
Blunt: 3
Blunt: 6
Heat: 7
- type: IgnitionSource
ignited: true
@@ -85,8 +85,8 @@
- type: Projectile
damage:
types:
Piercing: 3
Slash: 3 #remember, it's metal shrapnel!
Piercing: 5
Slash: 5 #remember, it's metal shrapnel!
- type: entity
id: PelletShotgunTranquilizer
@@ -177,8 +177,8 @@
- type: Projectile
damage:
types:
Radiation: 5
Piercing: 5
Radiation: 6
Piercing: 7
- type: entity
id: PelletGrapeshot #tally fucking ho

View File

@@ -15,6 +15,7 @@
- type: AmmoCounter
- type: Gun
fireRate: 2
projectileSpeed: 48
selectedMode: SemiAuto
availableModes:
- SemiAuto

View File

@@ -49,6 +49,7 @@
proto: GrenadeFrag
soundInsert:
path: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg
autoCycle: false
- type: entity
name: RPG-7

View File

@@ -36,6 +36,7 @@
proto: ShellShotgun
soundInsert:
path: /Audio/Weapons/Guns/MagIn/shotgun_insert.ogg
autoCycle: false
- type: ContainerContainer
containers:
ballistic-ammo: !type:Container

View File

@@ -30,6 +30,7 @@
whitelist:
tags:
- CartridgeLightRifle
autoCycle: false
- type: ContainerContainer
containers:
ballistic-ammo: !type:Container
@@ -63,6 +64,7 @@
- CartridgeAntiMateriel
capacity: 5
proto: CartridgeAntiMateriel
autoCycle: true
- type: Wieldable
wieldTime: 0
forceTwoHanded: True

View File

@@ -18,6 +18,7 @@
- type: StaminaDamageOnHit
damage: 5
- type: Wieldable
- type: ReduceBlindness
- type: IncreaseDamageOnWield
damage:
types:

View File

@@ -113,7 +113,7 @@
- name: suitstorage
slotTexture: suit_storage
slotFlags: SUITSTORAGE
slotGroup: MainHotbar
slotGroup: SecondHotbar
stripTime: 3
uiWindowPos: 2,0
strippingWindowPos: 2,5

View File

@@ -1,13 +0,0 @@
- type: entity
noSpawn: true
parent: BaseObjective
id: CultistKillObjective
description: objective-condition-kill-person-description
components:
- type: Objective
difficulty: 1.5
issuer: Cult
icon:
sprite: White/Cult/interface.rsi
state: icon
- type: KillCultistTargetCondition

View File

@@ -0,0 +1,22 @@
- type: entity
noSpawn: true
parent: BaseTargetObjective
id: KillCultTargetObjective
description: This fool person should be sacrificed in the glory of our God.
components:
- type: Objective
issuer: cult
unique: true
difficulty: 3
icon:
sprite: Objects/Weapons/Melee/cult_dagger.rsi
state: icon
- type: RoleRequirement
roles:
components:
- CultistRole
- type: TargetObjective
title: objective-condition-kill-person-title
- type: PickCultTarget
- type: KillPersonCondition
requireDead: true