feat: add 11 copy-paste features from ss14-wega and ss14-wl

From ss14-wega (_Wega):
- DeleteOnDrop: auto-delete items when dropped
- FriendlyFaction: prevent friendly fire by faction
- NullRod: holy weapon that removes magic
- EdibleMatter: edible entity component
- Ghost Respawn: allow ghosts to respawn to lobby
- Barks: NPC voice sounds system (99 audio files)

From ss14-wl (_WL):
- Day/Night Cycle: automatic lighting cycle for maps
- Sleep on Buckle: sleep action when buckled
- Height System: tall entities become large items
- Freeze Component: freeze entities at high cold damage
- Suckable Food: mouth-slot consumables (lollipops, gum, etc.)
- GolemHeat: bonus feature (heat mechanics for golems)

Includes:
- 34 C# files
- 99 audio files
- 68 texture files
- 9 prototype files
- 2 locale files
- WegaCVars configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-16 23:13:24 +01:00
committed by github-actions[bot]
parent ee4db851aa
commit 0f4ceb13b4
217 changed files with 3279 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
using Content.Client.Audio;
using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Speech.Synthesis;
using Robust.Client.Audio;
using Robust.Client.ResourceManagement;
using Robust.Client.Player;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Content.Shared.SoundInsolation;
namespace Content.Client.Speech.Synthesis.System;
/// <summary>
/// Система отвечающая за прогрышь звука для каждого калиента
/// </summary>
public sealed class BarkSystem : EntitySystem
{
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly SoundInsulationSystem _soundInsulation = default!;
private const float MinimalVolume = -10f;
private const float WhisperFade = 4f;
public override void Initialize()
{
SubscribeNetworkEvent<PlayBarkEvent>(OnPlayBark);
}
public void RequestPreviewBark(string barkVoiceId)
{
RaiseNetworkEvent(new RequestPreviewBarkEvent(barkVoiceId));
}
private void OnPlayBark(PlayBarkEvent ev)
{
var sourceEntity = _entityManager.GetEntity(ev.SourceUid);
if (!_entityManager.EntityExists(sourceEntity) || _entityManager.Deleted(sourceEntity) || !HasComp<TransformComponent>(sourceEntity))
return;
float volumeMultiplier = 1f;
if (_player.LocalEntity != null && HasComp<TransformComponent>(_player.LocalEntity.Value))
{
var sourceTransform = Transform(sourceEntity);
var playerTransform = Transform(_player.LocalEntity.Value);
if (sourceTransform.Coordinates.TryDistance(EntityManager, playerTransform.Coordinates, out var distance) &&
distance > SharedChatSystem.VoiceRange)
return;
var insulation = _soundInsulation.GetSoundInsulation(sourceEntity, _player.LocalEntity.Value);
if (insulation >= 0.95f)
return;
if (insulation > 0.1f && insulation < 0.95f)
{
volumeMultiplier = 1f - MathHelper.Lerp(0.1f, 0.9f, insulation);
volumeMultiplier = Math.Clamp(volumeMultiplier, 0.1f, 0.9f);
}
}
var userVolume = _cfg.GetCVar(WegaCVars.BarksVolume);
var baseVolume = SharedAudioSystem.GainToVolume(userVolume * ContentAudioSystem.BarksMultiplier);
float volume = MinimalVolume + baseVolume * volumeMultiplier;
if (ev.Obfuscated)
volume -= WhisperFade;
var audioParams = new AudioParams
{
Volume = volume,
Variation = 0.125f
};
int messageLength = ev.Message.Length;
float totalDuration = messageLength * 0.05f;
float soundInterval = 0.15f / ev.PlaybackSpeed;
int soundCount = (int)(totalDuration / soundInterval);
soundCount = Math.Max(soundCount, 1);
var audioResource = new AudioResource();
audioResource.Load(IoCManager.Instance!, new ResPath(ev.SoundPath));
var soundSpecifier = new ResolvedPathSpecifier(ev.SoundPath);
for (int i = 0; i < soundCount; i++)
{
Timer.Spawn(TimeSpan.FromSeconds(i * soundInterval), () =>
{
if (!_entityManager.EntityExists(sourceEntity) || _entityManager.Deleted(sourceEntity))
return;
_audio.PlayEntity(audioResource.AudioStream, sourceEntity, soundSpecifier, audioParams);
});
}
}
}

View File

@@ -0,0 +1,63 @@
using System.Linq;
using Content.Client.Lobby;
using Content.Shared.Speech.Synthesis;
using Content.Client.Speech.Synthesis.System;
namespace Content.Client.Lobby.UI;
public sealed partial class HumanoidProfileEditor
{
private List<BarkPrototype> _barkVoiceList = new();
private void InitializeBarkVoice()
{
_barkVoiceList = _prototypeManager
.EnumeratePrototypes<BarkPrototype>()
.Where(o => o.RoundStart)
.OrderBy(o => Loc.GetString(o.Name))
.ToList();
BarkVoiceButton.OnItemSelected += args =>
{
BarkVoiceButton.SelectId(args.Id);
SetBarkVoice(_barkVoiceList[args.Id].ID);
};
BarkVoicePlayButton.OnPressed += _ => PlayPreviewBark();
}
private void UpdateBarkVoicesControls()
{
if (Profile is null)
return;
BarkVoiceButton.Clear();
var firstVoiceChoiceId = 1;
for (var i = 0; i < _barkVoiceList.Count; i++)
{
var voice = _barkVoiceList[i];
var name = Loc.GetString(voice.Name);
BarkVoiceButton.AddItem(name, i);
if (firstVoiceChoiceId == 1)
firstVoiceChoiceId = i;
}
var voiceChoiceId = _barkVoiceList.FindIndex(x => x.ID == Profile.BarkVoice);
if (!BarkVoiceButton.TrySelectId(voiceChoiceId) &&
BarkVoiceButton.TrySelectId(firstVoiceChoiceId))
{
SetBarkVoice(_barkVoiceList[firstVoiceChoiceId].ID);
}
}
private void PlayPreviewBark()
{
if (Profile is null)
return;
_entManager.System<BarkSystem>().RequestPreviewBark(Profile.BarkVoice);
}
}

View File

