Merge remote-tracking branch 'upstream/master' into ups

This commit is contained in:
Jabak
2024-07-29 12:58:16 +03:00
167 changed files with 2923 additions and 468 deletions

View File

@@ -50,6 +50,8 @@ public sealed partial class CultRuleComponent : Component
public List<ConstructComponent> Constructs = new();
public CultWinCondition WinCondition = CultWinCondition.Draw;
public CultStage Stage = CultStage.Normal;
}
public enum CultWinCondition : byte
@@ -59,4 +61,11 @@ public enum CultWinCondition : byte
Failure,
}
public enum CultStage : byte
{
Normal,
RedEyes,
Pentagram,
}
public sealed class CultNarsieSummoned : EntityEventArgs;

View File

@@ -6,6 +6,7 @@ using Content.Server.Bible.Components;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Hands.Systems;
using Content.Server.Objectives.Components;
using Content.Server.Roles;
using Content.Server.RoundEnd;
@@ -53,6 +54,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
[Dependency] private readonly GulagSystem _gulag = default!;
[Dependency] private readonly BloodSpearSystem _bloodSpear = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly HandsSystem _hands = default!;
private const int PlayerPerCultist = 10;
private int _minStartingCultists;
@@ -175,6 +177,8 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
if (TryComp<ActorComponent>(uid, out var actor))
{
cult.CultistsCache.TryAdd(name, actor.PlayerSession.Name);
_mindSystem.TryGetMind(actor.PlayerSession.UserId, out var mind);
component.OriginalMind = mind;
}
UpdateCultistsAppearance(cult);
@@ -339,7 +343,12 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
var cultistsCount = cultRuleComponent.CurrentCultists.Count;
var constructsCount = cultRuleComponent.Constructs.Count;
var totalCultMembers = cultistsCount + constructsCount;
if (totalCultMembers < cultRuleComponent.ReadEyeThreshold)
if (totalCultMembers >= cultRuleComponent.PentagramThreshold)
cultRuleComponent.Stage = CultStage.Pentagram;
else if (totalCultMembers >= cultRuleComponent.ReadEyeThreshold && cultRuleComponent.Stage == CultStage.Normal)
cultRuleComponent.Stage = CultStage.RedEyes;
if (cultRuleComponent.Stage == CultStage.Normal)
return;
foreach (var cultistComponent in cultRuleComponent.CurrentCultists)
@@ -350,7 +359,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
Dirty(cultistComponent.Owner, appearanceComponent);
}
if (totalCultMembers < cultRuleComponent.PentagramThreshold)
if (cultRuleComponent.Stage != CultStage.Pentagram)
return;
EnsureComp<PentagramComponent>(cultistComponent.Owner);
@@ -458,6 +467,13 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
_container.Remove(container.ContainedEntity.Value, container, true, true);
}
}
foreach (var item in _hands.EnumerateHeld(uid))
{
if (TryComp(item, out CultItemComponent? cultItem) && !cultItem.CanPickUp &&
!_hands.TryDrop(uid, item, null, false, false))
QueueDel(item);
}
}
public void TransferRole(EntityUid transferFrom, EntityUid transferTo)

View File

@@ -0,0 +1,20 @@
namespace Content.Server._White.Cult.Items.Components;
[RegisterComponent]
public sealed partial class CultStunHandComponent : Component
{
[DataField]
public TimeSpan Duration = TimeSpan.FromSeconds(16);
[DataField]
public TimeSpan HaloDuration = TimeSpan.FromSeconds(1.5);
[DataField]
public TimeSpan MuteDuration = TimeSpan.FromSeconds(12);
[DataField]
public TimeSpan HaloMuteDuration = TimeSpan.FromSeconds(1);
[DataField]
public string Speech = "Fuu ma'jin!";
}

View File

