diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs index 7172fec7fe..f3a249c06c 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -81,7 +81,7 @@ public sealed partial class CargoSystem : SharedCargoSystem } [PublicAPI] - public void UpdateBankAccount(EntityUid uid, StationBankAccountComponent component, int balanceAdded) + public void UpdateBankAccount(EntityUid? uid, StationBankAccountComponent component, int balanceAdded) { component.Balance += balanceAdded; var query = EntityQueryEnumerator(); diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index d3e38a15ea..a6094e3606 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -340,6 +340,39 @@ public sealed class TraitorRuleSystem : GameRuleSystem args.AgentName = Loc.GetString("traitor-round-end-agent-name"); } + public List<(EntityUid Id, MindComponent Mind)> GetAllLivingConnectedTraitors() + { + var traitors = new List<(EntityUid Id, MindComponent Mind)>(); + + var traitorRules = EntityQuery(); + + foreach (var traitorRule in traitorRules) + { + traitors.AddRange(GetLivingConnectedTraitors(traitorRule)); + } + + return traitors; + } + + private List<(EntityUid Id, MindComponent Mind)> GetLivingConnectedTraitors(TraitorRuleComponent traitorRule) + { + var traitors = new List<(EntityUid Id, MindComponent Mind)>(); + + foreach (var traitor in traitorRule.TraitorMinds) + { + if (TryComp(traitor, out MindComponent? mind) && + mind.OwnedEntity != null && + mind.Session != null && + _mobStateSystem.IsAlive(mind.OwnedEntity.Value) && + mind.CurrentEntity == mind.OwnedEntity) + { + traitors.Add((traitor, mind)); + } + } + + return traitors; + } + private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextPrependEvent args) { args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords))); diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 1d256071d6..d0e6ddb5cc 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -5,10 +5,11 @@ using Content.Shared.Camera; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.Projectiles; -using Robust.Server.GameObjects; +using Content.Shared.White; +using Robust.Shared.Configuration; +using Robust.Shared.Player; using Robust.Shared.Physics.Events; using Content.Shared.Mobs.Components; -using Robust.Shared.Player; namespace Content.Server.Projectiles; @@ -19,10 +20,18 @@ public sealed class ProjectileSystem : SharedProjectileSystem [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly GunSystem _guns = default!; [Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + + private float DamageModifier { get; set; } + + private void SetDamage(float value) => DamageModifier = value; public override void Initialize() { base.Initialize(); + + _cfg.OnValueChanged(WhiteCVars.DamageModifier, SetDamage, true); + SubscribeLocalEvent(OnStartCollide); } @@ -48,7 +57,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem var otherName = ToPrettyString(target); var direction = args.OurBody.LinearVelocity.Normalized(); - var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: component.Shooter); + var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage * DamageModifier, component.IgnoreResistances, origin: component.Shooter); var deleted = Deleted(target); if (modifiedDamage is not null && EntityManager.EntityExists(component.Shooter)) diff --git a/Content.Server/White/AspectsSystem/Aspects/AiRunLockAspect.cs b/Content.Server/White/AspectsSystem/Aspects/AiRunLockAspect.cs new file mode 100644 index 0000000000..466940a8ce --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/AiRunLockAspect.cs @@ -0,0 +1,20 @@ +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Shared.Access.Components; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class AiRunLockAspect : AspectSystem +{ + protected override void Started(EntityUid uid, AiRunLockAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var accessReaderComponent)) + { + accessReaderComponent.Enabled = false; + } + } +} diff --git a/Content.Server/White/AspectsSystem/Aspects/BattledAspect.cs b/Content.Server/White/AspectsSystem/Aspects/BattledAspect.cs new file mode 100644 index 0000000000..d4a2812b0d --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/BattledAspect.cs @@ -0,0 +1,87 @@ +using System.Linq; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Humanoid; +using Content.Shared.White.NonPeacefulRoundEnd; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class BattledAspect : AspectSystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + private NonPeacefulRoundItemsPrototype _nonPeacefulRoundItemsPrototype = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(HandleLateJoin); + } + + protected override void Started(EntityUid uid, BattledAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var prototypes = _prototypeManager.EnumeratePrototypes().ToList(); + + if (prototypes.Count == 0) + ForceEndSelf(uid, gameRule); + + _nonPeacefulRoundItemsPrototype = _robustRandom.Pick(prototypes); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out _)) + { + GiveItem(ent); + } + + } + + private void HandleLateJoin(PlayerSpawnCompleteEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ruleEntity, out _, out var gameRule)) + { + if (!GameTicker.IsGameRuleAdded(ruleEntity, gameRule)) + continue; + + if (!ev.LateJoin) + return; + + var mob = ev.Mob; + + GiveItem(mob); + } + } + + #region Helpers + + private void GiveItem(EntityUid player) + { + var item = _robustRandom.Pick(_nonPeacefulRoundItemsPrototype.Items); + + var transform = CompOrNull(player); + + if(transform == null) + return; + + if(!HasComp(player)) + return; + + var weaponEntity = EntityManager.SpawnEntity(item, transform.Coordinates); + + _handsSystem.TryDrop(player); + _handsSystem.PickupOrDrop(player, weaponEntity); + } + + #endregion +} diff --git a/Content.Server/White/AspectsSystem/Aspects/BloodyAspect.cs b/Content.Server/White/AspectsSystem/Aspects/BloodyAspect.cs new file mode 100644 index 0000000000..84200b8bce --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/BloodyAspect.cs @@ -0,0 +1,26 @@ +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Shared.White; +using Robust.Shared.Configuration; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class BloodyAspect : AspectSystem +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + + protected override void Started(EntityUid uid, BloodyAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + _cfg.SetCVar(WhiteCVars.DamageGetModifier, 2.5f); + } + + protected override void Ended(EntityUid uid, BloodyAspectComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) + { + base.Ended(uid, component, gameRule, args); + + _cfg.SetCVar(WhiteCVars.DamageGetModifier, 1.0f); + } +} diff --git a/Content.Server/White/AspectsSystem/Aspects/BombassAspect.cs b/Content.Server/White/AspectsSystem/Aspects/BombassAspect.cs new file mode 100644 index 0000000000..f69a7926e2 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/BombassAspect.cs @@ -0,0 +1,31 @@ +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Robust.Shared.Random; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class BombassAspect : AspectSystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + protected override void Added(EntityUid uid, BombassAspectComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) + { + base.Added(uid, component, gameRule, args); + + SpawnMines(); + } + + private void SpawnMines() + { + var minMines = _random.Next(35, 100); + + for (var i = 0; i < minMines; i++) + { + if (!TryFindRandomTile(out _, out _, out _, out var targetCoords)) + break; + + EntityManager.SpawnEntity("LandMineExplosive", targetCoords); + } + } +} diff --git a/Content.Server/White/AspectsSystem/Aspects/CargoRichAspect.cs b/Content.Server/White/AspectsSystem/Aspects/CargoRichAspect.cs new file mode 100644 index 0000000000..3f38b8d124 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/CargoRichAspect.cs @@ -0,0 +1,25 @@ +using Content.Server.Cargo.Components; +using Content.Server.Cargo.Systems; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class CargoRichAspect : AspectSystem +{ + [Dependency] private readonly CargoSystem _cargoSystem = default!; + + protected override void Added(EntityUid uid, CargoRichAspectComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) + { + base.Added(uid, component, gameRule, args); + + if (!TryGetRandomStation(out var station)) + return; + + if (!TryComp(station, out var stationBankAccountComponent)) + return; + + _cargoSystem.UpdateBankAccount(station, stationBankAccountComponent, 100000); + } +} diff --git a/Content.Server/White/AspectsSystem/Aspects/ChairLeakAspect.cs b/Content.Server/White/AspectsSystem/Aspects/ChairLeakAspect.cs new file mode 100644 index 0000000000..b54995eb85 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/ChairLeakAspect.cs @@ -0,0 +1,21 @@ +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Server.White.Other; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class ChairLeakAspect : AspectSystem +{ + protected override void Started(EntityUid uid, ChairLeakAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out _)) + { + EntityManager.DeleteEntity(ent); + } + } + +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/AiRunLockAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/AiRunLockAspectComponent.cs new file mode 100644 index 0000000000..2e5ed03610 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/AiRunLockAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class AiRunLockAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/BattledAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/BattledAspectComponent.cs new file mode 100644 index 0000000000..91314b0deb --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/BattledAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class BattledAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/BloodyAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/BloodyAspectComponent.cs new file mode 100644 index 0000000000..575c3293ef --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/BloodyAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class BloodyAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/BombassAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/BombassAspectComponent.cs new file mode 100644 index 0000000000..ada958679b --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/BombassAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class BombassAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/CargoRichAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/CargoRichAspectComponent.cs new file mode 100644 index 0000000000..1b7af8cf25 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/CargoRichAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class CargoRichAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/ChairLeakAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/ChairLeakAspectComponent.cs new file mode 100644 index 0000000000..a7b0713d5e --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/ChairLeakAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class ChairLeakAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/DrunkAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/DrunkAspectComponent.cs new file mode 100644 index 0000000000..2914655929 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/DrunkAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class DrunkAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/FastAndFuriousAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/FastAndFuriousAspectComponent.cs new file mode 100644 index 0000000000..1f45878659 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/FastAndFuriousAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class FastAndFuriousAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/NoEngineAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/NoEngineAspectComponent.cs new file mode 100644 index 0000000000..8835030288 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/NoEngineAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class NoEngineAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/RandomAccentAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/RandomAccentAspectComponent.cs new file mode 100644 index 0000000000..f01476e9f7 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/RandomAccentAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class RandomAccentAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/RandomAppearanceAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/RandomAppearanceAspectComponent.cs new file mode 100644 index 0000000000..593c8ef6ab --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/RandomAppearanceAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class RandomAppearanceAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/TraitoredAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/TraitoredAspectComponent.cs new file mode 100644 index 0000000000..3702fe5381 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/TraitoredAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class TraitoredAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/WeakAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/WeakAspectComponent.cs new file mode 100644 index 0000000000..16acf4f024 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/WeakAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class WeakAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/Components/WeakWallsAspectComponent.cs b/Content.Server/White/AspectsSystem/Aspects/Components/WeakWallsAspectComponent.cs new file mode 100644 index 0000000000..a7ab371705 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/Components/WeakWallsAspectComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.AspectsSystem.Aspects.Components; + +[RegisterComponent] +public sealed partial class WeakWallsAspectComponent : Component +{ +} diff --git a/Content.Server/White/AspectsSystem/Aspects/DrunkAspect.cs b/Content.Server/White/AspectsSystem/Aspects/DrunkAspect.cs new file mode 100644 index 0000000000..2beaf669e0 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/DrunkAspect.cs @@ -0,0 +1,48 @@ +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Shared.Drunk; +using Content.Shared.Humanoid; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class DrunkAspect : AspectSystem +{ + [Dependency] private readonly SharedDrunkSystem _drunkSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(HandleLateJoin); + } + + protected override void Started(EntityUid uid, DrunkAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out _)) + { + _drunkSystem.TryApplyDrunkenness(ent, 50); + } + } + + private void HandleLateJoin(PlayerSpawnCompleteEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ruleEntity, out _, out var gameRule)) + { + if (!GameTicker.IsGameRuleAdded(ruleEntity, gameRule)) + continue; + + if (!ev.LateJoin) + return; + + var mob = ev.Mob; + + _drunkSystem.TryApplyDrunkenness(mob, 50); + } + } +} diff --git a/Content.Server/White/AspectsSystem/Aspects/FastAndFuriousAspect.cs b/Content.Server/White/AspectsSystem/Aspects/FastAndFuriousAspect.cs new file mode 100644 index 0000000000..e76a0cac87 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/FastAndFuriousAspect.cs @@ -0,0 +1,59 @@ +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class FastAndFuriousAspect : AspectSystem +{ + [Dependency] private readonly MovementSpeedModifierSystem _movementSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(HandleLateJoin); + } + + protected override void Started(EntityUid uid, FastAndFuriousAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out var speedModifierComponent)) + { + _movementSystem.ChangeBaseSpeed(ent, speedModifierComponent.BaseWalkSpeed + 2.5f, speedModifierComponent.BaseSprintSpeed + 4, speedModifierComponent.Acceleration); + } + } + + protected override void Ended(EntityUid uid, FastAndFuriousAspectComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) + { + base.Ended(uid, component, gameRule, args); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out var speedModifierComponent)) + { + _movementSystem.ChangeBaseSpeed(ent, 2.5f, 4.5f, speedModifierComponent.Acceleration); + } + } + + private void HandleLateJoin(PlayerSpawnCompleteEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ruleEntity, out _, out var gameRule)) + { + if (!GameTicker.IsGameRuleAdded(ruleEntity, gameRule)) + continue; + + if (!ev.LateJoin) + return; + + var mob = ev.Mob; + + if (!TryComp(mob, out var speedModifierComponent)) + return; + + _movementSystem.ChangeBaseSpeed(mob, speedModifierComponent.BaseWalkSpeed + 2.5f, speedModifierComponent.BaseSprintSpeed + 4, speedModifierComponent.Acceleration); + } + } +} diff --git a/Content.Server/White/AspectsSystem/Aspects/NoEngineAspect.cs b/Content.Server/White/AspectsSystem/Aspects/NoEngineAspect.cs new file mode 100644 index 0000000000..45fffe91c8 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/NoEngineAspect.cs @@ -0,0 +1,21 @@ +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Server.White.Other; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class NoEngineAspect : AspectSystem +{ + protected override void Started(EntityUid uid, NoEngineAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out _)) + { + EntityManager.DeleteEntity(ent); + } + } + +} diff --git a/Content.Server/White/AspectsSystem/Aspects/RandomAccentAspect.cs b/Content.Server/White/AspectsSystem/Aspects/RandomAccentAspect.cs new file mode 100644 index 0000000000..8e16c8bbcc --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/RandomAccentAspect.cs @@ -0,0 +1,107 @@ +using System.Linq; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Speech.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Shared.Mind.Components; +using Robust.Shared.Random; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class RandomAccentAspect : AspectSystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(HandleLateJoin); + } + + protected override void Started(EntityUid uid, RandomAccentAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out _)) + { + ApplyRandomAccent(ent); + } + } + + private void HandleLateJoin(PlayerSpawnCompleteEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ruleEntity, out _, out var gameRule)) + { + if (!GameTicker.IsGameRuleAdded(ruleEntity, gameRule)) + continue; + + if (!ev.LateJoin) + return; + + var mob = ev.Mob; + + ApplyRandomAccent(mob); + } + } + + #region Helpers + + private enum AccentType + { + Stuttering, + Spanish, + Slurred, + Scrambled, + Pirate, + Russian, + Anime, + Lizard, + Backwards + } + + private void ApplyRandomAccent(EntityUid uid) + { + var allAccents = Enum.GetValues(typeof(AccentType)).Cast().ToList(); + var randomIndex = _random.Next(allAccents.Count); + var selectedAccent = allAccents[randomIndex]; + ApplyAccent(uid, selectedAccent); + } + + private void ApplyAccent(EntityUid uid, AccentType accentType) + { + switch (accentType) + { + case AccentType.Stuttering: + EntityManager.EnsureComponent(uid); + break; + case AccentType.Spanish: + EntityManager.EnsureComponent(uid); + break; + case AccentType.Slurred: + EntityManager.EnsureComponent(uid); + break; + case AccentType.Scrambled: + EntityManager.EnsureComponent(uid); + break; + case AccentType.Pirate: + EntityManager.EnsureComponent(uid); + break; + case AccentType.Russian: + EntityManager.EnsureComponent(uid); + break; + case AccentType.Anime: + EntityManager.EnsureComponent(uid); + break; + case AccentType.Lizard: + EntityManager.EnsureComponent(uid); + break; + case AccentType.Backwards: + EntityManager.EnsureComponent(uid); + break; + } + } + + #endregion +} diff --git a/Content.Server/White/AspectsSystem/Aspects/RandomAppearanceAspect.cs b/Content.Server/White/AspectsSystem/Aspects/RandomAppearanceAspect.cs new file mode 100644 index 0000000000..c166cf7170 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/RandomAppearanceAspect.cs @@ -0,0 +1,44 @@ +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Server.White.Other.RandomHumanSystem; +using Content.Shared.Humanoid; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class RandomAppearanceAspect : AspectSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(HandleLateJoin); + } + + protected override void Started(EntityUid uid, RandomAppearanceAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out _)) + { + EnsureComp(ent); + } + } + + private void HandleLateJoin(PlayerSpawnCompleteEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ruleEntity, out _, out var gameRule)) + { + if (!GameTicker.IsGameRuleAdded(ruleEntity, gameRule)) + continue; + + if (!ev.LateJoin) + return; + + var mob = ev.Mob; + + EnsureComp(mob); + } + } +} diff --git a/Content.Server/White/AspectsSystem/Aspects/TraitoredAspect.cs b/Content.Server/White/AspectsSystem/Aspects/TraitoredAspect.cs new file mode 100644 index 0000000000..4d42b58824 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/TraitoredAspect.cs @@ -0,0 +1,130 @@ +using System.Linq; +using Content.Server.Chat.Managers; +using Content.Server.Chat.Systems; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Mind; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Random; + +namespace Content.Server.White.AspectsSystem.Aspects +{ + public sealed class TraitoredAspect : AspectSystem + { + [Dependency] private readonly TraitorRuleSystem _traitorRuleSystem = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly ChatSystem _chatSystem = default!; + [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + private bool _announcedForTators; + + private float _timeElapsed; + private float _timeElapsedForTators; + + private float _wacky; + private const float WackyAaa = 60; + + protected override void Started(EntityUid uid, TraitoredAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + // Just to make sure + ResetValues(); + + if (!HasTraitorGameRule()) + ForceEndSelf(uid, gameRule); + + _wacky = _random.Next(300, 360); + } + + protected override void ActiveTick(EntityUid uid, TraitoredAspectComponent component, GameRuleComponent gameRule, float frameTime) + { + base.ActiveTick(uid, component, gameRule, frameTime); + + _timeElapsedForTators += frameTime; + _timeElapsed += frameTime; + + if (_timeElapsedForTators >= WackyAaa && !_announcedForTators) + { + AnnounceToTators(uid, gameRule); + _announcedForTators = true; + } + + if (_timeElapsed >= _wacky) + { + AnnounceToAll(uid, gameRule); + } + } + + protected override void Ended(EntityUid uid, TraitoredAspectComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) + { + base.Ended(uid, component, gameRule, args); + ResetValues(); + } + + #region Helpers + + private void AnnounceToTators(EntityUid uid, GameRuleComponent rule) + { + var tators = _traitorRuleSystem.GetAllLivingConnectedTraitors(); + + if (tators.Count == 0) + { + ForceEndSelf(uid, rule); + } + + foreach (var tator in tators) + { + if (!_mindSystem.TryGetSession(tator.Mind, out var session)) + continue; + + var nig = tator.Mind.OwnedEntity; + + if (nig == null) + return; + + _chatManager.DispatchServerMessage(session, "Внимание, коммуникации синдиката перехвачены, вас раскрыли!"); + _audio.PlayEntity("/Audio/White/Aspects/palevo.ogg", nig.Value, nig.Value); + } + } + + private void AnnounceToAll(EntityUid uid, GameRuleComponent rule) + { + var tators = _traitorRuleSystem.GetAllLivingConnectedTraitors(); + + var msg = "Станция, служба контрразведки нанотрейзен рассекретила секретную передачу Синдиката и выяснила имена проникниших на вашу станцию агентов. Агенты имеют следующие имена: \n"; + + foreach (var tator in tators) + { + var name = tator.Mind.CharacterName; + if (!string.IsNullOrEmpty(name)) + { + msg += $" {name} - УБЕЙТЕ ЕГО НАХУЙ\n"; + } + } + + _chatSystem.DispatchGlobalAnnouncement(msg, "Мяукиман Крысус"); + + ForceEndSelf(uid, rule); + } + + private void ResetValues() + { + _announcedForTators = false; + _timeElapsed = 0; + _timeElapsedForTators = 0; + } + + private bool HasTraitorGameRule() + { + return EntityQuery().Any(); + } + + #endregion + + } +} diff --git a/Content.Server/White/AspectsSystem/Aspects/WeakAspect.cs b/Content.Server/White/AspectsSystem/Aspects/WeakAspect.cs new file mode 100644 index 0000000000..4cd0a1ab77 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/WeakAspect.cs @@ -0,0 +1,26 @@ +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Shared.White; +using Robust.Shared.Configuration; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class WeakAspect : AspectSystem +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + + protected override void Started(EntityUid uid, WeakAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + _cfg.SetCVar(WhiteCVars.DamageGetModifier, 0.5f); + } + + protected override void Ended(EntityUid uid, WeakAspectComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) + { + base.Ended(uid, component, gameRule, args); + + _cfg.SetCVar(WhiteCVars.DamageGetModifier, 1.0f); + } +} diff --git a/Content.Server/White/AspectsSystem/Aspects/WeakWallsAspect.cs b/Content.Server/White/AspectsSystem/Aspects/WeakWallsAspect.cs new file mode 100644 index 0000000000..c3b948d09b --- /dev/null +++ b/Content.Server/White/AspectsSystem/Aspects/WeakWallsAspect.cs @@ -0,0 +1,35 @@ +using System.Linq; +using Content.Server.Destructible; +using Content.Server.Destructible.Thresholds.Triggers; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.White.AspectsSystem.Aspects.Components; +using Content.Server.White.AspectsSystem.Base; +using Content.Server.White.Other; + +namespace Content.Server.White.AspectsSystem.Aspects; + +public sealed class WeakWallsAspect : AspectSystem +{ + private const int DamageToSet = 100; + + protected override void Started(EntityUid uid, WeakWallsAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out _)) + { + if (!TryComp(ent, out var destructible)) + continue; + + var trigger = (DamageTrigger?) destructible.Thresholds + .LastOrDefault(threshold => threshold.Trigger is DamageTrigger)?.Trigger; + + if (trigger == null) + continue; + + trigger.Damage = (trigger.Damage == DamageToSet) ? trigger.Damage : DamageToSet; + } + } + +} diff --git a/Content.Server/White/AspectsSystem/Base/AspectComponent.cs b/Content.Server/White/AspectsSystem/Base/AspectComponent.cs new file mode 100644 index 0000000000..eed239b595 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Base/AspectComponent.cs @@ -0,0 +1,39 @@ +using Robust.Shared.Audio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Server.White.AspectsSystem.Base +{ + [RegisterComponent] + public sealed partial class AspectComponent : Component + { + [DataField("name")] + public string? Name; + + [DataField("description")] + public string? Description; + + [DataField("requires")] + public string? Requires; + + [DataField("weight")] + public float Weight = 1.0f; + + [DataField("forbidden")] + public bool IsForbidden; + + [DataField("hidden")] + public bool IsHidden; + + [DataField("startAudio")] + public SoundSpecifier? StartAudio; + + [DataField("endAudio")] + public SoundSpecifier? EndAudio; + + [DataField("startDelay")] + public TimeSpan StartDelay = TimeSpan.Zero; + + [DataField("startTime", customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan StartTime; + } +} diff --git a/Content.Server/White/AspectsSystem/Base/AspectSystem.cs b/Content.Server/White/AspectsSystem/Base/AspectSystem.cs new file mode 100644 index 0000000000..16e2b39978 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Base/AspectSystem.cs @@ -0,0 +1,211 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Server.Administration.Logs; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Chat.Systems; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Station.Components; +using Content.Shared.Database; +using Robust.Server.GameObjects; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Collections; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Player; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.White.AspectsSystem.Base +{ + /// + /// Base class for aspect systems. + /// + /// The type of component to which the system is applied. + public abstract class AspectSystem : GameRuleSystem where T : Component + { + [Dependency] private readonly IAdminLogManager _adminLogManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ChatSystem _chatSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + [Dependency] private readonly TransformSystem _transform = default!; + + protected ISawmill Sawmill = default!; + + public override void Initialize() + { + base.Initialize(); + + Sawmill = Logger.GetSawmill("aspects"); + } + + /// + /// Called every tick when this aspect is running. + /// + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var aspect, out var ruleData)) + { + if (!GameTicker.IsGameRuleAdded(uid, ruleData)) + continue; + + if (!GameTicker.IsGameRuleActive(uid, ruleData) && _timing.CurTime >= aspect.StartTime) + { + GameTicker.StartGameRule(uid, ruleData); + } + } + } + + /// + /// Called when an aspect is added to an entity. + /// + protected override void Added(EntityUid uid, T component, GameRuleComponent gameRule, GameRuleAddedEvent args) + { + base.Added(uid, component, gameRule, args); + + if (!TryComp(uid, out var aspect)) + return; + + _adminLogManager.Add(LogType.AspectAnnounced, $"Aspect added {ToPrettyString(uid)}"); + + if (aspect is { Description: not null, IsHidden: false }) + { + _chatSystem.DispatchGlobalAnnouncement(aspect.Description, playSound: false, colorOverride: Color.LimeGreen); + } + + _audio.PlayGlobal(aspect.StartAudio, Filter.Broadcast(), true); + aspect.StartTime = _timing.CurTime + aspect.StartDelay; + } + + /// + /// Called when an aspect is started. + /// + protected override void Started(EntityUid uid, T component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + if (!TryComp(uid, out _)) + return; + + _adminLogManager.Add(LogType.AspectStarted, LogImpact.High, $"Aspect started: {ToPrettyString(uid)}"); + } + + /// + /// Called when an aspect is ended. + /// + protected override void Ended(EntityUid uid, T component, GameRuleComponent gameRule, GameRuleEndedEvent args) + { + base.Ended(uid, component, gameRule, args); + + if (!TryComp(uid, out var aspect)) + return; + + _adminLogManager.Add(LogType.AspectStopped, $"Aspect ended: {ToPrettyString(uid)}"); + + if (aspect is { Name: not null, IsHidden: false }) + { + _chatSystem.DispatchGlobalAnnouncement($"Именем аспекта являлось: {aspect.Name}", playSound: false, colorOverride: Color.LimeGreen); + } + + _audio.PlayGlobal(aspect.EndAudio, Filter.Broadcast(), true); + } + + #region Helpers + + /// + /// Forces this aspect to end prematurely. + /// + /// The entity UID on which the aspect is being performed. + /// The game rule component associated with this aspect (optional). + protected void ForceEndSelf(EntityUid uid, GameRuleComponent? component = null) + { + GameTicker.EndGameRule(uid, component); + } + + protected bool TryGetRandomStation([NotNullWhen(true)] out EntityUid? station, Func? filter = null) + { + var stations = new ValueList(); + + if (filter == null) + { + stations.EnsureCapacity(Count()); + } + + filter ??= _ => true; + var query = AllEntityQuery(); + + while (query.MoveNext(out var uid, out _)) + { + if (!filter(uid)) + continue; + + stations.Add(uid); + } + + if (stations.Count == 0) + { + station = null; + return false; + } + + station = stations[_robustRandom.Next(stations.Count)]; + return true; + } + + protected bool TryFindRandomTile(out Vector2i tile, [NotNullWhen(true)] out EntityUid? targetStation, out EntityUid targetGrid, out EntityCoordinates targetCoords) + { + tile = default; + + targetCoords = EntityCoordinates.Invalid; + if (!TryGetRandomStation(out targetStation)) + { + targetStation = EntityUid.Invalid; + targetGrid = EntityUid.Invalid; + return false; + } + var possibleTargets = Comp(targetStation.Value).Grids; + if (possibleTargets.Count == 0) + { + targetGrid = EntityUid.Invalid; + return false; + } + + targetGrid = _robustRandom.Pick(possibleTargets); + + if (!TryComp(targetGrid, out var gridComp)) + return false; + + var found = false; + var (gridPos, _, gridMatrix) = _transform.GetWorldPositionRotationMatrix(targetGrid); + var gridBounds = gridMatrix.TransformBox(gridComp.LocalAABB); + + for (var i = 0; i < 10; i++) + { + var randomX = _robustRandom.Next((int) gridBounds.Left, (int) gridBounds.Right); + var randomY = _robustRandom.Next((int) gridBounds.Bottom, (int) gridBounds.Top); + + tile = new Vector2i(randomX - (int) gridPos.X, randomY - (int) gridPos.Y); + if (_atmosphere.IsTileSpace(targetGrid, Transform(targetGrid).MapUid, tile, + mapGridComp: gridComp) + || _atmosphere.IsTileAirBlocked(targetGrid, tile, mapGridComp: gridComp)) + { + continue; + } + + found = true; + targetCoords = gridComp.GridTileToLocal(tile); + break; + } + + return found; + } + + + #endregion + + } +} diff --git a/Content.Server/White/AspectsSystem/Commands/AspectCommands.cs b/Content.Server/White/AspectsSystem/Commands/AspectCommands.cs new file mode 100644 index 0000000000..326a97a609 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Commands/AspectCommands.cs @@ -0,0 +1,145 @@ +using Content.Server.Administration; +using Content.Server.GameTicking; +using Content.Server.White.AspectsSystem.Managers; +using Content.Shared.Administration; +using Robust.Shared.Console; + +namespace Content.Server.White.AspectsSystem.Commands +{ + [AdminCommand(AdminFlags.Fun)] + public sealed class ForceAspectCommand : IConsoleCommand + { + public string Command => "forceaspect"; + public string Description => "Принудительно форсит аспект по его ID."; + public string Help => "forceaspect "; + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + var ticker = EntitySystem.Get(); + if (ticker.RunLevel != GameRunLevel.PreRoundLobby) + { + shell.WriteLine("This can only be executed while the game is in the pre-round lobby."); + return; + } + + if (args.Length != 1) + { + shell.WriteError("Использование: forceaspect "); + return; + } + + var aspectId = args[0]; + var aspectManager = EntitySystem.Get(); + var result = aspectManager.ForceAspect(aspectId); + shell.WriteLine(result); + } + } + + [AdminCommand(AdminFlags.Fun)] + public sealed class DeForceAspectCommand : IConsoleCommand + { + public string Command => "deforceaspect"; + public string Description => "Дефорсит принудительно установленный аспект."; + public string Help => "deforceaspect"; + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + var ticker = EntitySystem.Get(); + if (ticker.RunLevel != GameRunLevel.PreRoundLobby) + { + shell.WriteLine("This can only be executed while the game is in the pre-round lobby."); + return; + } + + var aspectManager = EntitySystem.Get(); + var result = aspectManager.DeForceAspect(); + shell.WriteLine(result); + } + } + + [AdminCommand(AdminFlags.Fun)] + public sealed class GetForcedAspectCommand : IConsoleCommand + { + public string Command => "getforcedaspect"; + public string Description => "Получает информацию о принудительно установленном аспекте."; + public string Help => "getforcedaspect"; + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + var ticker = EntitySystem.Get(); + if (ticker.RunLevel != GameRunLevel.PreRoundLobby) + { + shell.WriteLine("This can only be executed while the game is in the pre-round lobby."); + return; + } + + var aspectManager = EntitySystem.Get(); + var result = aspectManager.GetForcedAspect(); + shell.WriteLine(result); + } + } + + [AdminCommand(AdminFlags.Fun)] + public sealed class ListAspectsCommand : IConsoleCommand + { + public string Command => "listaspects"; + public string Description => "Список всех доступных аспектов."; + public string Help => "listaspects"; + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + var aspectManager = EntitySystem.Get(); + var aspectIds = aspectManager.GetAllAspectIds(); + + if (aspectIds.Count == 0) + { + shell.WriteLine("Нет доступных аспектов."); + } + else + { + shell.WriteLine("Список доступных аспектов:"); + foreach (var aspectId in aspectIds) + { + shell.WriteLine(aspectId); + } + } + } + } + + [AdminCommand(AdminFlags.Fun)] + public sealed class RunAspectCommand : IConsoleCommand + { + public string Command => "runaspect"; + public string Description => "Запускает аспект по его ID."; + public string Help => "runaspect "; + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 1) + { + shell.WriteError("Использование: runaspect "); + return; + } + + var aspectId = args[0]; + var aspectManager = EntitySystem.Get(); + var result = aspectManager.RunAspect(aspectId); + shell.WriteLine(result); + } + } + + [AdminCommand(AdminFlags.Fun)] + public sealed class RunRandomAspectCommand : IConsoleCommand + { + public string Command => "runrandomaspect"; + public string Description => "Запускает случайный аспект."; + public string Help => "runrandomaspect"; + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + var aspectManager = EntitySystem.Get(); + var result = aspectManager.RunRandomAspect(); + shell.WriteLine(result); + } + } +} diff --git a/Content.Server/White/AspectsSystem/Managers/AspectManager.cs b/Content.Server/White/AspectsSystem/Managers/AspectManager.cs new file mode 100644 index 0000000000..5e71323657 --- /dev/null +++ b/Content.Server/White/AspectsSystem/Managers/AspectManager.cs @@ -0,0 +1,378 @@ +using Content.Server.GameTicking; +using Content.Server.White.AspectsSystem.Base; +using Content.Shared.GameTicking; +using Content.Shared.White; +using Robust.Shared.Configuration; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.White.AspectsSystem.Managers +{ + /// + /// Manager for aspects. + /// + public sealed class AspectManager : EntitySystem + { + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + + private ISawmill _sawmill = default!; + + private bool AspectsEnabled { get; set; } + + private double Chance { get; set; } + + private void SetEnabled(bool value) => AspectsEnabled = value; + + private void SetChance(double value) => Chance = value; + + private string? ForcedAspect { get; set; } + + public override void Initialize() + { + base.Initialize(); + + _sawmill = Logger.GetSawmill("aspects"); + + _cfg.OnValueChanged(WhiteCVars.IsAspectsEnabled, SetEnabled, true); + _cfg.OnValueChanged(WhiteCVars.AspectChance, SetChance, true); + + SubscribeLocalEvent(OnRoundStarted); + SubscribeLocalEvent(OnRestart); + } + + #region Handlers + + private void OnRoundStarted(RoundStartedEvent ev) + { + if (!AspectsEnabled) + return; + + if (ForcedAspect != null) + { + RunAspect(ForcedAspect); + + ForcedAspect = null; + + return; + } + + if (_random.NextDouble() <= Chance) + { + RunRandomAspect(); + } + } + + // Put here needed cleanup. + private void OnRestart(RoundRestartCleanupEvent ev) + { + _cfg.SetCVar(WhiteCVars.DamageModifier, 1.0f); + _cfg.SetCVar(WhiteCVars.DamageGetModifier, 1.0f); + } + + #endregion + + #region PublicApi + + /// + /// Forces a specific aspect by its prototype ID. + /// + /// The prototype ID of the aspect to be forced. + public string ForceAspect(string aspectProtoId) + { + if (!AspectsEnabled) + { + var disabledStr = "Aspects disabled."; + _sawmill.Warning("Someone tried to force aspect when they disabled!"); + return disabledStr; + } + + if (!_prototype.TryIndex(aspectProtoId, out var entityPrototype)) + { + var response = "Aspect not found. Can`t find proto"; + _sawmill.Warning("Someone tried to force invalid Aspect!"); + return response; + } + + if (!entityPrototype.TryGetComponent(out _)) + { + var errStr = $"Aspect with ID '{aspectProtoId}' not found or does not have an AspectComponent!"; + _sawmill.Error(errStr); + return errStr; + } + + if (ForcedAspect == aspectProtoId) + { + var errStr = $"Aspect with ID '{aspectProtoId}' already forced!"; + _sawmill.Error(errStr); + return errStr; + } + + ForcedAspect = aspectProtoId; + + var str = $"Successfully forced Aspect with ID '{aspectProtoId}'"; + _sawmill.Info(str); + return str; + } + + /// + /// DeForces a ForcedAspect, if any. + /// + public string DeForceAspect() + { + string response; + + if (ForcedAspect != null) + { + response = $"DeForced Aspect : {ForcedAspect}"; + ForcedAspect = null; + } + else + { + response = "How to DeForce if no aspect forced, retard.."; + } + + return response; + } + + + /// + /// Retrieves information about the currently forced aspect, if any. + /// + public string GetForcedAspect() + { + var response = ForcedAspect != null + ? $"Current forced Aspect : {ForcedAspect}" + : "No forced Aspects"; + + return response; + } + + /// + /// Retrieves a list of IDs for all available aspects. + /// + /// A list of IDs for available aspects. + public List GetAllAspectIds() + { + var availableAspects = AllAspects(); + var aspectIds = new List(); + + foreach (var (proto, aspect) in availableAspects) + { + var aspectId = proto.ID; + + if (aspect.Requires != null) + { + aspectId += $" (Requires: {aspect.Requires})"; + } + + if (aspect.IsForbidden) + { + aspectId += " (ShitSpawn)"; + } + + if (ForcedAspect == aspectId) + { + aspectId += " (Forced)"; + } + + if (CheckIfAspectAlreadyRunning(aspectId)) + { + aspectId += " (Already Running)"; + } + + aspectIds.Add(aspectId); + } + + return aspectIds; + } + + /// + /// Runs the specified aspect and adds it as a game rule. + /// + /// The ID of the aspect to run. + public string RunAspect(string aspectId) + { + if (!AspectsEnabled) + { + var disabledStr = "Aspects disabled."; + _sawmill.Warning("Someone tried to run aspects when they disabled!"); + return disabledStr; + } + + if (!_prototype.TryIndex(aspectId, out var entityPrototype)) + { + var response = "Aspect not found. Can`t find proto"; + _sawmill.Warning("Someone tried to run invalid Aspect!"); + return response; + } + + if (!entityPrototype.TryGetComponent(out var aspect)) + { + var errStr = $"Aspect with ID '{aspectId}' not found or does not have an AspectComponent!"; + _sawmill.Error(errStr); + return errStr; + } + + if (CheckIfAspectAlreadyRunning(aspectId)) + { + var alreadyRunningStr = $"Aspect '{aspectId}' is already running!"; + _sawmill.Warning(alreadyRunningStr); + return alreadyRunningStr; + } + + var ent = _gameTicker.AddGameRule(aspectId); + var str = $"Ran {aspect.Name ?? "Unnamed Aspect"} ({ToPrettyString(ent)})!!"; + _sawmill.Info(str); + return str; + } + + /// + /// Runs a random aspect and adds it as a game rule. + /// + public string RunRandomAspect() + { + if (!AspectsEnabled) + { + var disabledStr = "Aspects disabled."; + _sawmill.Warning("Someone tried to run aspects when they disabled!"); + return disabledStr; + } + + var randomAspect = PickRandomAspect(); + + if (randomAspect == null) + { + var errStr = "Oopsie, no valid aspects found! Sorry."; + _sawmill.Error(errStr); + return errStr; + } + + var ent = _gameTicker.AddGameRule(randomAspect); + var str = $"Ran {ToPrettyString(ent)}!!"; + _sawmill.Info(str); + return str; + } + + #endregion + + #region Helpers + + /// + /// Picks a random aspect based on their weight. + /// + /// Allow selecting forbidden aspects. + /// The ID of the selected aspect or null if no aspect was selected. + private string? PickRandomAspect(bool allowForbidden = false) + { + var availableAspects = AllAspects(); + _sawmill.Info($"Picking from {availableAspects.Count} total available aspects"); + return FindAspect(availableAspects, allowForbidden); + } + + /// + /// Finds a suitable aspect from the available aspects. + /// + /// A dictionary of available aspects. + /// Allow selecting forbidden aspects. + /// The ID of the selected aspect or null if no aspect was found. + private string? FindAspect(Dictionary availableAspects, bool allowForbidden = false) + { + if (availableAspects.Count == 0) + { + _sawmill.Warning("No aspects were available to run!"); + return null; + } + + var sumOfWeights = 0; + + foreach (var (_, aspect) in availableAspects) + { + if (!allowForbidden && aspect.IsForbidden) + { + continue; + } + + sumOfWeights += (int)aspect.Weight; + } + + sumOfWeights = _random.Next(sumOfWeights); + + foreach (var (proto, aspect) in availableAspects) + { + if (!allowForbidden && aspect.IsForbidden) + { + continue; + } + + if (CheckIfAspectAlreadyRunning(proto.ID)) + { + continue; + } + + sumOfWeights -= (int)aspect.Weight; + + if (sumOfWeights <= 0) + { + return proto.ID; + } + } + + _sawmill.Error("Aspect was not found after weighted pick process!"); + + return null; + } + + + /// + /// Checking if aspect is already running, needed to avoid repeating. + /// + private bool CheckIfAspectAlreadyRunning(string aspectId) + { + var activeRules = _gameTicker.GetActiveGameRules(); + + foreach (var gameRule in activeRules) + { + if (!HasComp(gameRule)) + continue; + + if (!TryComp(gameRule, out var metaDataComponent)) + continue; + + var runningAspectId = metaDataComponent.EntityPrototype?.ID; + + if (runningAspectId == aspectId) + { + return true; + } + } + + return false; + } + + /// + /// Retrieves a dictionary of all available aspects from prototypes. + /// + /// A dictionary of available aspects. + private Dictionary AllAspects() + { + var allAspects = new Dictionary(); + foreach (var prototype in _prototype.EnumeratePrototypes()) + { + if (prototype.Abstract) + continue; + + if (!prototype.TryGetComponent(out var aspect)) + continue; + + allAspects.Add(prototype, aspect); + } + + return allAspects; + } + + #endregion + } +} diff --git a/Content.Server/White/Other/ChairMarkComponent.cs b/Content.Server/White/Other/ChairMarkComponent.cs new file mode 100644 index 0000000000..228b536cc1 --- /dev/null +++ b/Content.Server/White/Other/ChairMarkComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.White.Other; + +/* + * Used for mark chairs, used by I Have Two Butts But I must Seat Aspect. + */ +[RegisterComponent] +public sealed partial class ChairMarkComponent : Component +{ +} diff --git a/Content.Server/White/Other/EngineMarkComponent.cs b/Content.Server/White/Other/EngineMarkComponent.cs new file mode 100644 index 0000000000..d65ba80171 --- /dev/null +++ b/Content.Server/White/Other/EngineMarkComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.White.Other; + +/* + * Used for mark engines, used by NoEngine Aspect. + */ +[RegisterComponent] +public sealed partial class EngineMarkComponent : Component +{ +} diff --git a/Content.Server/White/Other/RandomHumanSystem/RandomHumanComponent.cs b/Content.Server/White/Other/RandomHumanSystem/RandomHumanComponent.cs new file mode 100644 index 0000000000..dfd0f39f69 --- /dev/null +++ b/Content.Server/White/Other/RandomHumanSystem/RandomHumanComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.White.Other.RandomHumanSystem; + +[RegisterComponent] +public sealed partial class RandomHumanComponent : Component +{ +} diff --git a/Content.Server/White/Other/RandomHumanSystem/RandomHumanSystem.cs b/Content.Server/White/Other/RandomHumanSystem/RandomHumanSystem.cs new file mode 100644 index 0000000000..856b37e75e --- /dev/null +++ b/Content.Server/White/Other/RandomHumanSystem/RandomHumanSystem.cs @@ -0,0 +1,49 @@ +using Content.Server.Access.Systems; +using Content.Server.Humanoid; +using Content.Server.IdentityManagement; +using Content.Server.PDA; +using Content.Shared.Access.Components; +using Content.Shared.Inventory; +using Content.Shared.PDA; +using Content.Shared.Preferences; + +namespace Content.Server.White.Other.RandomHumanSystem; + +public sealed class RandomHumanSystem : EntitySystem +{ + [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly InventorySystem _inventorySystem = default!; + [Dependency] private readonly IdCardSystem _card = default!; + [Dependency] private readonly PdaSystem _pda = default!; + [Dependency] private readonly IdentitySystem _identity = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + } + + private void OnInit(EntityUid uid, RandomHumanComponent component, ComponentInit args) + { + var newProfile = HumanoidCharacterProfile.RandomWithSpecies(); + + _humanoid.LoadProfile(uid, newProfile); + + _metaData.SetEntityName(uid, newProfile.Name); + + if (!_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid)) + return; + + if (!EntityManager.TryGetComponent(idUid, out PdaComponent? pdaComponent) || !TryComp(pdaComponent.ContainedId, out var card)) + return; + + var cardId = pdaComponent.ContainedId.Value; + + _card.TryChangeFullName(cardId, newProfile.Name, card); + _pda.SetOwner(idUid.Value, pdaComponent, newProfile.Name); + + _identity.QueueIdentityUpdate(uid); + } +} diff --git a/Content.Server/White/Other/WallMarkComponent.cs b/Content.Server/White/Other/WallMarkComponent.cs new file mode 100644 index 0000000000..72ca0228b4 --- /dev/null +++ b/Content.Server/White/Other/WallMarkComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.White.Other; + +/* + * Used for mark walls, used by WeakWalls Aspect. + */ +[RegisterComponent] +public sealed partial class WallMarkComponent : Component +{ +} diff --git a/Content.Shared.Database/LogType.cs b/Content.Shared.Database/LogType.cs index 1d5dbcc3b4..233e8c1c03 100644 --- a/Content.Shared.Database/LogType.cs +++ b/Content.Shared.Database/LogType.cs @@ -9,6 +9,9 @@ public enum LogType Healed = 3, Slip = 4, EventAnnounced = 5, + AspectAnnounced = 86, + AspectStarted = 87, + AspectStopped = 88, EventStarted = 6, EventRan = 16, EventStopped = 7, diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 9337e79439..21718e6fd6 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -8,6 +8,8 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Radiation.Events; using Content.Shared.Rejuvenate; +using Content.Shared.White; +using Robust.Shared.Configuration; using Robust.Shared.GameStates; using Robust.Shared.Network; using Robust.Shared.Prototypes; @@ -22,6 +24,12 @@ namespace Content.Shared.Damage [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly INetManager _netMan = default!; [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + + private float DamageGetModifier { get; set; } + + private void SetDamage(float value) => DamageGetModifier = value; + private EntityQuery _appearanceQuery; private EntityQuery _damageableQuery; @@ -29,6 +37,8 @@ namespace Content.Shared.Damage public override void Initialize() { + _cfg.OnValueChanged(WhiteCVars.DamageGetModifier, SetDamage, true); + SubscribeLocalEvent(DamageableInit); SubscribeLocalEvent(DamageableHandleState); SubscribeLocalEvent(DamageableGetState); @@ -139,6 +149,7 @@ namespace Content.Shared.Damage return damage; } + damage *= DamageGetModifier; var before = new BeforeDamageChangedEvent(damage, origin); RaiseLocalEvent(uid.Value, ref before); diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index c6adf00113..10e4b24ef9 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -21,15 +21,16 @@ using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; +using Content.Shared.White; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using Robust.Shared.Toolshed.Syntax; using ItemToggleMeleeWeaponComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleMeleeWeaponComponent; namespace Content.Shared.Weapons.Melee; @@ -50,6 +51,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem [Dependency] protected readonly SharedPopupSystem PopupSystem = default!; [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; [Dependency] private readonly StaminaSystem _stamina = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; public const float DamagePitchVariation = 0.05f; private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque); @@ -59,6 +61,10 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem /// public const int MaxTargets = 5; + private float DamageModifier { get; set; } + + private void SetDamage(float value) => DamageModifier = value; + /// /// If an attack is released within this buffer it's assumed to be full damage. /// @@ -68,6 +74,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem { base.Initialize(); + _cfg.OnValueChanged(WhiteCVars.DamageModifier, SetDamage, true); + SubscribeLocalEvent(OnMeleeUnpaused); SubscribeLocalEvent(OnMeleeSelected); SubscribeLocalEvent(OnMeleeShotAttempted); @@ -238,7 +246,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem var ev = new GetMeleeDamageEvent(uid, new (component.Damage), new(), user); RaiseLocalEvent(uid, ref ev); - return DamageSpecifier.ApplyModifierSets(ev.Damage, ev.Modifiers); + var modifiedDamage = ev.Damage * DamageModifier; + + return DamageSpecifier.ApplyModifierSets(modifiedDamage, ev.Modifiers); } public float GetAttackRate(EntityUid uid, EntityUid user, MeleeWeaponComponent? component = null) diff --git a/Content.Shared/White/WhiteCVars.cs b/Content.Shared/White/WhiteCVars.cs index 02f7a2e8a4..2fa38c3093 100644 --- a/Content.Shared/White/WhiteCVars.cs +++ b/Content.Shared/White/WhiteCVars.cs @@ -302,4 +302,27 @@ public sealed class WhiteCVars public static readonly CVarDef LogChatActions = CVarDef.Create("white.log_to_chat", true, CVar.CLIENT | CVar.ARCHIVE | CVar.REPLICATED); + + /* + * Aspects + */ + + public static readonly CVarDef IsAspectsEnabled = + CVarDef.Create("aspects.enabled", false, CVar.SERVERONLY); + + public static readonly CVarDef AspectChance = + CVarDef.Create("aspects.chance", 0.66, CVar.SERVERONLY); + + /* + * Damage + */ + + // Applies Projectile and Melee damage. + public static readonly CVarDef DamageModifier = + CVarDef.Create("damage.modifier", 1.0f, CVar.REPLICATED); + + // Applies ALL damage, EVEN walls and etc. + public static readonly CVarDef DamageGetModifier = + CVarDef.Create("damage.get_modifier", 1.0f, CVar.REPLICATED); + } diff --git a/Resources/Audio/White/Aspects/accent.ogg b/Resources/Audio/White/Aspects/accent.ogg new file mode 100644 index 0000000000..f5ec3cb9fc Binary files /dev/null and b/Resources/Audio/White/Aspects/accent.ogg differ diff --git a/Resources/Audio/White/Aspects/palevo.ogg b/Resources/Audio/White/Aspects/palevo.ogg new file mode 100644 index 0000000000..8599a25704 Binary files /dev/null and b/Resources/Audio/White/Aspects/palevo.ogg differ diff --git a/Resources/Prototypes/Entities/Objects/Misc/land_mine.yml b/Resources/Prototypes/Entities/Objects/Misc/land_mine.yml index 9088edc815..de78268d57 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/land_mine.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/land_mine.yml @@ -67,8 +67,8 @@ - type: ExplodeOnTrigger - type: Explosive explosionType: Default - maxIntensity: 10 - intensitySlope: 3 - totalIntensity: 120 # about a ~4 tile radius + maxIntensity: 3 + intensitySlope: 1 + totalIntensity: 120 canCreateVacuum: false - type: DeleteOnTrigger diff --git a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml index a49c211153..44cac067d6 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml @@ -48,6 +48,7 @@ path: /Audio/Effects/metalbreak.ogg - type: StaticPrice price: 10 + - type: ChairMark - type: entity name: chair diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/generator.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/generator.yml index 647eae2772..67954502f4 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/generator.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/generator.yml @@ -33,4 +33,5 @@ - type: Pullable - type: GuideHelp guides: [ Singularity, Power ] + - type: EngineMark diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index 2aea34170f..40694d918d 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -49,6 +49,7 @@ price: 75 - type: RadiationBlocker resistance: 2 + - type: WallMark - type: entity parent: BaseWall diff --git a/Resources/Prototypes/White/Aspects/Aspects.yml b/Resources/Prototypes/White/Aspects/Aspects.yml new file mode 100644 index 0000000000..a67efd753d --- /dev/null +++ b/Resources/Prototypes/White/Aspects/Aspects.yml @@ -0,0 +1,181 @@ +- type: entity + id: RandomAccentAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Акценты вне контроля" + description: "Всегда интересно, какой акцент вы услышите следующим." + weight: 3 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: RandomAccentAspect + +- type: entity + id: FastAndFuriousAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Fast and Furious" + description: "Люди спешат и не важно куда." + weight: 3 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: FastAndFuriousAspect + +- type: entity + id: RandomAppearanceAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Random appearance" + description: "Экипаж перестал узнавать друг-друга в лицо." + weight: 3 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: RandomAppearanceAspect + +- type: entity + id: BombassAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Bombass" + description: "Кто-то заложил мины на станции!" + weight: 1 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: BombassAspect + +- type: entity + id: DrunkAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Drunk" + description: "На станции стоит явный запах вчерашнего веселья... и кажется оно только начинается." + weight: 3 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: DrunkAspect + +- type: entity + id: CargoRichAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Cargo Rich" + description: "Карго работало усердно в прошлую смену, за что они и были награждены премией в размере 100000 кредитов." + weight: 2 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: CargoRichAspect + +- type: entity + id: TraitoredAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Traitored" + description: "Кто-то сдал всех предателей!" + requires: "Traitors" + weight: 1 + hidden: true + - type: TraitoredAspect + +- type: entity + id: WeakWallsAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Weak Walls" + description: "На стенах явно экономили." + weight: 2 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: WeakWallsAspect + +- type: entity + id: NoEngineAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "No engine" + description: "Какой-то смышлённый агент синдиката решил украсть все ваши генераторы энергии целиком." + weight: 2 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: NoEngineAspect + +- type: entity + id: AiRunLockAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Airunlock" + description: "Кого волнует безопасность? Экипаж свободно может ходить по всем отсекам, ведь все шлюзы теперь для них доступны." + weight: 2 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: AiRunLockAspect + +- type: entity + id: BloodyAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Bloody" + description: "В эту смену любая незначительная травма может оказаться летальной." + weight: 1 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: BloodyAspect + +- type: entity + id: WeakAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Weak" + description: "Удары стали слабее. Пули мягче. К чему это приведёт?" + weight: 1 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: WeakAspect + +- type: entity + id: BattledAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "Battled" + description: "Люди очень насторожены и готовы дать отпор в любую секунду." + weight: 1 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: BattledAspect + +- type: entity + id: ChairLeakAspect + parent: BaseGameRule + noSpawn: true + components: + - type: Aspect + name: "I Have Two Butts But I must Seat" + description: "Стулья украли!" + weight: 3 + startAudio: + path: /Audio/White/Aspects/accent.ogg + - type: ChairLeakAspect diff --git a/Resources/Prototypes/White/Entities/Structures/Supermatter/supermatter.yml b/Resources/Prototypes/White/Entities/Structures/Supermatter/supermatter.yml index d5e3f3ae05..cfb3e99417 100644 --- a/Resources/Prototypes/White/Entities/Structures/Supermatter/supermatter.yml +++ b/Resources/Prototypes/White/Entities/Structures/Supermatter/supermatter.yml @@ -64,3 +64,4 @@ maxIntensity: 10000 intensitySlope: 10 totalIntensity: 10000 + - type: EngineMark