@@ -0,0 +1,20 @@
using Content.Shared.Wega.Ghost.Respawn;
namespace Content.Client.Wega.Ghost.Respawn;
public sealed class GhostRespawnSystem : EntitySystem
{
public TimeSpan? GhostRespawnTime { get; private set; }
public event Action? GhostRespawn;
public override void Initialize()
{
SubscribeNetworkEvent<GhostRespawnEvent>(OnGhostRespawnReset);
}
private void OnGhostRespawnReset(GhostRespawnEvent e)
{
GhostRespawnTime = e.Time;
GhostRespawn?.Invoke();
}
}

View File

@@ -0,0 +1,7 @@
namespace Content.Server._WL.AddHeightItem;
[RegisterComponent]
public sealed partial class AddHeightItemComponent : Component
{
}

View File

@@ -0,0 +1,43 @@
using Content.Server.Resist;
using Content.Shared.Humanoid;
using Content.Shared.Item;
using Robust.Shared.Prototypes;
namespace Content.Server._WL.AddHeightItem
{
public sealed partial class AddHeightItemSystem : EntitySystem
{
[Dependency] private readonly SharedItemSystem _item = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AddHeightItemComponent, ComponentInit>(OnADHI);
}
/// <summary>
/// Add item component depending on height
/// </summary>
private void OnADHI(EntityUid uid, AddHeightItemComponent com, ComponentInit args)
{
if (!TryComp<HumanoidAppearanceComponent>(uid, out var humanoid))
return;
if (!_proto.TryIndex(humanoid.Species, out var speciesProto))
return;
if (speciesProto.MaxItemHeight >= humanoid.Height)
{
var size1 = "Ginormous";
var item = EnsureComp<ItemComponent>(uid);
_item.SetSize(uid, size1, item);
EnsureComp<MultiHandedItemComponent>(uid);
var escape = EnsureComp<CanEscapeInventoryComponent>(uid);
escape.BaseResistTime = 1f;
}
}
}
}

View File

@@ -0,0 +1,127 @@
using Content.Server._WL.DayNight;
using Content.Server.Administration;
using Content.Shared.Administration;
using Robust.Server.GameObjects;
using Robust.Shared.Console;
using Robust.Shared.Map;
using System.Linq;
using System.Numerics;
namespace Content.Server._WL.Administration.Commands
{
[AdminCommand(AdminFlags.Mapping)]
public sealed partial class DayNightCommand : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IMapManager _mapMan = default!;
public override string Command => "daynight";
public override string Description
=>
"""
Добавляет карте смену дня и ночи.
Желательно, чтоб это была планета.
Также желательно, чтобы эта команда использовалась только с неинициализированными картами.
""";
public override string Help => "daynight <mapId> <fullCycle> <dayRatio> <nightRatio> <dayColor> <nightColor>";
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
{
return CompletionResult.FromHintOptions(_mapMan.GetAllMapIds().Select(x => x.ToString()), "MapId");
}
else if (args.Length == 2)
{
return CompletionResult.FromHint("FullCycle in seconds");
}
else if (args.Length == 3)
{
return CompletionResult.FromHint("Day ratio an integer");
}
else if (args.Length == 4)
{
return CompletionResult.FromHint("Night ration an integer");
}
else if (args.Length == 5)
{
return CompletionResult.FromHint("Day Hex");
}
else if (args.Length == 6)
{
return CompletionResult.FromHint("Night Hex");
}
return CompletionResult.Empty;
}
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 6 && args.Length != 4)
{
shell.WriteError(LocalizationManager.GetString("shell-wrong-arguments-number"));
return;
}
var mapSys = _entMan.System<MapSystem>();
if (!int.TryParse(args[0], out var mapIntegerId))
{
shell.WriteError("MapId должно быть числом!");
return;
}
var mapId = new MapId(mapIntegerId);
if (!mapSys.MapExists(mapId))
{
shell.WriteError($"Карты с ID равнм {mapIntegerId} не существует!");
return;
}
if (!int.TryParse(args[1], out var fullCycleTime) || fullCycleTime <= 0)
{
shell.WriteError("fullCycleTime должен представлять целое число большее нуля!");
return;
}
if (!int.TryParse(args[2], out var dayRatio) || dayRatio <= 0)
{
shell.WriteError("dayRatio должен представлять целое число большее нуля!");
return;
}
if (!int.TryParse(args[3], out var nightRatio) || nightRatio <= 0)
{
shell.WriteError("nightRatio должен представлять целое число большее нуля!");
return;
}
if (!mapSys.TryGetMap(mapId, out var mapUid) || mapUid == null)
{
shell.WriteError("Неизвестная ошибка.");
return;
}
var dayNnightComp = _entMan.EnsureComponent<DayNightComponent>(mapUid.Value);
dayNnightComp.DayNightRatio = new Vector2(dayRatio, nightRatio);
dayNnightComp.FullCycle = TimeSpan.FromSeconds(fullCycleTime);
if (args.Length != 6)
return;
var dayColor = Color.TryFromHex(args[4]);
var nightColor = Color.TryFromHex(args[5]);
if (dayColor != null)
{
dayNnightComp.DayHex = args[4];
}
if (nightColor != null)
{
dayNnightComp.NightHex = args[5];
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Numerics;
namespace Content.Server._WL.DayNight
{
[RegisterComponent]
public sealed partial class DayNightComponent : Component
{
[ViewVariables(VVAccess.ReadOnly)]
[DataField]
public TimeSpan FullCycle = TimeSpan.FromSeconds(1200);
[ViewVariables(VVAccess.ReadOnly)]
[DataField("ratio")]
public Vector2 DayNightRatio = new(6, 4);
[ViewVariables(VVAccess.ReadOnly)]
[DataField("day")]
public string DayHex = "#F7CA68FF";
[ViewVariables(VVAccess.ReadOnly)]
[DataField("night")]
public string NightHex = "#0f1026";
[ViewVariables(VVAccess.ReadOnly)]
public bool WasInit = false;
[ViewVariables(VVAccess.ReadOnly)]
public TimeSpan NextCycle;
}
}

View File

@@ -0,0 +1,97 @@
using Robust.Server.GameObjects;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using System.Linq;
using System.Numerics;
namespace Content.Server._WL.DayNight
{
public sealed partial class DayNightSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTime = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly MapSystem _mapSys = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DayNightComponent, MapInitEvent>(OnMapInit, after: [typeof(SharedMapSystem)]);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<DayNightComponent>();
while (query.MoveNext(out var map, out var dayNightComp))
{
if (!TryComp<MapLightComponent>(map, out var mapLightComp))
continue;
if (!TryComp<MapComponent>(map, out var mapComponent))
continue;
if (!dayNightComp.WasInit || mapComponent.MapPaused)
continue;
if (_gameTime.CurTime >= dayNightComp.NextCycle)
dayNightComp.NextCycle += dayNightComp.FullCycle;
var color = CalculateColor(
_gameTime.CurTime,
dayNightComp.FullCycle,
dayNightComp.NextCycle,
Color.FromHex(dayNightComp.DayHex),
Color.FromHex(dayNightComp.NightHex),
dayNightComp.DayNightRatio);
if (color == mapLightComp.AmbientLightColor) //Оптимизация для случаев, если цикл дня и ночи огромен.
continue;
_mapSys.SetAmbientLight(mapComponent.MapId, color);
}
}
private void OnMapInit(EntityUid station, DayNightComponent comp, MapInitEvent args)
{
if (!TryComp<MapComponent>(station, out var mapComponent))
return;
_mapSys.SetAmbientLight(mapComponent.MapId, Color.FromHex(comp.DayHex));
comp.NextCycle = _gameTime.CurTime + comp.FullCycle;
comp.WasInit = true;
}
public static Color CalculateColor(TimeSpan currentTime, TimeSpan fullCycle, TimeSpan nextCycle, Color dayColor, Color nightColor, Vector2 dayNightRatio)
{
currentTime = currentTime - (nextCycle - fullCycle);
var pair = dayNightRatio.X + dayNightRatio.Y;
var dayTime = fullCycle.TotalMinutes / pair * dayNightRatio.X;
var nightTime = fullCycle.TotalMinutes / pair * dayNightRatio.Y;
var isDay = currentTime.TotalMinutes <= dayTime;
var filledPercentage = isDay
? currentTime.TotalMinutes / dayTime
: (currentTime.TotalMinutes - dayTime) / nightTime;
var r = isDay
? dayColor.R + (nightColor.R - dayColor.R) * filledPercentage
: nightColor.R + (dayColor.R - nightColor.R) * filledPercentage;
var g = isDay
? dayColor.G + (nightColor.G - dayColor.G) * filledPercentage
: nightColor.G + (dayColor.G - nightColor.G) * filledPercentage;
var b = isDay
? dayColor.B + (nightColor.B - dayColor.B) * filledPercentage
: nightColor.B + (dayColor.B - nightColor.B) * filledPercentage;
var result = new Color((float) r, (float) g, (float) b);
return result;
}
}
}