@@ -0,0 +1,56 @@
using Content.Server._White.Cult.Items.Components;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chat.Systems;
using Content.Server.Popups;
using Content.Server.Stunnable;
using Content.Shared._White.Chaplain;
using Content.Shared.Popups;
using Content.Shared.StatusEffect;
using Content.Shared.Weapons.Melee.Events;
namespace Content.Server._White.Cult.Items.Systems;
public sealed class CultStunHandSystem : EntitySystem
{
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly HolyWeaponSystem _holyWeapon = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CultStunHandComponent, MeleeHitEvent>(OnHit);
}
private void OnHit(Entity<CultStunHandComponent> ent, ref MeleeHitEvent args)
{
if (args.HitEntities.Count == 0)
return;
var target = args.HitEntities[0];
var (uid, comp) = ent;
QueueDel(uid);
Spawn("CultStunFlashEffect", Transform(target).Coordinates);
_chat.TrySendInGameICMessage(args.User, comp.Speech, InGameICChatType.Whisper, false);
if (TryComp(args.User, out BloodstreamComponent? bloodstream))
_bloodstream.TryModifyBloodLevel(args.User, -10, bloodstream, createPuddle: false);
if (_holyWeapon.IsHoldingHolyWeapon(target))
{
_popupSystem.PopupEntity(Loc.GetString("cult-magic-holy"), args.User, args.User, PopupType.MediumCaution);
return;
}
var halo = HasComp<PentagramComponent>(args.User);
_statusEffects.TryAddStatusEffect(target, "Muted", halo ? comp.HaloMuteDuration : comp.MuteDuration, true,
"Muted");
_stun.TryParalyze(target, halo ? comp.HaloDuration : comp.Duration, true);
}
}

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Numerics;
using Content.Server.Atmos.Piping.Other.Components;
using Content.Server.Body.Components;
@@ -61,6 +61,15 @@ public sealed class PylonSystem : EntitySystem
private void OnInit(EntityUid uid, SharedPylonComponent component, ComponentInit args)
{
var coords = Transform(uid).Coordinates;
if (SharedPylonComponent.CheckForStructure(coords, EntityManager, 9f, uid))
{
QueueDel(uid);
_popupSystem.PopupCoordinates(Loc.GetString("cult-structure-craft-another-structure-nearby"),
coords, PopupType.MediumCaution);
Spawn("CultRunicMetal4", coords);
return;
}
UpdateAppearance(uid, component);
}

View File

@@ -1,3 +1,5 @@
using Content.Server.Chat.Systems;
namespace Content.Server._White.Cult.Runes.Comps;
[RegisterComponent]
@@ -14,4 +16,7 @@ public sealed partial class CultRuneBaseComponent : Component
[ViewVariables(VVAccess.ReadWrite), DataField("invokePhrase")]
public string InvokePhrase = "";
[DataField]
public InGameICChatType InvokeChatType = InGameICChatType.Whisper;
}

View File

