Aspects (#399)
* base * remove redudant shit * implement needed * doc * manager * Commands * Update AspectManager.cs * oops, hardcoded! * fix * похуй * да похуй * увфывфывфывфыв * Update AspectManager.cs * RandomAccentAspect * avoid repeating * FastAndFuriousAccent * Update Aspects.yml * Update FastandFuriousAspect.cs * RandomAppearanceAspect * helpers and some shit * Bombassssssss * DrunkAspect * CargoRich * TraitoredAspect * require info and feature to force aspect * add exec only in lobby * deforce command * Update TraitoredAspect.cs * Prepare for Bloody and Weak Aspects. * comments * WeakWallsAspect * tweak * NoEngineAspect * airunlock aspect * BloodyAspect * WeakAspect * BattledAspect * I Have Two Butts But I must Seat * веса * Update WhiteCVars.cs
This commit is contained in:
@@ -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<CargoOrderConsoleComponent>();
|
||||
|
||||
@@ -340,6 +340,39 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
|
||||
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<TraitorRuleComponent>();
|
||||
|
||||
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)));
|
||||
|
||||
@@ -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<ProjectileComponent, StartCollideEvent>(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))
|
||||
|
||||
@@ -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<AiRunLockAspectComponent>
|
||||
{
|
||||
protected override void Started(EntityUid uid, AiRunLockAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
var query = EntityQueryEnumerator<AccessReaderComponent>();
|
||||
while (query.MoveNext(out _, out var accessReaderComponent))
|
||||
{
|
||||
accessReaderComponent.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
Content.Server/White/AspectsSystem/Aspects/BattledAspect.cs
Normal file
87
Content.Server/White/AspectsSystem/Aspects/BattledAspect.cs
Normal file
@@ -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<BattledAspectComponent>
|
||||
{
|
||||
[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<PlayerSpawnCompleteEvent>(HandleLateJoin);
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, BattledAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
var prototypes = _prototypeManager.EnumeratePrototypes<NonPeacefulRoundItemsPrototype>().ToList();
|
||||
|
||||
if (prototypes.Count == 0)
|
||||
ForceEndSelf(uid, gameRule);
|
||||
|
||||
_nonPeacefulRoundItemsPrototype = _robustRandom.Pick(prototypes);
|
||||
|
||||
var query = EntityQueryEnumerator<HumanoidAppearanceComponent>();
|
||||
while (query.MoveNext(out var ent, out _))
|
||||
{
|
||||
GiveItem(ent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void HandleLateJoin(PlayerSpawnCompleteEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<BattledAspectComponent, GameRuleComponent>();
|
||||
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<TransformComponent>(player);
|
||||
|
||||
if(transform == null)
|
||||
return;
|
||||
|
||||
if(!HasComp<HandsComponent>(player))
|
||||
return;
|
||||
|
||||
var weaponEntity = EntityManager.SpawnEntity(item, transform.Coordinates);
|
||||
|
||||
_handsSystem.TryDrop(player);
|
||||
_handsSystem.PickupOrDrop(player, weaponEntity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
26
Content.Server/White/AspectsSystem/Aspects/BloodyAspect.cs
Normal file
26
Content.Server/White/AspectsSystem/Aspects/BloodyAspect.cs
Normal file
@@ -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<BloodyAspectComponent>
|
||||
{
|
||||
[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);
|
||||
}
|
||||
}
|
||||
31
Content.Server/White/AspectsSystem/Aspects/BombassAspect.cs
Normal file
31
Content.Server/White/AspectsSystem/Aspects/BombassAspect.cs
Normal file
@@ -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<BombassAspectComponent>
|
||||
{
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<CargoRichAspectComponent>
|
||||
{
|
||||
[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<StationBankAccountComponent>(station, out var stationBankAccountComponent))
|
||||
return;
|
||||
|
||||
_cargoSystem.UpdateBankAccount(station, stationBankAccountComponent, 100000);
|
||||
}
|
||||
}
|
||||
@@ -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<ChairLeakAspectComponent>
|
||||
{
|
||||
protected override void Started(EntityUid uid, ChairLeakAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
var query = EntityQueryEnumerator<ChairMarkComponent>();
|
||||
while (query.MoveNext(out var ent, out _))
|
||||
{
|
||||
EntityManager.DeleteEntity(ent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class AiRunLockAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class BattledAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class BloodyAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class BombassAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CargoRichAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class ChairLeakAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class DrunkAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class FastAndFuriousAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class NoEngineAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class RandomAccentAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class RandomAppearanceAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class TraitoredAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class WeakAspectComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.AspectsSystem.Aspects.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class WeakWallsAspectComponent : Component
|
||||
{
|
||||
}
|
||||
48
Content.Server/White/AspectsSystem/Aspects/DrunkAspect.cs
Normal file
48
Content.Server/White/AspectsSystem/Aspects/DrunkAspect.cs
Normal file
@@ -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<DrunkAspectComponent>
|
||||
{
|
||||
[Dependency] private readonly SharedDrunkSystem _drunkSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(HandleLateJoin);
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, DrunkAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
var query = EntityQueryEnumerator<HumanoidAppearanceComponent>();
|
||||
while (query.MoveNext(out var ent, out _))
|
||||
{
|
||||
_drunkSystem.TryApplyDrunkenness(ent, 50);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleLateJoin(PlayerSpawnCompleteEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<DrunkAspectComponent, GameRuleComponent>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<FastAndFuriousAspectComponent>
|
||||
{
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(HandleLateJoin);
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, FastAndFuriousAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
var query = EntityQueryEnumerator<MovementSpeedModifierComponent>();
|
||||
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<MovementSpeedModifierComponent>();
|
||||
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<FastAndFuriousAspectComponent, GameRuleComponent>();
|
||||
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<MovementSpeedModifierComponent>(mob, out var speedModifierComponent))
|
||||
return;
|
||||
|
||||
_movementSystem.ChangeBaseSpeed(mob, speedModifierComponent.BaseWalkSpeed + 2.5f, speedModifierComponent.BaseSprintSpeed + 4, speedModifierComponent.Acceleration);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Content.Server/White/AspectsSystem/Aspects/NoEngineAspect.cs
Normal file
21
Content.Server/White/AspectsSystem/Aspects/NoEngineAspect.cs
Normal file
@@ -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<NoEngineAspectComponent>
|
||||
{
|
||||
protected override void Started(EntityUid uid, NoEngineAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
var query = EntityQueryEnumerator<EngineMarkComponent>();
|
||||
while (query.MoveNext(out var ent, out _))
|
||||
{
|
||||
EntityManager.DeleteEntity(ent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
107
Content.Server/White/AspectsSystem/Aspects/RandomAccentAspect.cs
Normal file
107
Content.Server/White/AspectsSystem/Aspects/RandomAccentAspect.cs
Normal file
@@ -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<RandomAccentAspectComponent>
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(HandleLateJoin);
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, RandomAccentAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
var query = EntityQueryEnumerator<MindContainerComponent>();
|
||||
while (query.MoveNext(out var ent, out _))
|
||||
{
|
||||
ApplyRandomAccent(ent);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleLateJoin(PlayerSpawnCompleteEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<RandomAccentAspectComponent, GameRuleComponent>();
|
||||
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<AccentType>().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<StutteringAccentComponent>(uid);
|
||||
break;
|
||||
case AccentType.Spanish:
|
||||
EntityManager.EnsureComponent<SpanishAccentComponent>(uid);
|
||||
break;
|
||||
case AccentType.Slurred:
|
||||
EntityManager.EnsureComponent<SlurredAccentComponent>(uid);
|
||||
break;
|
||||
case AccentType.Scrambled:
|
||||
EntityManager.EnsureComponent<ScrambledAccentComponent>(uid);
|
||||
break;
|
||||
case AccentType.Pirate:
|
||||
EntityManager.EnsureComponent<PirateAccentComponent>(uid);
|
||||
break;
|
||||
case AccentType.Russian:
|
||||
EntityManager.EnsureComponent<RussianAccentComponent>(uid);
|
||||
break;
|
||||
case AccentType.Anime:
|
||||
EntityManager.EnsureComponent<OwOAccentComponent>(uid);
|
||||
break;
|
||||
case AccentType.Lizard:
|
||||
EntityManager.EnsureComponent<LizardAccentComponent>(uid);
|
||||
break;
|
||||
case AccentType.Backwards:
|
||||
EntityManager.EnsureComponent<BackwardsAccentComponent>(uid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -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<RandomAppearanceAspectComponent>
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(HandleLateJoin);
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, RandomAppearanceAspectComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
var query = EntityQueryEnumerator<HumanoidAppearanceComponent>();
|
||||
while (query.MoveNext(out var ent, out _))
|
||||
{
|
||||
EnsureComp<RandomHumanComponent>(ent);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleLateJoin(PlayerSpawnCompleteEvent ev)
|
||||
{
|
||||
var query = EntityQueryEnumerator<RandomAppearanceAspectComponent, GameRuleComponent>();
|
||||
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<RandomHumanComponent>(mob);
|
||||
}
|
||||
}
|
||||
}
|
||||
130
Content.Server/White/AspectsSystem/Aspects/TraitoredAspect.cs
Normal file
130
Content.Server/White/AspectsSystem/Aspects/TraitoredAspect.cs
Normal file
@@ -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<TraitoredAspectComponent>
|
||||
{
|
||||
[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<TraitorRuleComponent>().Any();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
26
Content.Server/White/AspectsSystem/Aspects/WeakAspect.cs
Normal file
26
Content.Server/White/AspectsSystem/Aspects/WeakAspect.cs
Normal file
@@ -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<WeakAspectComponent>
|
||||
{
|
||||
[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);
|
||||
}
|
||||
}
|
||||
@@ -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<WeakWallsAspectComponent>
|
||||
{
|
||||
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<WallMarkComponent>();
|
||||
while (query.MoveNext(out var ent, out _))
|
||||
{
|
||||
if (!TryComp<DestructibleComponent>(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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
39
Content.Server/White/AspectsSystem/Base/AspectComponent.cs
Normal file
39
Content.Server/White/AspectsSystem/Base/AspectComponent.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
211
Content.Server/White/AspectsSystem/Base/AspectSystem.cs
Normal file
211
Content.Server/White/AspectsSystem/Base/AspectSystem.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for aspect systems.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of component to which the system is applied.</typeparam>
|
||||
public abstract class AspectSystem<T> : GameRuleSystem<T> 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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every tick when this aspect is running.
|
||||
/// </summary>
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<AspectComponent, GameRuleComponent>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when an aspect is added to an entity.
|
||||
/// </summary>
|
||||
protected override void Added(EntityUid uid, T component, GameRuleComponent gameRule, GameRuleAddedEvent args)
|
||||
{
|
||||
base.Added(uid, component, gameRule, args);
|
||||
|
||||
if (!TryComp<AspectComponent>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when an aspect is started.
|
||||
/// </summary>
|
||||
protected override void Started(EntityUid uid, T component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
if (!TryComp<AspectComponent>(uid, out _))
|
||||
return;
|
||||
|
||||
_adminLogManager.Add(LogType.AspectStarted, LogImpact.High, $"Aspect started: {ToPrettyString(uid)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when an aspect is ended.
|
||||
/// </summary>
|
||||
protected override void Ended(EntityUid uid, T component, GameRuleComponent gameRule, GameRuleEndedEvent args)
|
||||
{
|
||||
base.Ended(uid, component, gameRule, args);
|
||||
|
||||
if (!TryComp<AspectComponent>(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
|
||||
|
||||
/// <summary>
|
||||
/// Forces this aspect to end prematurely.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity UID on which the aspect is being performed.</param>
|
||||
/// <param name="component">The game rule component associated with this aspect (optional).</param>
|
||||
protected void ForceEndSelf(EntityUid uid, GameRuleComponent? component = null)
|
||||
{
|
||||
GameTicker.EndGameRule(uid, component);
|
||||
}
|
||||
|
||||
protected bool TryGetRandomStation([NotNullWhen(true)] out EntityUid? station, Func<EntityUid, bool>? filter = null)
|
||||
{
|
||||
var stations = new ValueList<EntityUid>();
|
||||
|
||||
if (filter == null)
|
||||
{
|
||||
stations.EnsureCapacity(Count<StationEventEligibleComponent>());
|
||||
}
|
||||
|
||||
filter ??= _ => true;
|
||||
var query = AllEntityQuery<StationEventEligibleComponent>();
|
||||
|
||||
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<StationDataComponent>(targetStation.Value).Grids;
|
||||
if (possibleTargets.Count == 0)
|
||||
{
|
||||
targetGrid = EntityUid.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
targetGrid = _robustRandom.Pick(possibleTargets);
|
||||
|
||||
if (!TryComp<MapGridComponent>(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
|
||||
|
||||
}
|
||||
}
|
||||
145
Content.Server/White/AspectsSystem/Commands/AspectCommands.cs
Normal file
145
Content.Server/White/AspectsSystem/Commands/AspectCommands.cs
Normal file
@@ -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 <aspectId>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var ticker = EntitySystem.Get<GameTicker>();
|
||||
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 <aspectId>");
|
||||
return;
|
||||
}
|
||||
|
||||
var aspectId = args[0];
|
||||
var aspectManager = EntitySystem.Get<AspectManager>();
|
||||
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<GameTicker>();
|
||||
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<AspectManager>();
|
||||
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<GameTicker>();
|
||||
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<AspectManager>();
|
||||
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<AspectManager>();
|
||||
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 <aspectId>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteError("Использование: runaspect <aspectId>");
|
||||
return;
|
||||
}
|
||||
|
||||
var aspectId = args[0];
|
||||
var aspectManager = EntitySystem.Get<AspectManager>();
|
||||
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<AspectManager>();
|
||||
var result = aspectManager.RunRandomAspect();
|
||||
shell.WriteLine(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
378
Content.Server/White/AspectsSystem/Managers/AspectManager.cs
Normal file
378
Content.Server/White/AspectsSystem/Managers/AspectManager.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Manager for aspects.
|
||||
/// </summary>
|
||||
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<RoundStartedEvent>(OnRoundStarted);
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(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
|
||||
|
||||
/// <summary>
|
||||
/// Forces a specific aspect by its prototype ID.
|
||||
/// </summary>
|
||||
/// <param name="aspectProtoId">The prototype ID of the aspect to be forced.</param>
|
||||
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<EntityPrototype>(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<AspectComponent>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeForces a ForcedAspect, if any.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves information about the currently forced aspect, if any.
|
||||
/// </summary>
|
||||
public string GetForcedAspect()
|
||||
{
|
||||
var response = ForcedAspect != null
|
||||
? $"Current forced Aspect : {ForcedAspect}"
|
||||
: "No forced Aspects";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of IDs for all available aspects.
|
||||
/// </summary>
|
||||
/// <returns>A list of IDs for available aspects.</returns>
|
||||
public List<string> GetAllAspectIds()
|
||||
{
|
||||
var availableAspects = AllAspects();
|
||||
var aspectIds = new List<string>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the specified aspect and adds it as a game rule.
|
||||
/// </summary>
|
||||
/// <param name="aspectId">The ID of the aspect to run.</param>
|
||||
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<EntityPrototype>(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<AspectComponent>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a random aspect and adds it as a game rule.
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Picks a random aspect based on their weight.
|
||||
/// </summary>
|
||||
/// <param name="allowForbidden">Allow selecting forbidden aspects.</param>
|
||||
/// <returns>The ID of the selected aspect or null if no aspect was selected.</returns>
|
||||
private string? PickRandomAspect(bool allowForbidden = false)
|
||||
{
|
||||
var availableAspects = AllAspects();
|
||||
_sawmill.Info($"Picking from {availableAspects.Count} total available aspects");
|
||||
return FindAspect(availableAspects, allowForbidden);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a suitable aspect from the available aspects.
|
||||
/// </summary>
|
||||
/// <param name="availableAspects">A dictionary of available aspects.</param>
|
||||
/// <param name="allowForbidden">Allow selecting forbidden aspects.</param>
|
||||
/// <returns>The ID of the selected aspect or null if no aspect was found.</returns>
|
||||
private string? FindAspect(Dictionary<EntityPrototype, AspectComponent> 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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checking if aspect is already running, needed to avoid repeating.
|
||||
/// </summary>
|
||||
private bool CheckIfAspectAlreadyRunning(string aspectId)
|
||||
{
|
||||
var activeRules = _gameTicker.GetActiveGameRules();
|
||||
|
||||
foreach (var gameRule in activeRules)
|
||||
{
|
||||
if (!HasComp<AspectComponent>(gameRule))
|
||||
continue;
|
||||
|
||||
if (!TryComp<MetaDataComponent>(gameRule, out var metaDataComponent))
|
||||
continue;
|
||||
|
||||
var runningAspectId = metaDataComponent.EntityPrototype?.ID;
|
||||
|
||||
if (runningAspectId == aspectId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a dictionary of all available aspects from prototypes.
|
||||
/// </summary>
|
||||
/// <returns>A dictionary of available aspects.</returns>
|
||||
private Dictionary<EntityPrototype, AspectComponent> AllAspects()
|
||||
{
|
||||
var allAspects = new Dictionary<EntityPrototype, AspectComponent>();
|
||||
foreach (var prototype in _prototype.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (prototype.Abstract)
|
||||
continue;
|
||||
|
||||
if (!prototype.TryGetComponent<AspectComponent>(out var aspect))
|
||||
continue;
|
||||
|
||||
allAspects.Add(prototype, aspect);
|
||||
}
|
||||
|
||||
return allAspects;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
9
Content.Server/White/Other/ChairMarkComponent.cs
Normal file
9
Content.Server/White/Other/ChairMarkComponent.cs
Normal file
@@ -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
|
||||
{
|
||||
}
|
||||
9
Content.Server/White/Other/EngineMarkComponent.cs
Normal file
9
Content.Server/White/Other/EngineMarkComponent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Content.Server.White.Other;
|
||||
|
||||
/*
|
||||
* Used for mark engines, used by NoEngine Aspect.
|
||||
*/
|
||||
[RegisterComponent]
|
||||
public sealed partial class EngineMarkComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server.White.Other.RandomHumanSystem;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class RandomHumanComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -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<RandomHumanComponent, ComponentInit>(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<IdCardComponent>(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);
|
||||
}
|
||||
}
|
||||
9
Content.Server/White/Other/WallMarkComponent.cs
Normal file
9
Content.Server/White/Other/WallMarkComponent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Content.Server.White.Other;
|
||||
|
||||
/*
|
||||
* Used for mark walls, used by WeakWalls Aspect.
|
||||
*/
|
||||
[RegisterComponent]
|
||||
public sealed partial class WallMarkComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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<AppearanceComponent> _appearanceQuery;
|
||||
private EntityQuery<DamageableComponent> _damageableQuery;
|
||||
@@ -29,6 +37,8 @@ namespace Content.Shared.Damage
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_cfg.OnValueChanged(WhiteCVars.DamageGetModifier, SetDamage, true);
|
||||
|
||||
SubscribeLocalEvent<DamageableComponent, ComponentInit>(DamageableInit);
|
||||
SubscribeLocalEvent<DamageableComponent, ComponentHandleState>(DamageableHandleState);
|
||||
SubscribeLocalEvent<DamageableComponent, ComponentGetState>(DamageableGetState);
|
||||
@@ -139,6 +149,7 @@ namespace Content.Shared.Damage
|
||||
return damage;
|
||||
}
|
||||
|
||||
damage *= DamageGetModifier;
|
||||
var before = new BeforeDamageChangedEvent(damage, origin);
|
||||
RaiseLocalEvent(uid.Value, ref before);
|
||||
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
public const int MaxTargets = 5;
|
||||
|
||||
private float DamageModifier { get; set; }
|
||||
|
||||
private void SetDamage(float value) => DamageModifier = value;
|
||||
|
||||
/// <summary>
|
||||
/// If an attack is released within this buffer it's assumed to be full damage.
|
||||
/// </summary>
|
||||
@@ -68,6 +74,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_cfg.OnValueChanged(WhiteCVars.DamageModifier, SetDamage, true);
|
||||
|
||||
SubscribeLocalEvent<MeleeWeaponComponent, EntityUnpausedEvent>(OnMeleeUnpaused);
|
||||
SubscribeLocalEvent<MeleeWeaponComponent, HandSelectedEvent>(OnMeleeSelected);
|
||||
SubscribeLocalEvent<MeleeWeaponComponent, ShotAttemptedEvent>(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)
|
||||
|
||||
@@ -302,4 +302,27 @@ public sealed class WhiteCVars
|
||||
|
||||
public static readonly CVarDef<bool> LogChatActions =
|
||||
CVarDef.Create("white.log_to_chat", true, CVar.CLIENT | CVar.ARCHIVE | CVar.REPLICATED);
|
||||
|
||||
/*
|
||||
* Aspects
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<bool> IsAspectsEnabled =
|
||||
CVarDef.Create("aspects.enabled", false, CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<double> AspectChance =
|
||||
CVarDef.Create("aspects.chance", 0.66, CVar.SERVERONLY);
|
||||
|
||||
/*
|
||||
* Damage
|
||||
*/
|
||||
|
||||
// Applies Projectile and Melee damage.
|
||||
public static readonly CVarDef<float> DamageModifier =
|
||||
CVarDef.Create("damage.modifier", 1.0f, CVar.REPLICATED);
|
||||
|
||||
// Applies ALL damage, EVEN walls and etc.
|
||||
public static readonly CVarDef<float> DamageGetModifier =
|
||||
CVarDef.Create("damage.get_modifier", 1.0f, CVar.REPLICATED);
|
||||
|
||||
}
|
||||
|
||||
BIN
Resources/Audio/White/Aspects/accent.ogg
Normal file
BIN
Resources/Audio/White/Aspects/accent.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Aspects/palevo.ogg
Normal file
BIN
Resources/Audio/White/Aspects/palevo.ogg
Normal file
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
path: /Audio/Effects/metalbreak.ogg
|
||||
- type: StaticPrice
|
||||
price: 10
|
||||
- type: ChairMark
|
||||
|
||||
- type: entity
|
||||
name: chair
|
||||
|
||||
@@ -33,4 +33,5 @@
|
||||
- type: Pullable
|
||||
- type: GuideHelp
|
||||
guides: [ Singularity, Power ]
|
||||
- type: EngineMark
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
price: 75
|
||||
- type: RadiationBlocker
|
||||
resistance: 2
|
||||
- type: WallMark
|
||||
|
||||
- type: entity
|
||||
parent: BaseWall
|
||||
|
||||
181
Resources/Prototypes/White/Aspects/Aspects.yml
Normal file
181
Resources/Prototypes/White/Aspects/Aspects.yml
Normal file
@@ -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
|
||||
@@ -64,3 +64,4 @@
|
||||
maxIntensity: 10000
|
||||
intensitySlope: 10
|
||||
totalIntensity: 10000
|
||||
- type: EngineMark
|
||||
|
||||
Reference in New Issue
Block a user