View File

@@ -0,0 +1,19 @@
using Content.Shared.Damage.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Server._WL.Destructible.Components
{
[RegisterComponent]
public sealed partial class FrozenComponent : Component
{
[DataField] public LocId FrozenPrefix = "frozen-entity-prefix";
[DataField] public LocId FrozenPopup = "frozen-entity-popup";
[DataField] public LocId FrozenHealthString = "frozen-entity-health-string";
[DataField] public string BaseName;
[DataField] public Color BaseSkinColor;
[DataField] public ProtoId<DamageTypePrototype> FrozenDamage = "Cold";
}
}

View File

@@ -0,0 +1,62 @@
using Content.Server._WL.Destructible.Components;
using Content.Server.Humanoid;
using Content.Shared.Cloning;
using Content.Shared.Cloning.Events;
using Content.Shared.Damage;
using Content.Shared.Damage.Events;
using Content.Shared.Damage.Systems;
using Content.Shared.HealthExaminable;
using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.Rejuvenate;
namespace Content.Server._WL.Destructible.Systems
{
public sealed partial class FrozenSystem : EntitySystem
{
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly HumanoidAppearanceSystem _appearance = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FrozenComponent, RefreshNameModifiersEvent>(OnRefreshName);
SubscribeLocalEvent<FrozenComponent, BeforeDamageChangedEvent>(BeforeDamageChanged);
SubscribeLocalEvent<FrozenComponent, CloningEvent>(OnClone);
SubscribeLocalEvent<FrozenComponent, HealthBeingExaminedEvent>(OnHealthExamine);
SubscribeLocalEvent<FrozenComponent, RejuvenateEvent>(OnRejuvenate);
}
private void OnRefreshName(EntityUid ent, FrozenComponent comp, RefreshNameModifiersEvent args)
{
args.AddModifier(comp.FrozenPrefix);
args.AddModifier(comp.BaseName, int.MinValue);
}
private void BeforeDamageChanged(EntityUid ent, FrozenComponent comp, ref BeforeDamageChangedEvent args)
{
args.Damage.DamageDict[comp.FrozenDamage.Id] = 0f;
args.Damage.TrimZeros();
}
private void OnClone(EntityUid ent, FrozenComponent comp, ref CloningEvent args)
{
var target = args.CloneUid;
_metaData.SetEntityName(target, comp.BaseName, raiseEvents: true);
_appearance.SetSkinColor(target, comp.BaseSkinColor);
}
private void OnHealthExamine(EntityUid ent, FrozenComponent comp, HealthBeingExaminedEvent args)
{
args.Message.AddMarkupOrThrow("\n" + Loc.GetString(comp.FrozenHealthString));
}
private void OnRejuvenate(EntityUid ent, FrozenComponent comp, RejuvenateEvent args)
{
_metaData.SetEntityName(ent, comp.BaseName, raiseEvents: true);
_appearance.SetSkinColor(ent, comp.BaseSkinColor);
RemComp<FrozenComponent>(ent);
}
}
}

View File