@@ -5,6 +5,8 @@ using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Emp;
using Content.Server.EUI;
using Content.Server._White.Cult.UI;
using Content.Server._White.Wizard.Magic;
using Content.Server.Chat.Systems;
using Content.Shared._White.Chaplain;
using Content.Shared.Chemistry.Components;
using Content.Shared.Damage;
@@ -50,6 +52,7 @@ public partial class CultSystem
[Dependency] private readonly PhysicsSystem _physics = default!;
[Dependency] private readonly HolyWeaponSystem _holyWeapon = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly WizardSpellsSystem _spells = default!;
private const string TileId = "CultFloor";
private const string ConcealedTileId = "CultFloorConcealed";
@@ -66,7 +69,7 @@ public partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultBloodRitesInstantActionEvent>(OnBloodRites);
SubscribeLocalEvent<CultistComponent, CultBloodSpearRecallInstantActionEvent>(OnBloodSpearRecall);
SubscribeLocalEvent<CultistComponent, CultTeleportTargetActionEvent>(OnTeleport);
SubscribeLocalEvent<CultistComponent, CultStunTargetActionEvent>(OnStunTarget);
SubscribeLocalEvent<CultistComponent, CultStunActionEvent>(OnStun);
SubscribeLocalEvent<CultistComponent, ActionGettingRemovedEvent>(OnActionRemoved);
SubscribeLocalEvent<CultistComponent, ShacklesEvent>(OnShackles);
}
@@ -84,6 +87,11 @@ public partial class CultSystem
if (_cuffable.TryAddNewCuffs(args.Target.Value, args.User, cuffs, cuffable, handcuffComponent))
{
SharedCuffableSystem.SetUsed(handcuffComponent, true);
if (_statusEffectsSystem.TryAddStatusEffect(args.Target.Value, "Muted", TimeSpan.FromSeconds(10),
true, "Muted"))
{
_popupSystem.PopupEntity("Цель обезмолвлена.", args.User, args.User);
}
return;
}
@@ -96,32 +104,16 @@ public partial class CultSystem
Dirty(ent);
}
private void OnStunTarget(EntityUid uid, CultistComponent component, CultStunTargetActionEvent args)
private void OnStun(EntityUid uid, CultistComponent component, CultStunActionEvent args)
{
if (args.Target == uid || !TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) ||
!TryComp<StatusEffectsComponent>(args.Target, out var status))
return;
if (_holyWeapon.IsHoldingHolyWeapon(args.Target))
var entity = Spawn("StunHand", Transform(uid).Coordinates);
if (!_handsSystem.TryPickupAnyHand(uid, entity))
{
_popupSystem.PopupEntity("Сила священного оружия препятствует магии.", args.Performer, args.Performer,
PopupType.MediumCaution);
_popupSystem.PopupEntity(Loc.GetString("cult-magic-no-empty-hand"), uid, uid);
QueueDel(entity);
_actionsSystem.SetCooldown(args.Action, TimeSpan.FromSeconds(1));
return;
}
if (HasComp<MindShieldComponent>(args.Target))
{
_popupSystem.PopupEntity("Он имплантирован чипом защиты разума.", args.Performer, args.Performer,
PopupType.MediumCaution);
return;
}
if (!_stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(6), true, status) &
!_statusEffectsSystem.TryAddStatusEffect(args.Target, "Muted", TimeSpan.FromSeconds(12), true, "Muted",
status))
return;
_bloodstreamSystem.TryModifyBloodLevel(uid, -10, bloodstream, createPuddle: false);
args.Handled = true;
}
@@ -133,7 +125,7 @@ public partial class CultSystem
if (_holyWeapon.IsHoldingHolyWeapon(args.Target))
{
_popupSystem.PopupEntity("Сила священного оружия препятствует магии.", args.Performer, args.Performer,
_popupSystem.PopupEntity(Loc.GetString("cult-magic-holy"), args.Performer, args.Performer,
PopupType.MediumCaution);
return;
}
@@ -152,6 +144,7 @@ public partial class CultSystem
_euiManager.OpenEui(eui, actor.PlayerSession);
eui.StateDirty();
Speak(args);
args.Handled = true;
}
@@ -210,6 +203,7 @@ public partial class CultSystem
_popupSystem.PopupEntity(Loc.GetString("verb-blood-rites-message", ("blood", component.RitesBloodAmount)), uid,
uid);
Speak(args);
args.Handled = true;
}
@@ -342,6 +336,7 @@ public partial class CultSystem
_audio.PlayPvs(conceal ? "/Audio/White/Cult/smoke.ogg" : "/Audio/White/Cult/enter_blood.ogg", uid,
AudioParams.Default.WithMaxDistance(5f));
_bloodstreamSystem.TryModifyBloodLevel(uid, -2, bloodstream, createPuddle: false);
Speak(args);
args.Handled = true;
}
@@ -420,6 +415,7 @@ public partial class CultSystem
_empSystem.EmpPulse(_transform.GetMapCoordinates(uid), 5, 100000, 10f);
Speak(args);
args.Handled = true;
}
@@ -430,13 +426,6 @@ public partial class CultSystem
_bloodstreamSystem.TryModifyBloodLevel(uid, -5, bloodstream, createPuddle: false);
if (!_holyWeapon.IsHoldingHolyWeapon(args.Target) &&
_statusEffectsSystem.TryAddStatusEffect(args.Target, "Muted", TimeSpan.FromSeconds(10), true, "Muted"))
{
_popupSystem.PopupEntity("Цель обезмолвлена.", args.Performer, args.Performer);
args.Handled = true;
}
if (!TryComp(args.Target, out CuffableComponent? cuffs) || cuffs.Container.ContainedEntities.Count > 0)
return;
@@ -447,6 +436,7 @@ public partial class CultSystem
BreakOnDamage = true
});
Speak(args);
args.Handled = true;
}
@@ -482,6 +472,7 @@ public partial class CultSystem
stackNew.Count = count;
_popupSystem.PopupEntity("Конвертируем сталь в руинический металл!", args.Performer, args.Performer);
Speak(args);
args.Handled = true;
}
@@ -498,6 +489,12 @@ public partial class CultSystem
_bloodstreamSystem.TryModifyBloodLevel(args.Performer, -10, bloodstreamComponent, false);
_handsSystem.TryPickupAnyHand(args.Performer, dagger);
Speak(args);
args.Handled = true;
}
private void Speak(BaseActionEvent args)
{
_spells.Speak(args, InGameICChatType.Whisper);
}
}

View File