@@ -0,0 +1,81 @@
using Content.Server._WL.Destructible.Components;
using Content.Server.Destructible;
using Content.Server.Destructible.Thresholds.Behaviors;
using Content.Server.Humanoid;
using Content.Shared.Atmos.Rotting;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Humanoid;
using Content.Shared.IdentityManagement;
using Content.Shared.NameModifier.Components;
using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.Popups;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Enums;
namespace Content.Server._WL.Destructible.Thresholds.Behaviors
{
[UsedImplicitly]
[DataDefinition]
public sealed partial class FrozeBodyBehavior : IThresholdBehavior
{
public const float InterpolateStrength = 0.88f;
public static readonly Color InterpolateColor = Color.CadetBlue;
public void Execute(EntityUid bodyId, DestructibleSystem system, EntityUid? cause = null)
{
var entMan = system.EntityManager;
var humanoidAppearanceSys = entMan.System<HumanoidAppearanceSystem>();
var transformSys = entMan.System<TransformSystem>();
var popupSys = entMan.System<SharedPopupSystem>();
var metaDataSys = entMan.System<MetaDataSystem>();
var frozenComp = entMan.EnsureComponent<FrozenComponent>(bodyId);
//Обновляем цвет кожи
if (!entMan.TryGetComponent<HumanoidAppearanceComponent>(bodyId, out var humanoidAppearnceComp))
return;
var curColor = humanoidAppearnceComp.SkinColor;
frozenComp.BaseSkinColor = curColor;
humanoidAppearanceSys.SetSkinColor(
bodyId,
Color.InterpolateBetween(curColor, InterpolateColor, InterpolateStrength),
sync: true,
verify: false
);
//Устанавливаем префикс
var baseName = Identity.Name(bodyId, entMan);
frozenComp.BaseName = baseName;
var genderString = humanoidAppearnceComp.Gender switch
{
Gender.Male => "male",
Gender.Female => "female",
_ => "other"
};
var newName = $"{Loc.GetString(frozenComp.FrozenPrefix, ("gender", genderString))} {baseName}";
metaDataSys.SetEntityName(bodyId, newName);
//Запрещаем хил тела и разрешаем клонирование, убрав компонент гниения
entMan.RemoveComponent<PerishableComponent>(bodyId);
entMan.RemoveComponent<InjectableSolutionComponent>(bodyId);
//Поп-ап
var msg = Loc.GetString(frozenComp.FrozenPopup,
("name", baseName),
("gender", genderString));
popupSys.PopupCoordinates(
msg,
transformSys.GetMoverCoordinates(bodyId),
Robust.Shared.Player.Filter.Pvs(bodyId),
true,
PopupType.LargeCaution);
}
}
}

View File

@@ -0,0 +1,6 @@
namespace Content.Server._WL.Nutrition.Components;
[RegisterComponent]
public sealed partial class GolemHeatComponent : Component
{
}

View File

@@ -0,0 +1,39 @@
using Content.Server._WL.Nutrition.Systems;
using Content.Shared.Clothing.Components;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Server._WL.Nutrition.Components;
[RegisterComponent]
public sealed partial class SuckableFoodComponent : Component
{
[DataField]
public string Solution { get; set; } = "food";
/// <summary>
/// Количество поглощаемой из контейнера жидкости в секунду.
/// </summary>
[DataField]
public FixedPoint2 DissolveAmount { get; set; } = FixedPoint2.New(0.05f);
/// <summary>
/// Не указывайте сущности в прототипе, у которых есть <see cref="SuckableFoodComponent"/>, иначе будет runtime-ошибочка.
/// </summary>
[DataField("entityOnDissolve")]
public EntProtoId<ClothingComponent>? EquippedEntityOnDissolve { get; set; }
[DataField]
public ComponentRegistry? ComponentsOverride { get; set; }
[DataField]
public bool CanSuck { get; set; } = true;
[DataField]
public bool DeleteOnEmpty { get; set; } = true;
public bool IsSucking => SuckingEntity != null && CanSuck;
[Access(typeof(SuckableFoodSystem))]
public EntityUid? SuckingEntity;
}

View File

@@ -0,0 +1,19 @@
using Content.Server._WL.Nutrition.Components;
using Robust.Shared.Containers;
namespace Content.Server._WL.Nutrition.Events;
public sealed partial class SuckableFoodDissolvedEvent : EntityEventArgs
{
public Entity<SuckableFoodComponent> Suckable { get; }
public BaseContainer Container { get; }
public EntityUid Sucker { get; }
public SuckableFoodDissolvedEvent(Entity<SuckableFoodComponent> suckable, BaseContainer container, EntityUid sucker)
{
Suckable = suckable;
Container = container;
Sucker = sucker;
}
}

View File

@@ -0,0 +1,57 @@
using Content.Server._WL.Nutrition.Components;
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
using Content.Shared.Body.Systems;
using Content.Shared.Temperature.Components;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Nutrition.Components;
using JetBrains.Annotations;
namespace Content.Server._WL.Nutrition.Systems;
[UsedImplicitly]
public sealed class GolemHeatSystem : EntitySystem
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _systemManager = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly SharedBodySystem _bodySystem = default!;
private const int HeatChangeAmount = 4000;
private const float SprintSpeed = 3.24f;
private const float WalkSpeed = 1.8f;
private const int Acceleration = 20;
private void ChangeGolemHeat(EntityUid uid)
{
if (!_entityManager.TryGetComponent(uid, out HungerComponent? hungerComponent))
return;
if (hungerComponent.CurrentThreshold != HungerThreshold.Overfed)
{
_bodySystem.UpdateMovementSpeed(uid);
return;
}
if (!TryComp(uid, out TemperatureComponent? temperatureComponent))
return;
var temperatureSystem = _systemManager.GetEntitySystem<TemperatureSystem>();
temperatureSystem.ChangeHeat(uid, HeatChangeAmount, true, temperatureComponent);
var movementSpeed = EnsureComp<MovementSpeedModifierComponent>(uid);
_movement.ChangeBaseSpeed(uid, WalkSpeed, SprintSpeed, Acceleration, movementSpeed);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<GolemHeatComponent>();
while (query.MoveNext(out var uid, out var comp))
{
ChangeGolemHeat(uid);
}
}
}

View File

@@ -0,0 +1,189 @@
using Content.Server._WL.Nutrition.Components;
using Content.Server._WL.Nutrition.Events;
using Content.Server.Body.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Forensics;
using Content.Server.Popups;
using Content.Shared.Body.Components;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Prototypes;
using Robust.Server.Containers;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using System.Diagnostics.CodeAnalysis;
namespace Content.Server._WL.Nutrition.Systems;
public sealed partial class SuckableFoodSystem : EntitySystem
{
[Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly ForensicsSystem _forensics = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly IComponentFactory _componentFactory = default!;
[Dependency] private readonly FlavorProfileSystem _flavor = default!;
private const float UpdatePeriod = 2f; // in seconds
private float _updateTimer = 0f;
private static readonly LocId PutInMouthLoc = "food-sweets-put-in-mouth-popup-message";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SuckableFoodComponent, GotEquippedEvent>(OnEquip);
SubscribeLocalEvent<SuckableFoodComponent, GotUnequippedEvent>(ResetSucker);
SubscribeLocalEvent<SuckableFoodComponent, ComponentShutdown>(ResetSucker);
SubscribeLocalEvent<SuckableFoodComponent, SuckableFoodDissolvedEvent>(OnDissolved);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
_updateTimer += frameTime;
var isNewLoop = _updateTimer >= UpdatePeriod;
var query = EntityQueryEnumerator<SuckableFoodComponent, SolutionContainerManagerComponent>();
while (query.MoveNext(out var food, out var suckableComp, out var solContainerManComp))
{
if (!Exists(suckableComp.SuckingEntity))
{
suckableComp.SuckingEntity = null;
continue;
}
if (isNewLoop)
{
var sucker = suckableComp.SuckingEntity.Value;
if (!TryComp<BloodstreamComponent>(sucker, out var bloodstreamComp))
continue;
suckableComp.CanSuck = _mobState.IsAlive(sucker); // TODO: вынести в отдельное событие
if (!suckableComp.IsSucking)
continue;
if (!EnsureSolutionEntity((food, suckableComp, solContainerManComp), out var solutionEntity, out var solution))
continue;
var dissolvedSol = _solutionContainerSystem.SplitSolution(solutionEntity.Value, suckableComp.DissolveAmount * UpdatePeriod);
if (solution.Volume == FixedPoint2.Zero)
{
if (_container.TryGetContainingContainer(food, out var container))
{
var ev = new SuckableFoodDissolvedEvent((food, suckableComp), container, sucker);
RaiseLocalEvent(food, ev);
RaiseLocalEvent(ev);
}
continue;
}
_reactiveSystem.DoEntityReaction(sucker, dissolvedSol, ReactionMethod.Ingestion);
_bloodstreamSystem.TryAddToChemicals((sucker, bloodstreamComp), dissolvedSol);
}
}
if (isNewLoop)
_updateTimer -= UpdatePeriod;
}
public void SetState(Entity<SuckableFoodComponent> foodEnt, EntityUid? sucker)
{
var (food, comp) = foodEnt;
comp.SuckingEntity = sucker;
}
public bool EnsureSolutionEntity(
Entity<SuckableFoodComponent, SolutionContainerManagerComponent?> foodEnt,
[NotNullWhen(true)] out Entity<SolutionComponent>? solEnt,
[NotNullWhen(true)] out Solution? solution)
{
solEnt = null;
solution = null;
if (!Resolve(foodEnt, ref foodEnt.Comp2, false))
return false;
if (!_solutionContainerSystem.EnsureSolutionEntity((foodEnt, foodEnt.Comp2), foodEnt.Comp1.Solution, out var ent))
return false;
solEnt = ent;
solution = ent.Value.Comp.Solution;
return true;
}
private void OnEquip(EntityUid food, SuckableFoodComponent comp, GotEquippedEvent ev)
{
if (ev.SlotFlags.HasFlag(SlotFlags.MASK))
_forensics.TransferDna(food, ev.Equipee);
SetState((food, comp), ev.Equipee);
if (!EnsureSolutionEntity((food, comp), out _, out var sol))
return;
var flavor = _flavor.GetLocalizedFlavorsMessage(food, ev.Equipee, sol);
if (string.IsNullOrEmpty(flavor))
return;
var msg = Loc.GetString(PutInMouthLoc, ("flavor", flavor), ("entity", Identity.Name(food, EntityManager, ev.Equipee)));
_popup.PopupEntity(msg, ev.Equipee, Filter.Entities(ev.Equipee), false);
}
private void ResetSucker<T>(EntityUid food, SuckableFoodComponent comp, T ev)
{
SetState((food, comp), null);
}
private void OnDissolved(EntityUid food, SuckableFoodComponent comp, SuckableFoodDissolvedEvent ev)
{
if (comp.DeleteOnEmpty)
{
_inventory.TryUnequip(ev.Sucker, ev.Container.ID, true, true);
var msg = Loc.GetString("food-sweets-got-dissolved-popup-message", ("entity", Identity.Name(food, EntityManager)));
_popup.PopupEntity(msg, ev.Sucker, Filter.Entities(ev.Sucker), true, Shared.Popups.PopupType.Medium);
TryQueueDel(food);
}
if (comp.EquippedEntityOnDissolve != null)
{
if (_protoMan.TryIndex(comp.EquippedEntityOnDissolve.Value, out var proto)
&& proto.HasComponent<SuckableFoodComponent>(_componentFactory))
{
Log.Error($"EquippedEntityOnDissolve {comp.EquippedEntityOnDissolve.Value} on entity {ToPrettyString(food)} has {nameof(SuckableFoodComponent)}!");
return;
}
var ent = SpawnNextToOrDrop(comp.EquippedEntityOnDissolve.Value, ev.Sucker, overrides: comp.ComponentsOverride);
_inventory.TryEquip(ev.Sucker, ent, ev.Container.ID, true);
}
}
}

View File

@@ -0,0 +1,60 @@
using Content.Shared.CCVar;
using Content.Shared.Speech.Synthesis;
using Content.Shared.Speech.Synthesis.Components;
using Robust.Server.Audio;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Content.Shared.Chat;
namespace Content.Server.Speech.Synthesis.System;
/// <summary>
/// Обрабатывает барки для сущностей.
/// </summary>
public sealed class BarkSystem : EntitySystem
{
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
public override void Initialize()
{
SubscribeLocalEvent<SpeechSynthesisComponent, EntitySpokeEvent>(OnEntitySpoke);
SubscribeNetworkEvent<RequestPreviewBarkEvent>(OnRequestPreviewBark);
}
private void OnEntitySpoke(EntityUid uid, SpeechSynthesisComponent comp, EntitySpokeEvent args)
{
if (comp.VoicePrototypeId is null ||
!_prototypeManager.TryIndex<BarkPrototype>(comp.VoicePrototypeId, out var barkProto) ||
!_configurationManager.GetCVar(WegaCVars.BarksEnabled))
return;
bool isObfuscated = args.ObfuscatedMessage != null;
var sourceEntity = _entityManager.GetNetEntity(uid);
var soundPath = barkProto.SoundFiles[new Random().Next(barkProto.SoundFiles.Count)];
RaiseNetworkEvent(new PlayBarkEvent(soundPath, sourceEntity, args.Message, comp.PlaybackSpeed, isObfuscated));
}
private async void OnRequestPreviewBark(RequestPreviewBarkEvent ev, EntitySessionEventArgs args)
{
if (string.IsNullOrEmpty(ev.BarkVoiceId) || !_prototypeManager.TryIndex<BarkPrototype>(ev.BarkVoiceId, out var barkProto)
|| !_configurationManager.GetCVar(WegaCVars.BarksEnabled))
return;
var soundPath = barkProto.SoundFiles[new Random().Next(barkProto.SoundFiles.Count)];
var soundSpecifier = new SoundPathSpecifier(soundPath);
var audioParams = new AudioParams
{
Pitch = 1.0f,
Volume = 4f,
Variation = 0.125f
};
_audio.PlayGlobal(soundSpecifier, args.SenderSession, audioParams);
}
}