@@ -16,6 +16,7 @@ using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Fluids.Components;
using Content.Server.Ghost;
using Content.Server.Pinpointer;
using Content.Server.Revenant.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Cuffs.Components;
@@ -51,6 +52,7 @@ using Robust.Shared.Map;
using Robust.Shared.Physics.Events;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent;
namespace Content.Server._White.Cult.Runes.Systems;
@@ -77,6 +79,7 @@ public sealed partial class CultSystem : EntitySystem
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly MobThresholdSystem _thresholdSystem = default!;
[Dependency] private readonly NavMapSystem _navMap = default!;
public override void Initialize()
{
@@ -404,7 +407,7 @@ public sealed partial class CultSystem : EntitySystem
foreach (var cultist in cultists)
{
_chat.TrySendInGameICMessage(cultist, component.InvokePhrase, InGameICChatType.Speak, false, false, null,
_chat.TrySendInGameICMessage(cultist, component.InvokePhrase, component.InvokeChatType, false, false, null,
null, null, false);
}
}
@@ -1347,11 +1350,8 @@ public sealed partial class CultSystem : EntitySystem
}
damage = 40;
var pos = _transform.GetMapCoordinates(uid, transComp);
var x = (int) pos.X;
var y = (int) pos.Y;
var posText = $"(x = {x}, y = {y})";
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-narsie-summon-drawn-position", ("posText", posText)),
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-narsie-summon-drawn-position",
("location", FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((uid, transComp))))),
"CULT", true, _apocRuneEndDrawing, colorOverride: Color.DarkRed);
}

View File

@@ -8,13 +8,13 @@ namespace Content.Server._White.Mood;
[RegisterComponent]
public sealed partial class MoodComponent : Component
{
[DataField("currentMoodLevel"), ViewVariables(VVAccess.ReadOnly)]
[DataField, ViewVariables(VVAccess.ReadOnly)]
public float CurrentMoodLevel;
[DataField("currentMoodThreshold"), ViewVariables(VVAccess.ReadOnly)]
[DataField, ViewVariables(VVAccess.ReadOnly)]
public MoodThreshold CurrentMoodThreshold;
[DataField("lastThreshold"), ViewVariables(VVAccess.ReadOnly)]
[DataField, ViewVariables(VVAccess.ReadOnly)]
public MoodThreshold LastThreshold;
[ViewVariables(VVAccess.ReadOnly)]
@@ -23,22 +23,22 @@ public sealed partial class MoodComponent : Component
[ViewVariables(VVAccess.ReadOnly)]
public readonly Dictionary<string, float> UncategorisedEffects = new();
[DataField("slowdownSpeedModifier"), ViewVariables(VVAccess.ReadWrite)]
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float SlowdownSpeedModifier = 0.75f;
[DataField("increaseSpeedModifier"), ViewVariables(VVAccess.ReadWrite)]
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float IncreaseSpeedModifier = 1.15f;
[DataField("increaseCritThreshold"), ViewVariables(VVAccess.ReadWrite)]
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float IncreaseCritThreshold = 1.2f;
[DataField("decreaseCritThreshold"), ViewVariables(VVAccess.ReadWrite)]
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float DecreaseCritThreshold = 0.9f;
[ViewVariables(VVAccess.ReadOnly)]
public FixedPoint2 CritThresholdBeforeModify;
[DataField("moodThresholds", customTypeSerializer: typeof(DictionarySerializer<MoodThreshold, float>))]
[DataField(customTypeSerializer: typeof(DictionarySerializer<MoodThreshold, float>))]
public Dictionary<MoodThreshold, float> MoodThresholds = new()
{
{ MoodThreshold.VeryVeryGood, 10.0f },
@@ -53,7 +53,7 @@ public sealed partial class MoodComponent : Component
{ MoodThreshold.Dead, 0.0f }
};
[DataField("moodThresholdsAlerts", customTypeSerializer: typeof(DictionarySerializer<MoodThreshold, AlertType>))]
[DataField(customTypeSerializer: typeof(DictionarySerializer<MoodThreshold, AlertType>))]
public Dictionary<MoodThreshold, AlertType> MoodThresholdsAlerts = new()
{
{ MoodThreshold.Dead, AlertType.MoodDead },
@@ -69,7 +69,7 @@ public sealed partial class MoodComponent : Component
{ MoodThreshold.Insane, AlertType.Insane }
};
[DataField("moodChangeValues", customTypeSerializer: typeof(DictionarySerializer<Enum, float>))]
[DataField(customTypeSerializer: typeof(DictionarySerializer<Enum, float>))]
public Dictionary<Enum, float> MoodChangeValues = new()
{
{ MoodChangeLevel.None , 0.0f },
@@ -80,7 +80,7 @@ public sealed partial class MoodComponent : Component
{ MoodChangeLevel.Large , 2f }
};
[DataField("healthMoodEffectsThresholds", customTypeSerializer: typeof(DictionarySerializer<string, float>))]
[DataField(customTypeSerializer: typeof(DictionarySerializer<string, float>))]
public Dictionary<string, float> HealthMoodEffectsThresholds = new()
{
{ "HealthHeavyDamage", 80f },

View File

@@ -108,8 +108,8 @@ public sealed class MoodSystem : EntitySystem
if (!component.MoodChangeValues.TryGetValue(oldPrototype.MoodChange, out var oldValue))
return;
amount += (oldPrototype.PositiveEffect ? -oldValue : oldValue) +
(prototype.PositiveEffect ? value : -value);
amount += (oldPrototype.Positive ? -oldValue : oldValue) +
(prototype.Positive ? value : -value);
component.CategorisedEffects[prototype.Category] = prototype.ID;
}
@@ -117,7 +117,7 @@ public sealed class MoodSystem : EntitySystem
else
{
component.CategorisedEffects.Add(prototype.Category, prototype.ID);
amount += prototype.PositiveEffect ? value : -value;
amount += prototype.Positive ? value : -value;
}
if (prototype.Timeout != 0)
@@ -132,7 +132,7 @@ public sealed class MoodSystem : EntitySystem
if (component.UncategorisedEffects.TryGetValue(prototype.ID, out _))
return;
var effectValue = prototype.PositiveEffect ? value : -value;
var effectValue = prototype.Positive ? value : -value;
component.UncategorisedEffects.Add(prototype.ID, effectValue);
amount += effectValue;
@@ -173,7 +173,7 @@ public sealed class MoodSystem : EntitySystem
if (!comp.MoodChangeValues.TryGetValue(currentProto.MoodChange, out var value))
return;
amount += currentProto.PositiveEffect ? -value : value;
amount += currentProto.Positive ? -value : value;
comp.CategorisedEffects.Remove(category);
}
@@ -205,7 +205,7 @@ public sealed class MoodSystem : EntitySystem
if (!component.MoodChangeValues.TryGetValue(prototype.MoodChange, out var value))
return;
amount += prototype.PositiveEffect ? value : -value;
amount += prototype.Positive ? value : -value;
}
foreach (var (_, value) in component.UncategorisedEffects)
@@ -438,7 +438,7 @@ public sealed partial class ShowMoodEffects : IAlertClick
{
var chatManager = IoCManager.Resolve<IChatManager>();
var color = proto.PositiveEffect ? "#008000" : "#BA0000";
var color = proto.Positive ? "#008000" : "#BA0000";
var msg = $"[font size=10][color={color}]{proto.Description}[/color][/font]";
chatManager.ChatMessageToOne(ChatChannel.Emotes, msg, msg, EntityUid.Invalid, false,

View File

@@ -174,6 +174,12 @@ public sealed class WizardSpellsSystem : EntitySystem
return;
}
if (HasComp<CultistComponent>(target))
{
_popupSystem.PopupEntity(Loc.GetString("mindswap-cultist-failed"), uid, uid, PopupType.MediumCaution);
return;
}
var userHasMind = _mindSystem.TryGetMind(uid, out var mindId, out var mind);
var targetHasMind = _mindSystem.TryGetMind(target, out var targetMindId, out var targetMind);
@@ -183,8 +189,6 @@ public sealed class WizardSpellsSystem : EntitySystem
SwapComponent<WizardComponent>(uid, target);
SwapComponent<RevolutionaryComponent>(uid, target);
SwapComponent<HeadRevolutionaryComponent>(uid, target);
SwapComponent<PentagramComponent>(uid, target);
SwapComponent<CultistComponent>(uid, target);
SwapComponent<GlobalAntagonistComponent>(uid, target);
_mindSystem.TransferTo(mindId, target, mind: mind);
@@ -854,13 +858,12 @@ public sealed class WizardSpellsSystem : EntitySystem
!_statusEffectsSystem.HasStatusEffect(msg.Performer, "Incorporeal");
}
private void Speak(BaseActionEvent args)
public void Speak(BaseActionEvent args, InGameICChatType type = InGameICChatType.Speak)
{
if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech))
return;
_chat.TrySendInGameICMessage(args.Performer, Loc.GetString(speak.Speech),
InGameICChatType.Speak, false);
_chat.TrySendInGameICMessage(args.Performer, Loc.GetString(speak.Speech), type, false);
}
private void SetCooldown(EntityUid action, ActionUseType useType)