View File

@@ -0,0 +1,67 @@
using Content.Server.GameTicking;
using Content.Server.Mind;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Ghost;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Timing;
namespace Content.Server.Wega.Commands;
[AnyCommand()]
public sealed class GhostRespawnCommand : IConsoleCommand
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
public string Command => "ghostrespawn";
public string Description => "Allows the player to return to the lobby if they've been dead long enough, allowing re-entering the round AS ANOTHER CHARACTER.";
public string Help => $"{Command}";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (!_configurationManager.GetCVar(WegaCVars.GhostRespawnEnabled))
{
shell.WriteLine("Respawning is disabled, ask an admin to respawn you.");
return;
}
if (shell.Player is null)
{
shell.WriteLine("You cannot run this from the console!");
return;
}
if (shell.Player.AttachedEntity is null)
{
shell.WriteLine("You cannot run this in the lobby, or without an entity.");
return;
}
if (!_entityManager.TryGetComponent<GhostComponent>(shell.Player.AttachedEntity, out var ghost))
{
shell.WriteLine("You are not a ghost.");
return;
}
var mindSystem = _entityManager.EntitySysManager.GetEntitySystem<MindSystem>();
if (!mindSystem.TryGetMind(shell.Player, out _, out _))
{
shell.WriteLine("You have no mind.");
return;
}
var time = (_gameTiming.CurTime - ghost.TimeOfDeath);
var respawnTime = _configurationManager.GetCVar(WegaCVars.GhostRespawnTime);
if (respawnTime > time.TotalSeconds)
{
shell.WriteLine($"You haven't been dead long enough. You have been dead {time.TotalSeconds} seconds of the required {respawnTime}.");
return;
}
var gameTicker = _entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
gameTicker.Respawn(shell.Player);
}
}

View File

@@ -0,0 +1,41 @@
using System.Linq;
using Content.Shared.Friendly.Faction;
using Content.Shared.Mobs.Components;
using Content.Shared.Weapons.Melee.Events;
namespace Content.Server.Friendly.Faction
{
public sealed partial class FriendlyFactionSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FriendlyFactionComponent, MeleeHitEvent>(OnMeleeHit);
}
private void OnMeleeHit(EntityUid uid, FriendlyFactionComponent component, MeleeHitEvent args)
{
if (!TryComp<FriendlyFactionComponent>(args.User, out _))
return;
if (!args.HitEntities.Any())
return;
foreach (var entity in args.HitEntities)
{
if (args.User == entity)
continue;
if (!TryComp<MobStateComponent>(entity, out _))
continue;
if (TryComp<FriendlyFactionComponent>(entity, out var friendlyFaction)
&& friendlyFaction.Faction == component.Faction)
{
args.BonusDamage = -args.BaseDamage;
}
}
}
}
}

View File

@@ -0,0 +1,76 @@
using System.Runtime.InteropServices;
using Content.Shared.Wega.Ghost.Respawn;
using Content.Shared.GameTicking;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Robust.Server.Player;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Server.Wega.Ghost.Respawn;
public sealed class GhostRespawnSystem : EntitySystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private readonly Dictionary<ICommonSession, TimeSpan> _respawnResetTimes = [];
public override void Initialize()
{
SubscribeLocalEvent<MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<MindContainerComponent, MindRemovedMessage>(OnMindRemoved);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
_player.PlayerStatusChanged += OnPlayerStatusChanged;
}
private void OnMobStateChanged(MobStateChangedEvent e)
{
if (e.NewMobState != MobState.Dead)
return;
if (!_player.TryGetSessionByEntity(e.Target, out var session))
return;
ResetRespawnTime(e.Target, session);
}
private void OnMindRemoved(EntityUid entity, MindContainerComponent component, MindRemovedMessage e)
{
if (e.Mind.Comp.UserId is null)
return;
if (TryComp<MobStateComponent>(entity, out var state) && state.CurrentState == MobState.Dead)
return;
if (!_player.TryGetSessionById(e.Mind.Comp.UserId.Value, out var session))
return;
ResetRespawnTime(entity, session);
}
private void OnRoundRestartCleanup(RoundRestartCleanupEvent e)
{
_respawnResetTimes.Clear();
}
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus == Robust.Shared.Enums.SessionStatus.Connected)
SendRespawnResetTime(e.Session, GetRespawnResetTime(e.Session));
}
private void ResetRespawnTime(EntityUid entity, ICommonSession session)
{
ref var respawnTime = ref CollectionsMarshal.GetValueRefOrAddDefault(_respawnResetTimes, session, out _);
respawnTime = _timing.CurTime;
SendRespawnResetTime(session, _timing.CurTime);
}
private void SendRespawnResetTime(ICommonSession session, TimeSpan? time)
{
RaiseNetworkEvent(new GhostRespawnEvent(time), session);
}
public TimeSpan? GetRespawnResetTime(ICommonSession session)
{
return _respawnResetTimes.TryGetValue(session, out var time) ? time : null;
}
}

View File

@@ -0,0 +1,45 @@
using Content.Shared.Hands;
using Content.Shared.Interaction.Components;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory.Events;
namespace Content.Server.Interaction;
public sealed class DeleteOnDropSystem : EntitySystem
{
[Dependency] private readonly IEntityManager _entityManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DeleteOnDropComponent, GotUnequippedEvent>(OnUnequip);
SubscribeLocalEvent<DeleteOnDropComponent, GotUnequippedHandEvent>(OnUnequipHand);
SubscribeLocalEvent<DeleteOnDropComponent, DroppedEvent>(OnDropped);
}
private void OnUnequip(EntityUid uid, DeleteOnDropComponent item, GotUnequippedEvent args)
{
if (!item.DeleteOnDrop || !_entityManager.EntityExists(uid))
return;
QueueDel(uid);
}
private void OnUnequipHand(EntityUid uid, DeleteOnDropComponent item, GotUnequippedHandEvent args)
{
if (!item.DeleteOnDrop || !_entityManager.EntityExists(uid))
return;
QueueDel(uid);
}
private void OnDropped(EntityUid uid, DeleteOnDropComponent item, DroppedEvent args)
{
if (!item.DeleteOnDrop || !_entityManager.EntityExists(uid))
return;
QueueDel(uid);
}
}

View File

@@ -0,0 +1,51 @@
using Content.Server.Bible.Components;
using Content.Shared.Hands;
using Content.Shared.Inventory.Events;
using Content.Shared.NullRod.Components;
namespace Content.Server.NullRod;
public sealed class NullRodSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<NullRodComponent, GotEquippedEvent>(OnDidEquip);
SubscribeLocalEvent<NullRodComponent, GotEquippedHandEvent>(OnHandEquipped);
SubscribeLocalEvent<NullRodComponent, GotUnequippedEvent>(OnDidUnequip);
SubscribeLocalEvent<NullRodComponent, GotUnequippedHandEvent>(OnHandUnequipped);
}
private void OnDidEquip(Entity<NullRodComponent> ent, ref GotEquippedEvent args)
{
if (!HasComp<BibleUserComponent>(args.Equipee) || HasComp<NullRodOwnerComponent>(args.Equipee))
return;
EnsureComp<NullRodOwnerComponent>(args.Equipee);
}
private void OnHandEquipped(Entity<NullRodComponent> ent, ref GotEquippedHandEvent args)
{
if (!HasComp<BibleUserComponent>(args.User) || HasComp<NullRodOwnerComponent>(args.User))
return;
EnsureComp<NullRodOwnerComponent>(args.User);
}
private void OnDidUnequip(Entity<NullRodComponent> ent, ref GotUnequippedEvent args)
{
if (!HasComp<NullRodOwnerComponent>(args.Equipee))
return;
RemComp<NullRodOwnerComponent>(args.Equipee);
}
private void OnHandUnequipped(Entity<NullRodComponent> ent, ref GotUnequippedHandEvent args)
{
if (!HasComp<NullRodOwnerComponent>(args.User))
return;
RemComp<NullRodOwnerComponent>(args.User);
}
}

View File

@@ -0,0 +1,22 @@
using Robust.Shared.GameStates;
namespace Content.Shared._WL.Sleep;
/// <summary>
/// Allows entities buckled to this strap to sleep.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class SleepOnBuckleComponent : Component
{
/// <summary>
/// The sleep action entity that will be granted to buckled entities.
/// </summary>
[DataField, AutoNetworkedField]
public EntityUid? SleepAction;
/// <summary>
/// Who unbuckle entity
/// </summary>
[DataField]
public EntityUid? User;
}

View File

@@ -0,0 +1,66 @@
using Content.Shared.Actions;
using Content.Shared.Standing;
using Content.Shared.Buckle.Components;
using Content.Shared.Stunnable;
using Content.Shared.Bed.Sleep;
using Content.Shared.Actions.Components;
namespace Content.Shared._WL.Sleep;
public sealed class SleepOnBuckleSystem : EntitySystem
{
[Dependency] private readonly ActionContainerSystem _actConts = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SleepingSystem _sleepingSystem = default!;
[Dependency] protected readonly StandingStateSystem _standing = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SleepOnBuckleComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<SleepOnBuckleComponent, StrappedEvent>(OnStrapped);
SubscribeLocalEvent<SleepOnBuckleComponent, UnstrappedEvent>(OnUnstrapped);
SubscribeLocalEvent<SleepOnBuckleComponent, UnstrapAttemptEvent>(OnUnstrapAttempt);
}
private void OnMapInit(Entity<SleepOnBuckleComponent> ent, ref MapInitEvent args)
{
_actConts.EnsureAction(ent.Owner, ref ent.Comp.SleepAction, SleepingSystem.SleepActionId);
Dirty(ent);
}
private void OnStrapped(Entity<SleepOnBuckleComponent> ent, ref StrappedEvent args)
{
if (TryComp<StandingStateComponent>(args.Buckle, out var standing)
&& standing.SleepAction != null
&& TryComp<ActionComponent>(standing.SleepAction.Value, out var actionComp)
&& actionComp.AttachedEntity == args.Buckle.Owner)
_actionsSystem.RemoveAction(args.Buckle.Owner, standing.SleepAction);
_actionsSystem.AddAction(args.Buckle, ref ent.Comp.SleepAction, SleepingSystem.SleepActionId, ent);
Dirty(ent);
}
private void OnUnstrapped(Entity<SleepOnBuckleComponent> ent, ref UnstrappedEvent args)
{
if (!Terminating(args.Buckle.Owner))
{
_actionsSystem.RemoveAction(args.Buckle.Owner, ent.Comp.SleepAction);
_sleepingSystem.TryWaking(args.Buckle.Owner);
if (ent.Comp.User == args.Buckle.Owner)
{
RemComp<KnockedDownComponent>(args.Buckle.Owner);
RemComp<StunnedComponent>(args.Buckle.Owner);
_standing.Stand(args.Buckle.Owner, force: true);
}
}
}
private void OnUnstrapAttempt(Entity<SleepOnBuckleComponent> ent, ref UnstrapAttemptEvent args)
{
ent.Comp.User = args.User;
}
}

View File

@@ -0,0 +1,31 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Speech.Synthesis;
/// <summary>
/// Прототип для доступных барков.
/// </summary>
[Prototype("bark")]
public sealed class BarkPrototype : IPrototype
{
[IdDataField]
public string ID { get; } = default!;
/// <summary>
/// Название голоса.
/// </summary>
[DataField("name")]
public string Name { get; } = string.Empty;
/// <summary>
/// Набор звуков, используемых для речи.
/// </summary>
[DataField("soundFiles", required: true)]
public List<string> SoundFiles { get; } = new();
/// <summary>
/// Доступен ли на старте раунда.
/// </summary>
[DataField("roundStart")]
public bool RoundStart { get; } = true;
}

View File

@@ -0,0 +1,28 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Speech.Synthesis;
[Serializable, NetSerializable]
public sealed class RequestPreviewBarkEvent(string barkVoiceId) : EntityEventArgs
{
public string BarkVoiceId { get; } = barkVoiceId;
}
[Serializable, NetSerializable]
public sealed class PlayBarkEvent : EntityEventArgs
{
public string SoundPath { get; }
public NetEntity SourceUid { get; }
public string Message { get; }
public float PlaybackSpeed { get; }
public bool Obfuscated { get; }
public PlayBarkEvent(string soundPath, NetEntity sourceUid, string message, float playbackSpeed, bool obfuscated)
{
SoundPath = soundPath;
SourceUid = sourceUid;
Message = message;
PlaybackSpeed = playbackSpeed;
Obfuscated = obfuscated;
}
}

View File

@@ -0,0 +1,39 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Speech.Synthesis.Components;
/// <summary>
/// Применяет звуки барков для сущности.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class SpeechSynthesisComponent : Component
{
/// <summary>
/// Прототип голоса для барков.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("voice", customTypeSerializer: typeof(PrototypeIdSerializer<BarkPrototype>))]
public string? VoicePrototypeId { get; set; }
/// <summary>
/// Скорость воспроизведения звука.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("playbackSpeed")]
public float PlaybackSpeed { get; set; } = 1.0f;
/// <summary>
/// Тональность звука.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("pitch")]
public float Pitch { get; set; } = 1.0f;
/// <summary>
/// Выразительность речи.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("expression")]
public float Expression { get; set; } = 1.0f;
}

View File

@@ -0,0 +1,121 @@
using Robust.Shared.Configuration;
namespace Content.Shared.CCVar;
[CVarDefs]
public sealed class WegaCVars
{
/*
Ghost Respawn CVars
*/
/// <summary>
/// Whether or not respawning is enabled.
/// </summary>
public static readonly CVarDef<bool> GhostRespawnEnabled =
CVarDef.Create("wega.respawn_enabled", false, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Respawn time, how long the player has to wait in seconds after death.
/// </summary>
public static readonly CVarDef<float> GhostRespawnTime =
CVarDef.Create("wega.respawn_time", 1200.0f, CVar.SERVER | CVar.REPLICATED);
/*
Barks CVars
*/
/// <summary>
/// Responsible for turning on and off the bark system.
/// </summary>
public static readonly CVarDef<bool> BarksEnabled =
CVarDef.Create("wega.barks_enabled", false, CVar.SERVER | CVar.REPLICATED | CVar.ARCHIVE);
/// <summary>
/// Default volume setting of Barks sound.
/// </summary>
public static readonly CVarDef<float> BarksVolume =
CVarDef.Create("wega.barks_volume", 0f, CVar.CLIENTONLY | CVar.ARCHIVE);
/*
Night Light System CVars
*/
/// <summary>
/// Responsible for switching the night light system.
/// </summary>
public static readonly CVarDef<bool> NightLightEnabled =
CVarDef.Create("wega.night_light_enabled", false, CVar.SERVER | CVar.REPLICATED | CVar.ARCHIVE);
/// <summary>
/// Switching adjusts all the lamps to the holiday mode according to the logic of updating the night lighting.
/// </summary>
public static readonly CVarDef<bool> PartyEnabled =
CVarDef.Create("wega.party_enabled", false, CVar.SERVER | CVar.REPLICATED | CVar.ARCHIVE);
/*
Sound insulation CVars
*/
/// <summary>
/// If you enable this mode, it will process the sound with sound isolation.
/// </summary>
public static readonly CVarDef<bool> SoundInsulationEnabled =
CVarDef.Create("wega.sound_insulation_enabled", false, CVar.SERVER | CVar.REPLICATED | CVar.ARCHIVE);
/*
Vote CVars
*/
/// <summary>
/// If enabled forcibly, it will trigger a vote for the mode at the end of the round.
/// </summary>
public static readonly CVarDef<bool> VoteRoundEndEnabled =
CVarDef.Create("wega.roundend_vote_enabled", false, CVar.SERVERONLY);
/*
Ic Flavors
*/
/// <summary>
/// Sets the maximum length for OOC flavor text.
/// </summary>
public static readonly CVarDef<int> OOCMaxFlavorTextLength =
CVarDef.Create("ic.oocflavor_text_length", 2048, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Sets the maximum length for character description text.
/// </summary>
public static readonly CVarDef<int> CharacterDescriptionLength =
CVarDef.Create("ic.character_description_length", 2048, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Sets the maximum length for green preferences text.
/// </summary>
public static readonly CVarDef<int> GreenPreferencesLength =
CVarDef.Create("ic.green_preferences_length", 256, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Sets the maximum length for yellow preferences text.
/// </summary>
public static readonly CVarDef<int> YellowPreferencesLength =
CVarDef.Create("ic.yellow_preferences_length", 256, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Sets the maximum length for red preferences text.
/// </summary>
public static readonly CVarDef<int> RedPreferencesLength =
CVarDef.Create("ic.red_preferences_length", 256, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Sets the maximum length for tags text.
/// </summary>
public static readonly CVarDef<int> TagsLength =
CVarDef.Create("ic.tags_length", 128, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Sets the maximum length for links text.
/// </summary>
public static readonly CVarDef<int> LinksLength =
CVarDef.Create("ic.links_length", 512, CVar.SERVER | CVar.REPLICATED);
/// <summary>
/// Sets the maximum length for NSFW preferences text.
/// </summary>
public static readonly CVarDef<int> NSFWPreferencesLength =
CVarDef.Create("ic.nsfw_preferences_length", 1024, CVar.SERVER | CVar.REPLICATED);
}

View File

@@ -0,0 +1,11 @@
namespace Content.Shared.Edible.Matter;
[RegisterComponent]
public sealed partial class EdibleMatterComponent : Component
{
[DataField("nutritionValue")]
public float NutritionValue = 5f;
[DataField("canBeEaten")]
public bool CanBeEaten = true;
}

View File

@@ -0,0 +1,11 @@
using Content.Shared.NPC.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Shared.Friendly.Faction;
[RegisterComponent]
public sealed partial class FriendlyFactionComponent : Component
{
[DataField]
public ProtoId<NpcFactionPrototype>? Faction;
}

View File

@@ -0,0 +1,9 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Wega.Ghost.Respawn;
[Serializable, NetSerializable]
public sealed class GhostRespawnEvent(TimeSpan? time) : EntityEventArgs
{
public readonly TimeSpan? Time = time;
}

View File

@@ -0,0 +1,11 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Interaction.Components
{
[RegisterComponent, NetworkedComponent]
public sealed partial class DeleteOnDropComponent : Component
{
[DataField("deleteOnDrop")]
public bool DeleteOnDrop = true;
}
}

View File

@@ -0,0 +1,13 @@
using Content.Shared.FixedPoint;
namespace Content.Shared.NullRod.Components;
[RegisterComponent]
public sealed partial class NullRodComponent : Component
{
[DataField]
public FixedPoint2 FirstNullDamage = 30;
[DataField]
public FixedPoint2 NullDamage = 15;
}

View File

@@ -0,0 +1,6 @@
using Content.Shared.FixedPoint;
namespace Content.Shared.NullRod.Components;
[RegisterComponent]
public sealed partial class NullRodOwnerComponent : Component;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More