diff --git a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml.cs b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml.cs index 0859bd5ca9..633152d992 100644 --- a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml.cs +++ b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml.cs @@ -373,7 +373,7 @@ public sealed partial class AdminLogsControl : Control for (var i = 0; i < impacts.Length - 1; i++) { - LogImpactContainer.GetChild(i).StyleClasses.Add("ButtonSquare"); + LogImpactContainer.GetChild(i).StyleClasses.Add(StyleClass.ButtonSquare); } LogImpactContainer.GetChild(LogImpactContainer.ChildCount - 1).StyleClasses.Add("OpenLeft"); diff --git a/Content.Client/Botany/PlantAnalyzer/PlantAnalyzerWindow.xaml.cs b/Content.Client/Botany/PlantAnalyzer/PlantAnalyzerWindow.xaml.cs index 8ea8b4f26b..ffa4d53aaa 100644 --- a/Content.Client/Botany/PlantAnalyzer/PlantAnalyzerWindow.xaml.cs +++ b/Content.Client/Botany/PlantAnalyzer/PlantAnalyzerWindow.xaml.cs @@ -5,7 +5,6 @@ using Content.Shared.IdentityManagement; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Client.Botany.PlantAnalyzer; @@ -14,8 +13,8 @@ namespace Content.Client.Botany.PlantAnalyzer; public sealed partial class PlantAnalyzerWindow : FancyWindow { private readonly IEntityManager _entityManager; - private readonly IPrototypeManager _prototypeManager; private readonly IGameTiming _gameTiming; + private readonly PlantAnalyzerLocalizationHelper _localizationHelper; public PlantAnalyzerWindow() { @@ -23,8 +22,8 @@ public sealed partial class PlantAnalyzerWindow : FancyWindow var dependencies = IoCManager.Instance!; _entityManager = dependencies.Resolve(); - _prototypeManager = dependencies.Resolve(); _gameTiming = dependencies.Resolve(); + _localizationHelper = _entityManager.System(); } public void Populate(PlantAnalyzerScannedUserMessage msg) @@ -136,7 +135,7 @@ public sealed partial class PlantAnalyzerWindow : FancyWindow { var count = msg.TrayData.Chemicals.Count; var holder = ContainerLabel.Text; - var chemicals = PlantAnalyzerLocalizationHelper.ChemicalsToLocalizedStrings(msg.TrayData.Chemicals, _prototypeManager); + var chemicals = _localizationHelper.ChemicalsToLocalizedStrings(msg.TrayData.Chemicals); if (count == 0) ChemicalsInWaterLabel.Text = Loc.GetString("plant-analyzer-soil-empty", ("holder", holder)); else @@ -155,7 +154,7 @@ public sealed partial class PlantAnalyzerWindow : FancyWindow { (string, string)[] parameters = [ ("seedName", SeedLabel.Text), - ("gases", PlantAnalyzerLocalizationHelper.GasesToLocalizedStrings(msg.TolerancesData.ConsumeGasses, _prototypeManager)), + ("gases", _localizationHelper.GasesToLocalizedStrings(msg.TolerancesData.ConsumeGasses)), ("kpa", msg.TolerancesData.IdealPressure.ToString("0.00")), ("kpaTolerance", msg.TolerancesData.PressureTolerance.ToString("0.00")), ("temp", msg.TolerancesData.IdealHeat.ToString("0.00")), @@ -180,9 +179,9 @@ public sealed partial class PlantAnalyzerWindow : FancyWindow // Section 5: Output if (msg.ProduceData is not null) { - var gases = PlantAnalyzerLocalizationHelper.GasesToLocalizedStrings(msg.ProduceData.ExudeGasses, _prototypeManager); - var (produce, producePlural) = PlantAnalyzerLocalizationHelper.ProduceToLocalizedStrings(msg.ProduceData.Produce, _prototypeManager); - var chemicals = PlantAnalyzerLocalizationHelper.ChemicalsToLocalizedStrings(msg.ProduceData.Chemicals, _prototypeManager); + var gases = _localizationHelper.GasesToLocalizedStrings(msg.ProduceData.ExudeGasses); + var (produce, producePlural, firstProduce) = _localizationHelper.ProduceToLocalizedStrings(msg.ProduceData.Produce); + var chemicals = _localizationHelper.ChemicalsToLocalizedStrings(msg.ProduceData.Chemicals); (string, object)[] parameters = [ ("yield", msg.ProduceData.Yield), @@ -190,7 +189,7 @@ public sealed partial class PlantAnalyzerWindow : FancyWindow ("gases", gases), ("potency", Loc.GetString(msg.ProduceData.Potency)), ("seedless", msg.ProduceData.Seedless), - ("firstProduce", msg.ProduceData.Produce.FirstOrDefault().Id ?? ""), + ("firstProduce", firstProduce), ("produce", produce), ("producePlural", producePlural), ("chemCount", msg.ProduceData.Chemicals.Count), diff --git a/Content.Client/Info/LinkBanner.cs b/Content.Client/Info/LinkBanner.cs index ec6e1e96f4..92897e9818 100644 --- a/Content.Client/Info/LinkBanner.cs +++ b/Content.Client/Info/LinkBanner.cs @@ -55,7 +55,7 @@ namespace Content.Client.Info var roadmapButton = new Button { Text = Loc.GetString("server-info-roadmap-button"), - StyleClasses = { StyleBase.ButtonCaution }, + StyleClasses = { StyleClass.ButtonSquare }, }; roadmapButton.OnPressed += _ => UserInterfaceManager.GetUIController().ToggleRoadmap(); buttons.AddChild(roadmapButton); diff --git a/Content.Client/Lobby/LobbyState.cs b/Content.Client/Lobby/LobbyState.cs index c8e9574729..504a794a93 100644 --- a/Content.Client/Lobby/LobbyState.cs +++ b/Content.Client/Lobby/LobbyState.cs @@ -22,18 +22,15 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; using Content.Client.Changelog; using Content.Client.Parallax.Managers; -using Content.Server.GameTicking.Prototypes; using Content.Shared._Sunrise.Lobby; using Content.Shared._Sunrise.ServersHub; using Content.Shared._Sunrise.SunriseCCVars; using Content.Shared.GameTicking; -using Robust.Shared.Configuration; using Robust.Shared.ContentPack; -using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Markdown; using Robust.Shared.Serialization.Markdown.Mapping; -using Serilog; +using Content.Shared.GameTicking.Prototypes; namespace Content.Client.Lobby { @@ -55,7 +52,6 @@ namespace Content.Client.Lobby [Dependency] private readonly ContributorsManager _contributorsManager = default!; [Dependency] private readonly ChangelogManager _changelogManager = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private ClientGameTicker _gameTicker = default!; private ContentAudioSystem _contentAudioSystem = default!; @@ -406,7 +402,7 @@ namespace Content.Client.Lobby private void SetLobbyAnimation(string lobbyAnimation) { - if (!_prototypeManager.TryIndex(lobbyAnimation, out var lobbyAnimationPrototype)) + if (!_protoMan.TryIndex(lobbyAnimation, out var lobbyAnimationPrototype)) return; if (Lobby == null) @@ -421,7 +417,7 @@ namespace Content.Client.Lobby private void SetLobbyArt(string lobbyArt) { - if (!_prototypeManager.TryIndex(lobbyArt, out var lobbyArtPrototype)) + if (!_protoMan.TryIndex(lobbyArt, out var lobbyArtPrototype)) return; if (Lobby == null) @@ -435,7 +431,7 @@ namespace Content.Client.Lobby private void SetLobbyParallax(string lobbyParallax) { - if (!_prototypeManager.TryIndex(lobbyParallax, out var lobbyParallaxPrototype)) + if (!_protoMan.TryIndex(lobbyParallax, out var lobbyParallaxPrototype)) return; if (Lobby == null) diff --git a/Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs b/Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs index cd2714b671..6b8eb58ff5 100644 --- a/Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs +++ b/Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs @@ -115,7 +115,7 @@ public sealed partial class RequirementsSelector : BoxContainer Text = Loc.GetString("role-timer-locked"), Visible = true, HorizontalAlignment = HAlignment.Center, - StyleClasses = {StyleBase.StyleClassLabelSubText}, + StyleClasses = { "StyleClassLabelSubText" }, }; _lockStripe.Children.Clear(); @@ -136,7 +136,7 @@ public sealed partial class RequirementsSelector : BoxContainer Visible = true, HorizontalAlignment = HAlignment.Center, FontColorOverride = Color.Red, - StyleClasses = {StyleBase.StyleClassLabelSubText}, + StyleClasses = { "StyleClassLabelSubText" }, }; _lockStripe.Children.Clear(); diff --git a/Content.Client/Options/UI/Tabs/ExtraTab.xaml.cs b/Content.Client/Options/UI/Tabs/ExtraTab.xaml.cs index 3d65961233..404705d25c 100644 --- a/Content.Client/Options/UI/Tabs/ExtraTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/ExtraTab.xaml.cs @@ -1,8 +1,8 @@ using Content.Client.Audio; -using Content.Server.GameTicking.Prototypes; using Content.Shared._Sunrise.Lobby; using Content.Shared._Sunrise.SunriseCCVars; using Content.Shared.GameTicking; +using Content.Shared.GameTicking.Prototypes; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface; using Robust.Client.UserInterface.XAML; diff --git a/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs b/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs index b240f2de85..08c1a6c12c 100644 --- a/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/MiscTab.xaml.cs @@ -1,6 +1,5 @@ using System.Linq; using Content.Client.UserInterface.Screens; -using Content.Server.GameTicking.Prototypes; using Content.Shared._Sunrise.Lobby; using Content.Shared._Sunrise.SunriseCCVars; using Content.Shared.CCVar; diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index 7fd45d5db2..ee81c1325d 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -32,6 +32,9 @@ using Robust.Shared.Utility; using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; using Robust.Shared.Configuration; +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Content.Shared.Weapons.Hitscan.Events; namespace Content.Client.Weapons.Ranged.Systems; @@ -125,40 +128,37 @@ public sealed partial class GunSystem : SharedGunSystem private void OnHitscan(HitscanEvent ev) { - var hitscan = _proto.Index(ev.Hitscan); - //The real bullet speed is so high that the bullet isn’t visible at all. So, let's slow it down 5x. - var bulletSpeed = hitscan.Speed / 5000; - foreach (var effects in ev.Effects) + foreach (var trace in ev.Traces) { var delay = 0f; - foreach (var effect in effects) - delay = FireEffect(hitscan, bulletSpeed, delay, effect); + delay = FireEffect(ev, delay, trace); } } - private float FireEffect(HitscanPrototype hitscan, float bulletSpeed, float delay, Effect effect) + private float FireEffect(HitscanEvent visuals, float delay, HitscanTrace trace) { - var length = effect.Distance / bulletSpeed; - if (effect.MuzzleCoordinates is { } muzzleCoordinates) + //The real bullet speed is so high that the bullet isn’t visible at all. So, let's slow it down 5x. + var length = trace.Distance / (visuals.Speed / 5000); + if (trace.MuzzleCoordinates is { } muzzleCoordinates) { - if (hitscan.MuzzleFlash is { } mozzle && (_tracesEnabled || hitscan.Bullet is null)) - RenderFlash(muzzleCoordinates, effect.Angle, mozzle, 1f, false, false, length, delay); + if (visuals.MuzzleFlash is { } mozzle && (_tracesEnabled || visuals.Bullet is null)) + RenderFlash(muzzleCoordinates, trace.Angle, mozzle, 1f, false, false, length, delay); - if (hitscan.Bullet is { } bullet) - RenderBullet(muzzleCoordinates, effect.Angle, bullet, effect.Distance - 1.5f, length, delay); + if (visuals.Bullet is { } bullet) + RenderBullet(muzzleCoordinates, trace.Angle, bullet, trace.Distance - 1.5f, length, delay); } - if (hitscan.TravelFlash is { } travel && effect.TravelCoordinates is { } travelCoordinates && (_tracesEnabled || hitscan.Bullet is null)) - RenderFlash(travelCoordinates, effect.Angle, travel, effect.Distance - 1.5f, true, false, length, delay); + if (visuals.TravelFlash is { } travel && trace.TravelCoordinates is { } travelCoordinates && (_tracesEnabled || visuals.Bullet is null)) + RenderFlash(travelCoordinates, trace.Angle, travel, trace.Distance - 1.5f, true, false, length, delay); delay += length; - if ((hitscan.ImpactFlash is not null || effect.ImpactEnt is not null) && (_tracesEnabled || hitscan.Bullet is null)) + if ((visuals.ImpactFlash is not null || trace.ImpactedEnt is not null) && (_tracesEnabled || visuals.Bullet is null)) Timer.Spawn((int)delay, () => { - if (hitscan.ImpactFlash is { } impact) - RenderFlash(effect.ImpactCoordinates, effect.Angle, impact, 1f, false, true, length, delay); + if (visuals.ImpactFlash is { } impact) + RenderFlash(trace.ImpactCoordinates, trace.Angle, impact, 1f, false, true, length, delay); - if (effect.ImpactEnt is { } netEnt && GetEntity(netEnt) is EntityUid ent) - RenderDisplacementImpact(GetCoordinates(effect.ImpactCoordinates), effect.Angle, ent); + if (trace.ImpactedEnt is { } netEnt && GetEntity(netEnt) is EntityUid ent) + RenderDisplacementImpact(GetCoordinates(trace.ImpactCoordinates), trace.Angle, ent); }); return delay; } @@ -640,4 +640,6 @@ public sealed partial class GunSystem : SharedGunSystem _animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light"); _animPlayer.Play((gunUid, uidPlayer), animTwo, "muzzle-flash-light"); } + + public override void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound) {} } diff --git a/Content.Client/_Sunrise/BloodCult/UI/ListViewSelector/ListViewSelectorWindow.xaml.cs b/Content.Client/_Sunrise/BloodCult/UI/ListViewSelector/ListViewSelectorWindow.xaml.cs index fa7393c31a..7377aa70c0 100644 --- a/Content.Client/_Sunrise/BloodCult/UI/ListViewSelector/ListViewSelectorWindow.xaml.cs +++ b/Content.Client/_Sunrise/BloodCult/UI/ListViewSelector/ListViewSelectorWindow.xaml.cs @@ -37,7 +37,7 @@ public partial class ListViewSelectorWindow : DefaultWindow MinSize = new Vector2(100, 100), MaxSize = new Vector2(100, 100), HorizontalExpand = true, - StyleClasses = { StyleBase.ButtonSquare }, + StyleClasses = { StyleClass.ButtonSquare }, ToggleMode = false, ToolTip = Loc.GetString($"ent-{item}"), TooltipDelay = 0.01f, diff --git a/Content.Client/_Sunrise/InteractionsPanel/CustomInteractionEditor.xaml.cs b/Content.Client/_Sunrise/InteractionsPanel/CustomInteractionEditor.xaml.cs index bf2ca379fe..6881f65d76 100644 --- a/Content.Client/_Sunrise/InteractionsPanel/CustomInteractionEditor.xaml.cs +++ b/Content.Client/_Sunrise/InteractionsPanel/CustomInteractionEditor.xaml.cs @@ -438,7 +438,7 @@ public sealed partial class CustomInteractionEditor : DefaultWindow var removeButton = new Button { Text = "✕", - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, MinWidth = 22, MinHeight = 22, Label = { FontColorOverride = PrimaryColor} @@ -494,7 +494,7 @@ public sealed partial class CustomInteractionEditor : DefaultWindow var removeButton = new Button { Text = "✕", - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, MinWidth = 22, MinHeight = 22, Label = { FontColorOverride = PrimaryColor} @@ -553,7 +553,7 @@ public sealed partial class CustomInteractionEditor : DefaultWindow var closeButton = new Button { Text = "ОК", - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, HorizontalAlignment = HAlignment.Center, MinWidth = 80, MinHeight = 30 diff --git a/Content.Client/_Sunrise/InteractionsPanel/InteractionsUIWindow.xaml.cs b/Content.Client/_Sunrise/InteractionsPanel/InteractionsUIWindow.xaml.cs index c4e64ce904..9f935178f6 100644 --- a/Content.Client/_Sunrise/InteractionsPanel/InteractionsUIWindow.xaml.cs +++ b/Content.Client/_Sunrise/InteractionsPanel/InteractionsUIWindow.xaml.cs @@ -468,7 +468,7 @@ public sealed partial class InteractionsUIWindow : DefaultWindow HorizontalExpand = true, MinHeight = 40, MaxHeight = 45, - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, Disabled = isOnCooldown }; @@ -535,7 +535,7 @@ public sealed partial class InteractionsUIWindow : DefaultWindow { MinSize = new Vector2(32, 45), MaxSize = new Vector2(32, 45), - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, Margin = new Thickness(2, 0, 0, 0), VerticalAlignment = VAlignment.Center }; @@ -610,7 +610,7 @@ public sealed partial class InteractionsUIWindow : DefaultWindow HorizontalExpand = true, MinHeight = 40, MaxHeight = 45, - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, Disabled = isOnCooldown, }; @@ -678,7 +678,7 @@ public sealed partial class InteractionsUIWindow : DefaultWindow { MinSize = new Vector2(32, 44), MaxSize = new Vector2(32, 44), - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, Margin = new Thickness(2, 0, 0, 0), VerticalAlignment = VAlignment.Center }; @@ -967,7 +967,7 @@ public sealed partial class InteractionsUIWindow : DefaultWindow var editButton = new Button { Text = "Редактировать", - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, Margin = new Thickness(0, 0, 4, 0) }; @@ -981,7 +981,7 @@ public sealed partial class InteractionsUIWindow : DefaultWindow var deleteButton = new Button { Text = "Удалить", - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, }; deleteButton.StyleBoxOverride = new StyleBoxFlat @@ -1120,7 +1120,7 @@ public sealed partial class InteractionsUIWindow : DefaultWindow var cancelButton = new Button { Text = "Отмена", - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, Margin = new Thickness(0, 0, 4, 0) }; @@ -1134,7 +1134,7 @@ public sealed partial class InteractionsUIWindow : DefaultWindow var confirmButton = new Button { Text = "Удалить", - StyleClasses = { "ButtonSquare" }, + StyleClasses = { StyleClass.ButtonSquare }, }; confirmButton.StyleBoxOverride = new StyleBoxFlat diff --git a/Content.Client/_Sunrise/MentorHelp/MentorHelpUIController.cs b/Content.Client/_Sunrise/MentorHelp/MentorHelpUIController.cs index 5c5d2a6550..fc50ba8963 100644 --- a/Content.Client/_Sunrise/MentorHelp/MentorHelpUIController.cs +++ b/Content.Client/_Sunrise/MentorHelp/MentorHelpUIController.cs @@ -439,12 +439,12 @@ public sealed class MentorHelpUIController : UIController, IOnSystemChanged(player); var slashProto = protoMan.Index(DamageType); - damageableSystem.TryChangeDamage(player, new DamageSpecifier(slashProto, FixedPoint2.New(46.5)), useModifier: false, useVariance: false); // Sunrise-Edit + damageableSystem.ChangeDamage(player, new DamageSpecifier(slashProto, FixedPoint2.New(46.5)), ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit }); // Check that running the suicide command kills the player diff --git a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs index f32f208617..57a950291f 100644 --- a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs @@ -171,7 +171,7 @@ namespace Content.IntegrationTests.Tests.Damageable var damageToDeal = FixedPoint2.New(types.Count * 5); DamageSpecifier damage = new(group3, damageToDeal); - sDamageableSystem.TryChangeDamage(uid, damage, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, damage, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.Multiple(() => { @@ -185,7 +185,7 @@ namespace Content.IntegrationTests.Tests.Damageable }); // Heal - sDamageableSystem.TryChangeDamage(uid, -damage, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, -damage, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.Multiple(() => { @@ -204,7 +204,7 @@ namespace Content.IntegrationTests.Tests.Damageable Assert.That(types, Has.Count.EqualTo(3)); damage = new DamageSpecifier(group3, 14); - sDamageableSystem.TryChangeDamage(uid, damage, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, damage, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.Multiple(() => { @@ -216,7 +216,7 @@ namespace Content.IntegrationTests.Tests.Damageable }); // Heal - sDamageableSystem.TryChangeDamage(uid, -damage, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, -damage, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.Multiple(() => { @@ -232,7 +232,7 @@ namespace Content.IntegrationTests.Tests.Damageable Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero)); }); damage = new DamageSpecifier(group1, FixedPoint2.New(10)) + new DamageSpecifier(type2b, FixedPoint2.New(10)); - sDamageableSystem.TryChangeDamage(uid, damage, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, damage, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.Multiple(() => { @@ -252,9 +252,9 @@ namespace Content.IntegrationTests.Tests.Damageable Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero)); // Test 'wasted' healing - sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(type3a, 5), useModifier: false, useVariance: false); // Sunrise-Edit - sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(type3b, 7), useModifier: false, useVariance: false); // Sunrise-Edit - sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -11), useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(type3a, 5), ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(type3b, 7), ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, -11), ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.Multiple(() => { @@ -264,11 +264,11 @@ namespace Content.IntegrationTests.Tests.Damageable }); // Test Over-Healing - sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, FixedPoint2.New(-100)), useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, FixedPoint2.New(-100)), ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero)); // Test that if no health change occurred, returns false - sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -100), useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, -100), ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero)); }); await pair.CleanReturnAsync(); diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs index 0ab86ce1c4..e36cb07715 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs @@ -45,7 +45,7 @@ namespace Content.IntegrationTests.Tests.Destructible #pragma warning disable NUnit2045 // Interdependent assertions. Assert.DoesNotThrow(() => { - sEntityManager.System().TryChangeDamage(sDestructibleEntity, bruteDamage, true, useModifier: false, useVariance: false); // Sunrise-Edit + sEntityManager.System().ChangeDamage(sDestructibleEntity, bruteDamage, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit }); Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Has.Count.EqualTo(1)); diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs index 1e80d5c64a..9b635b57e7 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs @@ -65,12 +65,12 @@ namespace Content.IntegrationTests.Tests.Destructible { var bluntDamage = new DamageSpecifier(sPrototypeManager.Index(TestBluntDamageTypeId), 10); - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit // No thresholds reached yet, the earliest one is at 20 damage Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit // Only one threshold reached, 20 Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Has.Count.EqualTo(1)); @@ -89,7 +89,7 @@ namespace Content.IntegrationTests.Tests.Destructible sTestThresholdListenerSystem.ThresholdsReached.Clear(); - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 3, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage * 3, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit // One threshold reached, 50, since 20 already triggered before and it has not been healed below that amount Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Has.Count.EqualTo(1)); @@ -120,7 +120,7 @@ namespace Content.IntegrationTests.Tests.Destructible sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Damage for 50 again, up to 100 now - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 5, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage * 5, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit // No thresholds reached as they weren't healed below the trigger amount Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); @@ -129,7 +129,7 @@ namespace Content.IntegrationTests.Tests.Destructible sDamageableSystem.ClearAllDamage((sDestructibleEntity, sDamageableComponent)); // Damage for 100, up to 100 - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 10, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage * 10, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit // Two thresholds reached as damage increased past the previous, 20 and 50 Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Has.Count.EqualTo(2)); @@ -137,25 +137,25 @@ namespace Content.IntegrationTests.Tests.Destructible sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal the entity for 40 damage, down to 60 - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * -4, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage * -4, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit // ThresholdsLookup don't work backwards Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Damage for 10, up to 70 - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit // Not enough healing to de-trigger a threshold Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Heal by 30, down to 40 - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * -3, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage * -3, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit // ThresholdsLookup don't work backwards Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Damage up to 50 again - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit // The 50 threshold should have triggered again, after being healed Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Has.Count.EqualTo(1)); @@ -190,7 +190,7 @@ namespace Content.IntegrationTests.Tests.Destructible sDamageableSystem.ClearAllDamage((sDestructibleEntity, sDamageableComponent)); // Damage up to 50 - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 5, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage * 5, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.Multiple(() => { @@ -262,7 +262,7 @@ namespace Content.IntegrationTests.Tests.Destructible } // Damage the entity up to 50 damage again - sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 5, true, useModifier: false, useVariance: false); // Sunrise-Edit + sDamageableSystem.ChangeDamage(sDestructibleEntity, bluntDamage * 5, true, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit Assert.Multiple(() => { diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 2608c6bb4c..0d8c1653d7 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -67,6 +67,7 @@ using Robust.Shared.Random; using Robust.Shared.Spawners; using Robust.Shared.Utility; using Timer = Robust.Shared.Timing.Timer; +using Content.Shared.Damage; namespace Content.Server.Administration.Systems; @@ -1198,7 +1199,7 @@ public sealed partial class AdminVerbSystem { "Radiation", GetDamageToKill(target) } } }; - _damageable.SetDamage(target, Comp(target), damageSpecifier); + _damageable.SetDamage(target, damageSpecifier); } private void Scorched(EntityUid target) @@ -1223,7 +1224,7 @@ public sealed partial class AdminVerbSystem { "Heat", GetDamageToKill(target) - 50 } } }; - _damageable.SetDamage(target, Comp(target), damageSpecifier); + _damageable.SetDamage(target, damageSpecifier); }); } } @@ -1251,7 +1252,7 @@ public sealed partial class AdminVerbSystem { "Toxin", GetDamageToKill(target) } } }; - _damageable.SetDamage(target, Comp(target), damageSpecifier); + _damageable.SetDamage(target, damageSpecifier); } private void BluespaceAway(EntityUid target) @@ -1287,6 +1288,6 @@ public sealed partial class AdminVerbSystem } }; - _damageable.SetDamage(target, Comp(target), damageSpecifier); + _damageable.SetDamage(target, damageSpecifier); } } diff --git a/Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs b/Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs index 53e3bb7707..9991a0dd93 100644 --- a/Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs +++ b/Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs @@ -22,7 +22,8 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Player; using Robust.Shared.Timing; -using Robust.Shared.Utility; //Sunrise-Edit +using Robust.Shared.Utility; +using Content.Shared.PowerCell.Components; //Sunrise-Edit namespace Content.Server.Atmos.Monitor.Systems; @@ -37,7 +38,7 @@ public sealed class AtmosAlertsComputerSystem : SharedAtmosAlertsComputerSystem [Dependency] private readonly NavMapSystem _navMapSystem = default!; [Dependency] private readonly DeviceListSystem _deviceListSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPowerCellSystem _cell = default!; + [Dependency] private readonly PowerCellSystem _cell = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; private const float UpdateTime = 1.0f; @@ -207,7 +208,7 @@ public sealed class AtmosAlertsComputerSystem : SharedAtmosAlertsComputerSystem // Sunrise-start if (HasComp(ent) && TryComp(ent, out var draw)) { - if (_cell.HasActivatableCharge(ent, draw) || _cell.HasDrawCharge(ent, draw)) + if (_cell.HasActivatableCharge(ent) || _cell.HasDrawCharge(ent)) { Beep(ent, entConsole, highestAlert); } diff --git a/Content.Server/Bible/BibleSystem.cs b/Content.Server/Bible/BibleSystem.cs index fa39f7e8ec..9df660076d 100644 --- a/Content.Server/Bible/BibleSystem.cs +++ b/Content.Server/Bible/BibleSystem.cs @@ -146,20 +146,22 @@ namespace Content.Server.Bible var userEnt = Identity.Entity(args.User, EntityManager); var targetEnt = Identity.Entity(args.Target.Value, EntityManager); - //Damage unholy creatures + // Sunrise-start if (HasComp(args.Target)) { _damageableSystem.TryChangeDamage(args.Target.Value, component.DamageUnholy, true, origin: uid); - var othersMessage = Loc.GetString(component.LocPrefix + "-damage-unholy-others", ("user", Identity.Entity(args.User, EntityManager)), ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid)); - _popupSystem.PopupEntity(othersMessage, args.User, Filter.PvsExcept(args.User), true, PopupType.MediumCaution); + var othersUnholyMessage = Loc.GetString(component.LocPrefix + "-damage-unholy-others", ("user", Identity.Entity(args.User, EntityManager)), ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid)); + _popupSystem.PopupEntity(othersUnholyMessage, args.User, Filter.PvsExcept(args.User), true, PopupType.MediumCaution); - var selfMessage = Loc.GetString(component.LocPrefix + "-damage-unholy-self", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid)); - _popupSystem.PopupEntity(selfMessage, args.User, args.User, PopupType.LargeCaution); + var selfUnholyMessage = Loc.GetString(component.LocPrefix + "-damage-unholy-self", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid)); + _popupSystem.PopupEntity(selfUnholyMessage, args.User, args.User, PopupType.LargeCaution); + + _delay.TryResetDelay((uid, useDelay)); return; } - + // Sunrise-end // This only has a chance to fail if the target is not wearing anything on their head and is not a familiar.. if (!_invSystem.TryGetSlotEntity(args.Target.Value, "head", out _) && !HasComp(args.Target.Value)) @@ -198,9 +200,9 @@ namespace Content.Server.Bible _popupSystem.PopupEntity(othersMessage, args.User, Filter.PvsExcept(args.User), true, PopupType.Medium); _popupSystem.PopupEntity(selfMessage, args.User, args.User, PopupType.Large); - } - RaiseLocalEvent(args.Target.Value, new MoodEffectEvent("GotBlessed")); // Sunrise Edit + RaiseLocalEvent(args.Target.Value, new MoodEffectEvent("GotBlessed")); // Sunrise Edit + } private void AddSummonVerb(EntityUid uid, SummonableComponent component, GetVerbsEvent args) { diff --git a/Content.Server/Botany/Components/PlantAnalyzerComponent.cs b/Content.Server/Botany/Components/PlantAnalyzerComponent.cs index 88a0bdcb5a..24cc15852f 100644 --- a/Content.Server/Botany/Components/PlantAnalyzerComponent.cs +++ b/Content.Server/Botany/Components/PlantAnalyzerComponent.cs @@ -1,15 +1,14 @@ -using Content.Server.AbstractAnalyzer; -using Content.Server.Botany.Systems; +using Content.Shared.AbstractAnalyzer; +using Content.Shared.Paper; using Robust.Shared.Audio; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Server.Botany.Components; +namespace Content.Shared.Botany.Components; /// -[RegisterComponent, AutoGenerateComponentPause] -[Access(typeof(PlantAnalyzerSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause] public sealed partial class PlantAnalyzerComponent : AbstractAnalyzerComponent { /// @@ -20,24 +19,25 @@ public sealed partial class PlantAnalyzerComponent : AbstractAnalyzerComponent /// /// When will the analyzer be ready to print again? /// - [ViewVariables(VVAccess.ReadOnly)] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [AutoPausedField] public TimeSpan PrintReadyAt = TimeSpan.Zero; /// /// How often can the analyzer print? /// - [DataField("printCooldown")] + [DataField] public TimeSpan PrintCooldown = TimeSpan.FromSeconds(5); /// /// The sound that's played when the analyzer prints off a report. /// - [DataField("soundPrint")] + [DataField] public SoundSpecifier SoundPrint = new SoundPathSpecifier("/Audio/Machines/short_print_and_rip.ogg"); /// /// What the machine will print. /// - [DataField("machineOutput", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string MachineOutput = "PlantAnalyzerReportPaper"; + [DataField] + public EntProtoId MachineOutput = "PlantAnalyzerReportPaper"; } diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index bf0fc95449..5809bdea79 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -149,7 +149,20 @@ public sealed partial class BotanySystem : EntitySystem public IEnumerable GenerateProduct(SeedData proto, EntityCoordinates position, int yieldMod = 1) { - var totalYield = CalculateTotalYield(proto.Yield, yieldMod); + // Sunrise-start + // var totalYield = 0; + // if (proto.Yield > -1) + // { + // if (yieldMod < 0) + // totalYield = proto.Yield; + // else + // totalYield = proto.Yield * yieldMod; + + // totalYield = Math.Max(1, totalYield); + // } + // Sunrise-end + + var totalYield = CalculateTotalYield(proto.Yield, yieldMod); // Sunrise-edit, если тут появится конфликт, значит оффы мерджнули анализатор растений и надо все эдиты убрать var products = new List(); if (totalYield > 1 || proto.HarvestRepeat != HarvestType.NoRepeat) diff --git a/Content.Server/Botany/Systems/PlantAnalyzerSystem.cs b/Content.Server/Botany/Systems/PlantAnalyzerSystem.cs index 126acaca28..86b60db89b 100644 --- a/Content.Server/Botany/Systems/PlantAnalyzerSystem.cs +++ b/Content.Server/Botany/Systems/PlantAnalyzerSystem.cs @@ -1,11 +1,10 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; -using Content.Server.AbstractAnalyzer; using Content.Server.Botany.Components; using Content.Server.Popups; +using Content.Shared.AbstractAnalyzer; +using Content.Shared.Botany.Components; using Content.Shared.Botany.PlantAnalyzer; -using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Labels.EntitySystems; @@ -13,7 +12,6 @@ using Content.Shared.Paper; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; -using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Server.Botany.Systems; @@ -27,9 +25,8 @@ public sealed class PlantAnalyzerSystem : AbstractAnalyzerSystem(OnAfterInject); + SubscribeLocalEvent(OnAfterInject); } - private void OnAfterInject(Entity entity, ref HyposprayAfterInjectEvent args) + private void OnAfterInject(Entity entity, ref BeforeInjectTargetEvent args) { // Get the solution that was injected if (_solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var hypoSpraySoln, out _)) { - _borgHypospray.TryAnnounceInjection(entity.Owner, args.User, args.Target, hypoSpraySoln.Value); + _borgHypospray.TryAnnounceInjection(entity.Owner, args.EntityUsingInjector, args.TargetGettingInjected, hypoSpraySoln.Value); } } -} \ No newline at end of file +} diff --git a/Content.Server/EnergyDome/EnergyDomeSystem.cs b/Content.Server/EnergyDome/EnergyDomeSystem.cs index 8a1592f099..725e072782 100644 --- a/Content.Server/EnergyDome/EnergyDomeSystem.cs +++ b/Content.Server/EnergyDome/EnergyDomeSystem.cs @@ -1,7 +1,6 @@ using Content.Server.DeviceLinking.Systems; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; -using Content.Server.PowerCell; using Content.Shared._Sunrise.Biocode; using Content.Shared.Actions; using Content.Shared.Damage; @@ -20,6 +19,7 @@ using Content.Shared.Power.Components; using Robust.Server.Containers; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; +using Content.Shared.Damage.Systems; namespace Content.Server.EnergyDome; @@ -183,13 +183,13 @@ public sealed partial class EnergyDomeSystem : EntitySystem private void OnPowerCellChanged(Entity generator, ref PowerCellChangedEvent args) { - if (args.Ejected || !_powerCell.HasDrawCharge(generator)) + if (args.Ejected || !_powerCell.HasDrawCharge(generator.Owner)) TurnOff(generator, true); } private void OnChargeChanged(Entity generator, ref ChargeChangedEvent args) { - if (args.Charge == 0) + if (args.CurrentCharge == 0) TurnOff(generator, true); } private void OnDomeDamaged(Entity dome, ref DamageChangedEvent args) @@ -215,9 +215,9 @@ public sealed partial class EnergyDomeSystem : EntitySystem _powerCell.TryGetBatteryFromSlot(generatorUid, out var cell); if (cell != null) { - _battery.UseCharge(cell.Owner, energyLeak); + _battery.UseCharge(cell.Value.Owner, energyLeak); - if (cell.CurrentCharge == 0) + if (cell.Value.Comp.ChargeRate == 0) TurnOff((generatorUid, generatorComp), true); } } @@ -226,7 +226,7 @@ public sealed partial class EnergyDomeSystem : EntitySystem if (TryComp(generatorUid, out var battery)) { _battery.UseCharge(generatorUid, energyLeak); - if (battery.CurrentCharge == 0) + if (battery.ChargeRate == 0) TurnOff((generatorUid, generatorComp), true); } } @@ -265,9 +265,10 @@ public sealed partial class EnergyDomeSystem : EntitySystem return false; } - if (TryComp(generator, out var powerCellSlot)) + if (TryComp(generator, out var powerCellSlot)) { - if (!_powerCell.TryGetBatteryFromSlot(generator, out var cell) && !TryComp(generator, out cell)) + if (!_powerCell.TryGetBatteryFromSlot(generator.Owner, out var cell) && + !HasComp(generator.Owner)) { _audio.PlayPvs(generator.Comp.TurnOffSound, generator); _popup.PopupEntity( @@ -276,7 +277,7 @@ public sealed partial class EnergyDomeSystem : EntitySystem return false; } - if (!_powerCell.HasDrawCharge(generator)) + if (!_powerCell.HasDrawCharge(generator.Owner)) { _audio.PlayPvs(generator.Comp.TurnOffSound, generator); _popup.PopupEntity( @@ -288,7 +289,7 @@ public sealed partial class EnergyDomeSystem : EntitySystem if (TryComp(generator, out var battery)) { - if (battery.CurrentCharge == 0) + if (battery.ChargeRate == 0) { _audio.PlayPvs(generator.Comp.TurnOffSound, generator); _popup.PopupEntity( @@ -334,10 +335,6 @@ public sealed partial class EnergyDomeSystem : EntitySystem _powerCell.SetDrawEnabled(generator.Owner, true); } - if (TryComp(generator, out var recharger)) { - recharger.AutoRecharge = true; - } - generator.Comp.SpawnedDome = newDome; _audio.PlayPvs(generator.Comp.TurnOnSound, generator); generator.Comp.Enabled = true; @@ -377,10 +374,6 @@ public sealed partial class EnergyDomeSystem : EntitySystem { _powerCell.SetDrawEnabled(generator.Owner, false); } - if (TryComp(generator, out var recharger)) - { - recharger.AutoRecharge = true; - } _audio.PlayPvs(generator.Comp.TurnOffSound, generator); if (startReloading) diff --git a/Content.Server/GameTicking/Commands/SetMapCommand.cs b/Content.Server/GameTicking/Commands/SetMapCommand.cs index f05aa2b1da..978c2ef597 100644 --- a/Content.Server/GameTicking/Commands/SetMapCommand.cs +++ b/Content.Server/GameTicking/Commands/SetMapCommand.cs @@ -2,6 +2,7 @@ using System.Linq; using Content.Server.Administration; using Content.Server.Maps; using Content.Shared.Administration; +using Content.Shared.Maps; using Robust.Shared.Console; using Robust.Shared.Prototypes; diff --git a/Content.Server/GameTicking/GameTicker.LobbyBackground.cs b/Content.Server/GameTicking/GameTicker.LobbyBackground.cs index 4b9505ea7c..1148ec3446 100644 --- a/Content.Server/GameTicking/GameTicker.LobbyBackground.cs +++ b/Content.Server/GameTicking/GameTicker.LobbyBackground.cs @@ -1,8 +1,8 @@ using Robust.Shared.Random; using System.Linq; -using Content.Server.GameTicking.Prototypes; using Content.Shared._Sunrise.Lobby; using Content.Shared.GameTicking; +using Content.Shared.GameTicking.Prototypes; namespace Content.Server.GameTicking; diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 72b47d1442..80f7c26240 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -636,7 +636,7 @@ namespace Content.Server.Ghost DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage); - _damageable.ChangeDamage(playerEntity.Value, damage, true, useVariance: false, useModifier: false); // Sunrise-Edit + _damageable.ChangeDamage(playerEntity.Value, damage, true, useVariance: false, ignoreGlobalModifiers: true); // Sunrise-Edit } } diff --git a/Content.Server/Kitchen/Components/MicrowaveComponent.cs b/Content.Server/Kitchen/Components/MicrowaveComponent.cs index 97ffbe2ffe..64a16e0964 100644 --- a/Content.Server/Kitchen/Components/MicrowaveComponent.cs +++ b/Content.Server/Kitchen/Components/MicrowaveComponent.cs @@ -1,9 +1,12 @@ using Content.Shared.Construction.Prototypes; using Content.Shared.DeviceLinking; using Content.Shared.Item; +using Content.Shared.Kitchen; +using Content.Shared.Kitchen.Components; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Kitchen.Components diff --git a/Content.Server/Mech/Equipment/EntitySystems/MechGunSystem.cs b/Content.Server/Mech/Equipment/EntitySystems/MechGunSystem.cs index e8a820ac3f..8ea07e6d4b 100644 --- a/Content.Server/Mech/Equipment/EntitySystems/MechGunSystem.cs +++ b/Content.Server/Mech/Equipment/EntitySystems/MechGunSystem.cs @@ -35,13 +35,13 @@ public sealed class MechGunSystem : EntitySystem || !TryComp(mechEquipment.EquipmentOwner.Value, out var mech)) return; - var chargeDelta = component.MaxCharge - component.CurrentCharge; + var chargeDelta = component.MaxCharge - component.ChargeRate; // TODO: The battery charge of the mech would be spent directly when fired. if (chargeDelta <= 0 || mech.Energy - chargeDelta < 0 || !_mech.TryChangeEnergy(mechEquipment.EquipmentOwner.Value, -chargeDelta, mech)) return; - _battery.SetCharge(uid, component.MaxCharge, component); + _battery.SetCharge(uid, component.MaxCharge); } } diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index 7fc6782f46..7018948516 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -37,6 +37,7 @@ using System.Linq; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.Chat; +using Content.Shared.Damage.Components; namespace Content.Server.Mech.Systems; diff --git a/Content.Server/Medical/Components/HealthAnalyzerComponent.cs b/Content.Server/Medical/Components/HealthAnalyzerComponent.cs index cf166a3dd2..cea331acd2 100644 --- a/Content.Server/Medical/Components/HealthAnalyzerComponent.cs +++ b/Content.Server/Medical/Components/HealthAnalyzerComponent.cs @@ -1,4 +1,4 @@ -using Content.Server.AbstractAnalyzer; +using Content.Shared.AbstractAnalyzer; using Content.Shared.Damage.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; @@ -10,11 +10,7 @@ namespace Content.Server.Medical.Components; [Access(typeof(HealthAnalyzerSystem), typeof(CryoPodSystem))] public sealed partial class HealthAnalyzerComponent : AbstractAnalyzerComponent { - /// [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] [AutoPausedField] public override TimeSpan NextUpdate { get; set; } = TimeSpan.Zero; - - [DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List? DamageContainers; } diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs index 471acba1db..769e70a9e8 100644 --- a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs +++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs @@ -16,6 +16,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Timing; +using Content.Shared.PowerCell.Components; //Sunrise-Edit @@ -58,7 +59,7 @@ public sealed class CrewMonitoringConsoleSystem : EntitySystem { if (HasComp(uid) && TryComp(uid, out var draw)) { - if (_cell.HasActivatableCharge(uid, draw)) + if (_cell.HasActivatableCharge(uid)) { _audio.PlayPvs(component.CorpseAlertSound, uid); } diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index fffef2a7d2..605b644196 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -260,7 +260,7 @@ public sealed class DefibrillatorSystem : EntitySystem // Inject reagents if any are specified if (component.Reagents.Count > 0 && TryComp(target, out var bloodstream)) { - if (_solutionContainer.TryGetSolution(target, bloodstream.ChemicalSolutionName, out var solution)) + if (_solutionContainer.TryGetSolution(target, bloodstream.BloodReferenceSolution.Name, out var solution)) { foreach (var (reagent, amount) in component.Reagents) _solutionContainer.TryAddReagent(solution.Value, reagent, FixedPoint2.New(amount), out _); diff --git a/Content.Server/Medical/HealthAnalyzerSystem.cs b/Content.Server/Medical/HealthAnalyzerSystem.cs index dd9d6c5f5a..5d9cbe3df2 100644 --- a/Content.Server/Medical/HealthAnalyzerSystem.cs +++ b/Content.Server/Medical/HealthAnalyzerSystem.cs @@ -1,7 +1,5 @@ using Content.Server.Medical.Components; -using Content.Server.PowerCell; using System.Diagnostics.CodeAnalysis; -using Content.Server.AbstractAnalyzer; using Content.Server.Body.Components; using Content.Server.Temperature.Components; using Content.Shared.Body.Components; @@ -16,7 +14,6 @@ using Content.Shared.Mobs.Components; using Content.Shared.Popups; using Content.Shared.PowerCell; using Content.Shared.Temperature.Components; -using Content.Shared.Traits.Assorted; using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; using Robust.Server.GameObjects; @@ -24,6 +21,9 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Timing; using Content.Server.Body.Systems; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Interaction.Events; +using Content.Shared.AbstractAnalyzer; namespace Content.Server.Medical; @@ -31,63 +31,16 @@ public sealed class HealthAnalyzerSystem : AbstractAnalyzerSystem(OnAfterInteract); - SubscribeLocalEvent(OnDoAfter); - SubscribeLocalEvent(OnInsertedIntoContainer); - SubscribeLocalEvent(OnToggled); - SubscribeLocalEvent(OnDropped); - } - - public override void Update(float frameTime) - { - var analyzerQuery = EntityQueryEnumerator(); - while (analyzerQuery.MoveNext(out var uid, out var component, out var transform)) - { - //Update rate limited to 1 second - if (component.NextUpdate > _timing.CurTime) - continue; - - if (component.ScannedEntity is not {} patient) - continue; - - if (Deleted(patient)) - { - StopAnalyzingEntity((uid, component), patient); - continue; - } - - component.NextUpdate = _timing.CurTime + component.UpdateInterval; /// public override void UpdateScannedUser(EntityUid healthAnalyzer, EntityUid target, bool scanMode) { - if (args.Target == null || !args.CanReach || !HasComp(args.Target) || !_cell.HasDrawCharge(uid.Owner, user: args.User)) + if (!_uiSystem.HasUi(healthAnalyzer, HealthAnalyzerUiKey.Key)) return; - if (!TryComp(target, out var damageableComponent)) // Sunrise-Edit + if (!HasComp(target)) return; - var msg = Loc.GetString("health-analyzer-popup-scan-target", ("user", Identity.Entity(args.User, EntityManager))); - _popupSystem.PopupEntity(msg, args.Target.Value, args.Target.Value, PopupType.Medium); - } - - private void OnDoAfter(Entity uid, ref HealthAnalyzerDoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Target == null || !_cell.HasDrawCharge(uid.Owner, user: args.User)) - return; - - if (healthAnalyzerComp.DamageContainers is not null && - damageableComponent.DamageContainerID is not null && - !healthAnalyzerComp.DamageContainers.Contains(damageableComponent.DamageContainerID)) - return; - // Sunrise-End - var bodyTemperature = float.NaN; if (TryComp(target, out var temp)) @@ -101,13 +54,10 @@ public sealed class HealthAnalyzerSystem : AbstractAnalyzerSystem 0; } - if (TryComp(target, out var unrevivableComp) && unrevivableComp.Analyzable) - unrevivable = true; - // Collect hunger and thirst data as percentages float hungerLevel = -1; float thirstLevel = -1; @@ -128,6 +78,9 @@ public sealed class HealthAnalyzerSystem : AbstractAnalyzerSystem(target, out var unrevivableComp) && unrevivableComp.Analyzable) + unrevivable = true; + _uiSystem.ServerSendUiMessage(healthAnalyzer, HealthAnalyzerUiKey.Key, new HealthAnalyzerScannedUserMessage( GetNetEntity(target), bodyTemperature, diff --git a/Content.Server/Ninja/Systems/NinjaSuitDrawSystem.cs b/Content.Server/Ninja/Systems/NinjaSuitDrawSystem.cs index 30d81fb829..d897918261 100644 --- a/Content.Server/Ninja/Systems/NinjaSuitDrawSystem.cs +++ b/Content.Server/Ninja/Systems/NinjaSuitDrawSystem.cs @@ -104,8 +104,8 @@ public sealed class NinjaSuitDrawSystem : SharedNinjaSuitDrawSystem if (_ninja.GetNinjaBattery(user, out _, out var battery)) { - var canUse = ent.Comp.UseRate <= 0f || battery.CurrentCharge >= ent.Comp.UseRate; - var canDraw = ent.Comp.DrawRate <= 0f || battery.CurrentCharge > 0f; + var canUse = ent.Comp.UseRate <= 0f || battery.ChargeRate >= ent.Comp.UseRate; + var canDraw = ent.Comp.DrawRate <= 0f || battery.ChargeRate > 0f; SetPowerStatus(ent, canDraw, canUse); if (!canUse) { @@ -138,7 +138,7 @@ public sealed class NinjaSuitDrawSystem : SharedNinjaSuitDrawSystem if (!_ninja.IsNinja(user)) return false; - return _ninja.GetNinjaBattery(user, out _, out var battery) && battery.CurrentCharge > 0f; + return _ninja.GetNinjaBattery(user, out _, out var battery) && battery.ChargeRate > 0f; } public override bool CanUse(Entity ent) @@ -148,7 +148,7 @@ public sealed class NinjaSuitDrawSystem : SharedNinjaSuitDrawSystem return false; return _ninja.GetNinjaBattery(user, out _, out var battery) && - (ent.Comp.UseRate <= 0f || battery.CurrentCharge >= ent.Comp.UseRate); + (ent.Comp.UseRate <= 0f || battery.ChargeRate >= ent.Comp.UseRate); } } diff --git a/Content.Server/RoundEnd/RoundEndSystem.cs b/Content.Server/RoundEnd/RoundEndSystem.cs index 3b2aece913..18133f089a 100644 --- a/Content.Server/RoundEnd/RoundEndSystem.cs +++ b/Content.Server/RoundEnd/RoundEndSystem.cs @@ -65,10 +65,6 @@ namespace Content.Server.RoundEnd /// If the shuttle can't be recalled. if set to true, the station wont be able to recall /// public bool CantRecall = false; - - public TimeSpan AutoCallStartTime; - private bool _autoCalledBefore = false; - public override void Initialize() { base.Initialize(); diff --git a/Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs b/Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs index 880f3f3cbe..9bd542218f 100644 --- a/Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs +++ b/Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs @@ -259,8 +259,8 @@ public sealed partial class DockingSystem var spawnPosition = new EntityCoordinates(targetGridXform.MapUid!.Value, _transform.ToMapCoordinates(gridPosition).Position); // TODO: use tight bounds - var targetWorldAngle = (targetGridAngle + targetAngle).Reduced(); - var dockedBounds = new Box2Rotated(shuttleAABB.Translated(spawnPosition.Position), cacheTargetAngle, spawnPosition.Position); + var targetWorldAngle = (targetGridAngle + cacheTargetAngle).Reduced(); + var dockedBounds = new Box2Rotated(shuttleAABB.Translated(spawnPosition.Position), targetWorldAngle, spawnPosition.Position); var grids = new List>(); _mapManager.FindGridsIntersecting(targetGridXform.MapID, dockedBounds, ref grids, includeMap: false); diff --git a/Content.Server/Tips/TipsSystem.cs b/Content.Server/Tips/TipsSystem.cs index 35c288e7bf..689316a720 100644 --- a/Content.Server/Tips/TipsSystem.cs +++ b/Content.Server/Tips/TipsSystem.cs @@ -8,6 +8,7 @@ using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Server.ServerStatus; using Robust.Shared.Configuration; +using Robust.Shared.Console; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -56,7 +57,7 @@ public sealed class TipsSystem : SharedTipsSystem private void WatchdogOnUpdateReceived() { var message = Loc.GetString("server-updates-received"); - SendTippyForAll(message, 5f); + SendTippyForAll(message, null, 5f, 3f, 0.5f); } // Sunrise-End @@ -90,11 +91,15 @@ public sealed class TipsSystem : SharedTipsSystem } } - public void SendTippyForAll(string msg, float time = 1f) + public void SendTippyForAll(string msg, + EntProtoId? prototype = null, + float speakTime = 1f, + float slideTime = 1f, + float waddleInterval = 1f) { - var ev = new TippyEvent(msg) + var ev = new TippyEvent(msg, prototype, speakTime, slideTime, waddleInterval) { - SpeakTime = time + msg.Length * 0.05f + SpeakTime = speakTime + msg.Length * 0.05f }; RaiseNetworkEvent(ev); } diff --git a/Content.Server/Traitor/Uplink/UplinkSystem.cs b/Content.Server/Traitor/Uplink/UplinkSystem.cs index 4abc8dc0c0..793bd6cb33 100644 --- a/Content.Server/Traitor/Uplink/UplinkSystem.cs +++ b/Content.Server/Traitor/Uplink/UplinkSystem.cs @@ -127,7 +127,11 @@ public sealed class UplinkSystem : EntitySystem var pdaUid = containerSlot.ContainedEntity; // Sunrtise-Start - if (_tagSystem.HasTag(pdaUid.ContainedEntity.Value, "SunriseUplink")) + + if (pdaUid == null) + return null; + + if (_tagSystem.HasTag(pdaUid.Value, "SunriseUplink")) continue; // Sunrtise-End diff --git a/Content.Server/Vampire/VampireSystem.Abilities.cs b/Content.Server/Vampire/VampireSystem.Abilities.cs index 56c14c0150..5e9d3cb592 100644 --- a/Content.Server/Vampire/VampireSystem.Abilities.cs +++ b/Content.Server/Vampire/VampireSystem.Abilities.cs @@ -32,6 +32,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Flash; using Content.Shared.Flash.Components; using Content.Shared.Storage.Components; +using Content.Shared.Damage.Components; namespace Content.Server.Vampire; @@ -779,7 +780,7 @@ public sealed partial class VampireSystem //TODO: Replace with raised event? if (HasComp(args.Target)) { - _damageableSystem.TryChangeDamage(entity, VampireComponent.HolyDamage, true); + _damageableSystem.TryChangeDamage(entity.Owner, VampireComponent.HolyDamage, true); _popup.PopupEntity(Loc.GetString("vampire-ingest-holyblood"), entity, entity, PopupType.LargeCaution); _admin.Add(LogType.Damaged, LogImpact.Low, $"{ToPrettyString(entity):user} attempted to drink {volumeToConsume}u of {ToPrettyString(args.Target):target}'s holy blood"); return; diff --git a/Content.Server/Vampire/VampireSystem.Transform.cs b/Content.Server/Vampire/VampireSystem.Transform.cs index c7c414d9cf..ddac3d3c92 100644 --- a/Content.Server/Vampire/VampireSystem.Transform.cs +++ b/Content.Server/Vampire/VampireSystem.Transform.cs @@ -34,7 +34,7 @@ public sealed partial class VampireSystem RemComp(vampire); //Unsure, should vampires thirst.. or hunger? //Render immune to cold, but not heat - if (TryComp(vampire, out var temperatureComponent)) + if (TryComp(vampire, out var temperatureComponent)) temperatureComponent.ColdDamageThreshold = Atmospherics.TCMB; MakeVulnerableToHoly(vampire); diff --git a/Content.Server/Vampire/VampireSystem.cs b/Content.Server/Vampire/VampireSystem.cs index 1f5c30171f..fb0f3108c6 100644 --- a/Content.Server/Vampire/VampireSystem.cs +++ b/Content.Server/Vampire/VampireSystem.cs @@ -36,6 +36,8 @@ using Content.Shared.Movement.Systems; using Content.Shared.Nutrition.EntitySystems; using Content.Shared.StatusEffectNew; using Content.Shared.StatusEffectNew.Components; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; namespace Content.Server.Vampire; @@ -43,7 +45,6 @@ public sealed partial class VampireSystem : EntitySystem { [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly IAdminLogManager _admin = default!; - [Dependency] private readonly FoodSystem _food = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly BloodstreamSystem _blood = default!; [Dependency] private readonly RottingSystem _rotting = default!; @@ -339,7 +340,7 @@ public sealed partial class VampireSystem : EntitySystem private void DoSpaceDamage(EntityUid uid, VampireComponent comp, DamageableComponent damage) { var damageSpec = new DamageSpecifier(_prototypeManager.Index("Heat"), 2.5); - _damageableSystem.TryChangeDamage(uid, damageSpec, true, false, damage, uid); + _damageableSystem.TryChangeDamage(uid, damageSpec, true, false, uid); _popup.PopupEntity(Loc.GetString("vampire-startlight-burning"), uid, uid, PopupType.LargeCaution); } private bool IsInSpace(EntityUid vampireUid) diff --git a/Content.Server/Weapons/Melee/InfectOnMelee/InfectOnMeleeSystem.cs b/Content.Server/Weapons/Melee/InfectOnMelee/InfectOnMeleeSystem.cs index aabf74f0e4..dccba47523 100644 --- a/Content.Server/Weapons/Melee/InfectOnMelee/InfectOnMeleeSystem.cs +++ b/Content.Server/Weapons/Melee/InfectOnMelee/InfectOnMeleeSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Mindshield.Components; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Zombies; using Content.Shared.Weapons.Melee; +using Content.Shared.Damage.Components; namespace Content.Server.Weapons.Melee.InfectOnMelee; @@ -36,7 +37,7 @@ public sealed class InfectOnMeleeSystem : EntitySystem && !_mob.IsDead(entity) && _random.Prob(GenerateHitChance(entity, component)) && !HasComp(entity) - && !HasComp(entity) + && !HasComp(entity) && !HasComp(entity)) { _audio.PlayPvs(component.InfectionSound, uid); @@ -45,7 +46,7 @@ public sealed class InfectOnMeleeSystem : EntitySystem } } } - + private float GenerateHitChance(EntityUid enemy, InfectOnMeleeComponent component) { float chance = component.InfectionChance; @@ -59,7 +60,7 @@ public sealed class InfectOnMeleeSystem : EntitySystem chance = finalChance; } - + return chance; } } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 9c597c2ff2..fdbbeeaff4 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -1,96 +1,88 @@ -using System.Linq; using System.Numerics; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Atmos.Components; using Content.Server.Cargo.Systems; -using Content.Server.Interaction; -using Content.Server.Mech.Equipment.Components; -using Content.Server.Power.EntitySystems; using Content.Server.Weapons.Ranged.Components; -using Content.Server.Stunnable; -using Content.Server.Stunnable.Components; -using Content.Server.Emp; +using Content.Shared.Cargo; using Content.Shared.Damage; using Content.Shared.Damage.Systems; -using Content.Shared.Database; -using Content.Shared.Effects; -using Content.Shared.Interaction.Components; -using Content.Shared.Mech.Equipment.Components; using Content.Shared.Projectiles; -using Content.Shared.StatusEffect; using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Ranged; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; -using Content.Shared.Weapons.Reflect; -using Content.Shared.Damage.Components; +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; using Robust.Shared.Audio; using Robust.Shared.Map; -using Robust.Shared.Physics; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Utility; -using Robust.Shared.Containers; -using Content.Shared._Starlight.Weapon.Components; -using Robust.Shared.Physics.Dynamics; -using Content.Shared.Movement.Components; -using Robust.Shared.Random; -using Content.Shared.Decals; -using Content.Server.Body.Components; -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.Timing; -using Content.Server.Decals; + +#region Starlight using System; -using Content.Server.IgnitionSource; -using Content.Server.Atmos.EntitySystems; -using Microsoft.CodeAnalysis.Elfie.Diagnostics; -using Content.Server.Atmos.Components; -using Content.Shared._Starlight.Weapon; -using Robust.Shared.Maths; -using Content.Shared.Pinpointer; -using Robust.Server.GameObjects; using System.Collections.Generic; -using Content.Server.PowerCell; +using System.Linq; +using Content.Server.Atmos.Components; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Body.Components; +using Content.Server.Decals; +using Content.Server.Emp; +using Content.Server.IgnitionSource; +using Content.Server.Interaction; +using Content.Server.Mech.Equipment.Components; +using Content.Server.Power.EntitySystems; +using Content.Server.Stunnable.Components; +using Content.Server.Stunnable; using Content.Shared.Atmos.Components; using Content.Shared.Body.Components; -using Content.Shared.Cargo; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Damage.Components; +using Content.Shared.Decals; +using Content.Shared.Interaction.Components; +using Content.Shared.Mech.Components; +using Content.Shared.Mech.Equipment.Components; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.Pinpointer; +using Content.Shared.Standing; +using Content.Shared.StatusEffect; +using Content.Shared.Stunnable; +using Content.Shared.Weapons.Reflect; +using Content.Shared._Starlight.Weapon.Components; +using Content.Shared._Starlight.Weapon; +using Robust.Server.GameObjects; +using Robust.Shared.Containers; +using Robust.Shared.Maths; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Random; +using Robust.Shared.Timing; +#endregion Starlight namespace Content.Server.Weapons.Ranged.Systems; public sealed partial class GunSystem : SharedGunSystem { - [Dependency] private readonly IComponentFactory _factory = default!; - [Dependency] private readonly IRobustRandom _rand = default!; // 🌟Starlight🌟 - [Dependency] private readonly BatterySystem _battery = default!; - [Dependency] private readonly DamageExamineSystem _damageExamine = default!; [Dependency] private readonly PricingSystem _pricing = default!; - [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; - [Dependency] private readonly TransformSystem _transform = default!; // 🌟Starlight🌟 - [Dependency] private readonly SharedStaminaSystem _stamina = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedMapSystem _map = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; // 🌟Starlight🌟 - [Dependency] private readonly DecalSystem _decals = default!; // 🌟Starlight🌟 - [Dependency] private readonly FlammableSystem _flammableSystem = default!; // 🌟Starlight🌟 - [Dependency] private readonly AtmosphereSystem _atmosphere = default!; // 🌟Starlight🌟 - [Dependency] private readonly StunSystem _stunSystem = default!; // 🌟Starlight🌟 - [Dependency] private readonly EmpSystem _emp = default!; // 🌟Starlight🌟 + + #region Starlight + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly IComponentFactory _factory = default!; + [Dependency] private readonly IRobustRandom _rand = default!; + [Dependency] private readonly BatterySystem _battery = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly DecalSystem _decals = default!; + #endregion Starlight private const float DamagePitchVariation = 0.05f; - private string[] _bloodDecals = []; // 🌟Starlight🌟 public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnBallisticPrice); - CacheDecals(); - } - private void CacheDecals() // 🌟Starlight🌟 - { - _bloodDecals = _proto.EnumeratePrototypes().Where(x => x.Tags.Contains("BloodSplatter")).Select(x => x.ID).ToArray(); } + private void OnBallisticPrice(EntityUid uid, BallisticAmmoProviderComponent component, ref PriceCalculationEvent args) { if (string.IsNullOrEmpty(component.Proto) || component.UnspawnedCount == 0) @@ -134,9 +126,8 @@ public sealed partial class GunSystem : SharedGunSystem ? TransformSystem.WithEntityId(fromCoordinates, gridUid) : new EntityCoordinates(_map.GetMapOrInvalid(fromMap.MapId), fromMap.Position); - var pointerLength = mapDirection.Length(); // 🌟Starlight🌟 // Update shot based on the recoil - toMap = fromMap.Position + (angle.ToVec() * pointerLength); // 🌟Starlight🌟 + toMap = fromMap.Position + angle.ToVec() * mapDirection.Length(); mapDirection = toMap - fromMap.Position; var gunVelocity = Physics.GetMapLinearVelocity(fromEnt); @@ -144,8 +135,6 @@ public sealed partial class GunSystem : SharedGunSystem // DebugTools.Assert(direction != Vector2.Zero); var shotProjectiles = new List(ammo.Count); - bool bulletSoundCheck = false; //starlight - foreach (var (ent, shootable) in ammo) { // pneumatic cannon doesn't shoot bullets it just throws them, ignore ammo handling @@ -155,58 +144,9 @@ public sealed partial class GunSystem : SharedGunSystem continue; } + // TODO: Clean this up in a gun refactor at some point - too much copy pasting switch (shootable) { - //🌟Starlight🌟 - case HitScanCartridgeAmmoComponent cartridge: - if (!cartridge.Spent) - { - var hitscanPrototype = ProtoManager.Index(cartridge.Hitscan); - var hitHashSet = new HashSet(); - if (hitscanPrototype.Count > 1) - { - var spread = (hitscanPrototype.Spread + gun.Spread) / 2; - var spreadEvent = new GunGetAmmoSpreadEvent(spread); - RaiseLocalEvent(gunUid, ref spreadEvent); - - var angles = LinearSpreadWithRandom(mapAngle - (spreadEvent.Spread / 2), - mapAngle + (spreadEvent.Spread / 2), hitscanPrototype.Count, - 3f); - - List> hits = new(hitscanPrototype.Count); - for (var i = 0; i < hitscanPrototype.Count; i++) - hits.Add(Hitscan(gunUid, gun, fromCoordinates, user, fromMap, pointerLength, angles[i].ToVec(), hitscanPrototype, hitHashSet)); - - FireEffects(hits.ToList(), hitscanPrototype); - } - else - { - var hits = Hitscan(gunUid, gun, fromCoordinates, user, fromMap, pointerLength, mapDirection, hitscanPrototype, hitHashSet); - FireEffects([hits], hitscanPrototype); - } - - RaiseLocalEvent(ent!.Value, new AmmoShotEvent() - { - FiredProjectiles = shotProjectiles, - }); - - SetCartridgeSpent(ent!.Value, cartridge, true); - - if (cartridge.DeleteOnSpawn) - Del(ent.Value); - } - else - { - userImpulse = false; - Audio.PlayPredicted(gun.SoundEmpty, gunUid, user); - } - - // Something like ballistic might want to leave it in the container still - if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(ent!.Value) && !gun.Pump) - EjectCartridge(ent.Value, angle); - - Dirty(ent!.Value, cartridge); - break; // Cartridge shoots something else case CartridgeAmmoComponent cartridge: if (!cartridge.Spent) @@ -243,133 +183,21 @@ public sealed partial class GunSystem : SharedGunSystem CreateAndFireProjectiles(ent.Value, newAmmo); break; - case HitscanPrototype hitscan: + case HitscanAmmoComponent: + if (ent == null) + break; - EntityUid? lastHit = null; - List<(EntityCoordinates fromCoordinates, float distance, Angle mapDirection, EntityUid? hitEntity)> effects = []; - - var from = fromMap; - // can't use map coords above because funny FireEffects - var fromEffect = fromCoordinates; - var dir = mapDirection.Normalized(); - - //in the situation when user == null, means that the cannon fires on its own (via signals). And we need the gun to not fire by itself in this case - var lastUser = user ?? gunUid; - - if (hitscan.Reflective != ReflectType.None) + var hitscanEv = new HitscanTraceEvent { + FromCoordinates = fromCoordinates, + ShotDirection = mapDirection.Normalized(), + Gun = gunUid, + Shooter = user, + Target = gun.Targets, + }; + RaiseLocalEvent(ent.Value, ref hitscanEv); - for (var reflectAttempt = 0; reflectAttempt < 3; reflectAttempt++) - { - var ray = new CollisionRay(from.Position, dir, hitscan.CollisionMask); - var rayCastResults = - Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList(); - if (!rayCastResults.Any()) - break; - - var result = rayCastResults[0]; - - // Check if laser is shot from in a container - if (!_container.IsEntityOrParentInContainer(lastUser)) - { - // Checks if the laser should pass over unless targeted by its user - foreach (var collide in rayCastResults) - { - if (!gun.Targets.Contains(collide.HitEntity) && // Sunrise-Edit - CompOrNull(collide.HitEntity)?.Active == true) - { - continue; - } - - result = collide; - break; - } - } - - var hit = result.HitEntity; - lastHit = hit; - - effects.Add((fromEffect, result.Distance, dir.Normalized().ToAngle(), hit)); - - var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false); - RaiseLocalEvent(hit, ref ev); - - if (!ev.Reflected) - break; - - fromEffect = Transform(hit).Coordinates; - from = TransformSystem.ToMapCoordinates(fromEffect); - dir = ev.Direction; - lastUser = hit; - } - } - - if (lastHit != null) - { - var hitEntity = lastHit.Value; - if (hitscan.StaminaDamage > 0f) - _stamina.TakeStaminaDamage(hitEntity, hitscan.StaminaDamage, source: user); - - if (TryComp(hitEntity, out var status)) - { - _stunSystem.TryAddParalyzeDuration(hitEntity, TimeSpan.FromSeconds(hitscan.StunAmount)); - - _stunSystem.TryKnockdown(hitEntity, TimeSpan.FromSeconds(hitscan.KnockdownAmount), true); - } - - if (hitscan.Ignite) - { - if (TryComp(hitEntity, out var flammable)) - _flammableSystem.SetFireStacks(hitEntity, flammable.FireStacks + (flammable.MinIgnitionTemperature / hitscan.Temperature), flammable, true); - - if (Transform(hitEntity) is TransformComponent xform && xform.GridUid is { } hitGridUid) - { - var position = _transform.GetGridOrMapTilePosition(hitEntity, xform); - _atmosphere.HotspotExpose(hitGridUid, position, hitscan.Temperature, 50, user, true); - } - } - - if (hitscan.Emp != null) - _emp.EmpPulse(_transform.GetMapCoordinates(hitEntity), hitscan.Emp.Range, hitscan.Emp.EnergyConsumption, TimeSpan.FromSeconds(hitscan.Emp.DisableDuration)); - - var dmg = hitscan.Damage; - - var hitName = ToPrettyString(hitEntity); - if (dmg != null) - dmg = Damageable.TryChangeDamage(hitEntity, dmg, origin: user); - - // check null again, as TryChangeDamage returns modified damage values - if (dmg != null) - { - if (!Deleted(hitEntity)) - { - if (dmg.AnyPositive()) - { - _color.RaiseEffect(Color.Red, new List() { hitEntity }, Filter.Pvs(hitEntity, entityManager: EntityManager)); - } - - // TODO get fallback position for playing hit sound. - PlayImpactSound(hitEntity, dmg, hitscan.Sound, hitscan.ForceSound); - } - - if (user != null) - { - Logs.Add(LogType.HitScanHit, - $"{ToPrettyString(user.Value):user} hit {hitName:target} using hitscan and dealt {dmg.GetTotal():damage} damage"); - } - else - { - Logs.Add(LogType.HitScanHit, - $"{hitName:target} hit by hitscan dealing {dmg.GetTotal():damage} damage"); - } - } - } - else - { - effects.Add((fromEffect, hitscan.MaxLength, dir.ToAngle(), null)); - } - - FireEffects([effects], hitscan); + Del(ent); Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); break; @@ -386,6 +214,26 @@ public sealed partial class GunSystem : SharedGunSystem void CreateAndFireProjectiles(EntityUid ammoEnt, AmmoComponent ammoComp) { + // Startlight-edit: start + var isMechShooter = user != null && TryComp(user.Value, out _); + const float MechMuzzleOffset = 0.8f; + + EntityCoordinates SpawnFrom(Angle angle) + { + if (!isMechShooter) + return fromEnt; + + var localAngle = angle; + if (TryComp(fromEnt.EntityId, out TransformComponent? anchorXform)) + { + var anchorRot = _transform.GetWorldRotation(anchorXform); + localAngle -= anchorRot; + } + + var dir = localAngle.ToVec().Normalized(); + return fromEnt.Offset(dir * MechMuzzleOffset); + } + // Startlight-edit: end if (TryComp(ammoEnt, out var ammoSpreadComp)) { var spreadEvent = new GunGetAmmoSpreadEvent(ammoSpreadComp.Spread); @@ -393,19 +241,35 @@ public sealed partial class GunSystem : SharedGunSystem var angles = LinearSpread(mapAngle - spreadEvent.Spread / 2, mapAngle + spreadEvent.Spread / 2, ammoSpreadComp.Count); - + // Startlight-edit: start + if (isMechShooter) + { + var spawn = SpawnFrom(angles[0]); + _transform.SetCoordinates(ammoEnt, Transform(ammoEnt), spawn); + } + // Startlight-edit: end ShootOrThrow(ammoEnt, angles[0].ToVec(), gunVelocity, gun, gunUid, user); shotProjectiles.Add(ammoEnt); for (var i = 1; i < ammoSpreadComp.Count; i++) { - var newuid = Spawn(ammoSpreadComp.Proto, fromEnt); + // Startlight-edit: start + var spawn = isMechShooter ? SpawnFrom(angles[i]) : fromEnt; + var newuid = Spawn(ammoSpreadComp.Proto, spawn); + // Startlight-edit: end ShootOrThrow(newuid, angles[i].ToVec(), gunVelocity, gun, gunUid, user); shotProjectiles.Add(newuid); } } else { + // Startlight-edit: start + if (isMechShooter) + { + var spawn = SpawnFrom(mapDirection.ToAngle()); + _transform.SetCoordinates(ammoEnt, Transform(ammoEnt), spawn); + } + // Startlight-edit: end ShootOrThrow(ammoEnt, mapDirection, gunVelocity, gun, gunUid, user); shotProjectiles.Add(ammoEnt); } @@ -413,189 +277,6 @@ public sealed partial class GunSystem : SharedGunSystem MuzzleFlash(gunUid, ammoComp, mapDirection.ToAngle(), user); Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); } - - //🌟Starlight🌟 - // This is fucked already, we need to just comment out the entire Wizden shooting system, take over full maintenance, and rewrite it from scratch. - List<(EntityCoordinates, float, Angle, EntityUid?)> Hitscan - ( - EntityUid gunUid, - GunComponent gun, - EntityCoordinates fromCoordinates, - EntityUid? user, - MapCoordinates fromMap, - float pointer, - Vector2 mapDirection, - HitscanPrototype hitscan, - HashSet hitHashSet - ) - { - EntityUid? lastHit = null; - List<(EntityCoordinates fromCoordinates, float distance, Angle mapDirection, EntityUid? hitEntity)> effects = []; - - var from = fromMap; - // can't use map coords above because funny FireEffects - var fromEffect = fromCoordinates; - var dir = mapDirection.Normalized(); - - //in the situation when user == null, means that the cannon fires on its own (via signals). And we need the gun to not fire by itself in this case - var lastUser = user ?? gunUid; - hitHashSet.Clear(); - hitHashSet.Add(lastUser); - - if (hitscan.Reflective != ReflectType.None) - { - for (var reflectAttempt = 0; reflectAttempt < hitscan.Steps; reflectAttempt++) - { - var ray = new CollisionRay(from.Position, dir, hitscan.CollisionMask); - var rayCastResults = Physics.IntersectRay(from.MapId, ray, hitscan.MaxLength, lastUser, false).ToList(); - if (rayCastResults.Count == 0) - break; - - var result = rayCastResults[0]; - - // Check if laser is shot from in a container - if (!_container.IsEntityOrParentInContainer(lastUser)) - { - // Checks if the laser should pass over unless targeted by its user - foreach (var collide in rayCastResults) - { - if (!gun.Targets.Contains(collide.HitEntity) && // Sunrise-Edit - CompOrNull(collide.HitEntity)?.Active == true) - continue; - - if (collide.Distance < pointer - 2f && HasComp(collide.HitEntity)) - { - if (pointer - collide.Distance > 4f) continue; - - var chance = Math.Clamp(1f - ((collide.Distance - 2f) / 2f), 0f, 1f); - if (!_rand.Prob(chance)) continue; - } - if (!hitHashSet.Contains(collide.HitEntity)) - hitHashSet.Add(collide.HitEntity); - else - continue; - - result = collide; - break; - } - } - - var hit = result.HitEntity; - lastHit = hit; - - effects.Add((fromEffect, result.Distance, dir.Normalized().ToAngle(), hit)); - - if (hitscan.Reflective != ReflectType.None) - { - var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false); - RaiseLocalEvent(hit, ref ev); - - if (ev.Reflected) - { - hitHashSet.Clear(); - hitHashSet.Add(hit); - fromEffect = Transform(hit).Coordinates; - from = TransformSystem.ToMapCoordinates(fromEffect); - dir = ev.Direction; - lastUser = hit; - continue; - } - } - - if (hitscan.RicochetChance > 0f) - { - var ev = new HitScanRicochetAttemptEvent(hitscan.RicochetChance, from.Position, dir, false); - RaiseLocalEvent(hit, ref ev); - - if (ev.Ricocheted) - { - hitHashSet.Clear(); - hitHashSet.Add(hit); - fromEffect = _transform.ToCoordinates(result.HitEntity, new MapCoordinates(result.HitPos, fromMap.MapId)); - from = TransformSystem.ToMapCoordinates(fromEffect); - dir = ev.Dir; - lastUser = hit; - continue; - } - } - - Hit(user, hitscan, lastHit.Value); - - if (hitscan.PierceChance >= 1f || (hitscan.PierceChance > 0f && _rand.Prob(hitscan.PierceChance))) - { - var ev = new HitScanPierceAttemptEvent(hitscan.PierceLevel, true); - RaiseLocalEvent(hit, ref ev); - - if (ev.Pierced) - { - var random = Random.NextFloat(-hitscan.Derivation, hitscan.Derivation); - fromEffect = _transform.ToCoordinates(result.HitEntity, new MapCoordinates(result.HitPos, fromMap.MapId)); - from = TransformSystem.ToMapCoordinates(fromEffect); - dir = (dir.ToAngle() + random).ToVec(); - lastUser = hit; - continue; - } - } - break; - } - if (lastHit == null) - effects.Add((fromEffect, hitscan.MaxLength, dir.ToAngle(), null)); - - } - - // Starlight confirm bullet sound should play - bulletSoundCheck = true; - - return effects; - - void Hit(EntityUid? user, HitscanPrototype hitscan, EntityUid hitEntity) - { - if (Deleted(hitEntity)) return; - if (hitscan.StaminaDamage > 0f) - _stamina.TakeStaminaDamage(hitEntity, hitscan.StaminaDamage, source: user); - - var dmg = hitscan.Damage; - - var hitName = ToPrettyString(hitEntity); - if (dmg != null) - dmg = Damageable.TryChangeDamage( - hitEntity, - dmg, - ignoreResistances: hitscan.IgnoreResistances, - origin: user, - armorPenetration: hitscan.ArmorPenetration, - canHeal: false - ); - - // check null again, as TryChangeDamage returns modified damage values - if (dmg != null) - { - if (dmg.AnyPositive()) - { - _color.RaiseEffect(Color.Red, [hitEntity], Filter.Pvs(hitEntity, entityManager: EntityManager)); - } - - // TODO get fallback position for playing hit sound. - PlayImpactSound(hitEntity, dmg, hitscan.Sound, hitscan.ForceSound); - - if (user != null) - { - Logs.Add(LogType.HitScanHit, - $"{ToPrettyString(user.Value):user} hit {hitName:target} using hitscan and dealt {dmg.GetTotal():damage} damage"); - } - else - { - Logs.Add(LogType.HitScanHit, - $"{hitName:target} hit by hitscan dealing {dmg.GetTotal():damage} damage"); - } - } - } - } - - //starlight check to see if bullet sound should play - if (bulletSoundCheck){ - Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user); - } } private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid gunUid, EntityUid? user) @@ -607,6 +288,24 @@ public sealed partial class GunSystem : SharedGunSystem Dirty(uid, targeted); } + // Starlight start - cartridges can hold hitscans + if (HasComp(uid)) + { + var hitscanEv = new HitscanTraceEvent + { + FromCoordinates = EntityManager.GetComponent(uid).Coordinates, + ShotDirection = mapDirection.Normalized(), + Gun = gunUid, + Shooter = user, + Target = gun.Targets, + }; + RaiseLocalEvent(uid, ref hitscanEv); + + Del(uid); + return; + } + // Starlight end - cartridges can hold hitscans + // Do a throw if (!HasComp(uid)) { @@ -686,7 +385,7 @@ public sealed partial class GunSystem : SharedGunSystem RaiseNetworkEvent(message, filter); } - public void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound) + public override void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound) { DebugTools.Assert(!Deleted(otherEntity), "Impact sound entity was deleted"); @@ -717,102 +416,4 @@ public sealed partial class GunSystem : SharedGunSystem Audio.PlayPvs(weaponSound, otherEntity); } } - - // TODO: Pseudo RNG so the client can predict these. - #region Hitscan effects - // 🌟Starlight🌟 - private void FireEffects(List> hits, HitscanPrototype hitscan) - { - if (hits.Count == 0) return; - var hitscanEvent = new HitscanEvent - { - Hitscan = hitscan.ID, - Effects = new Effect[hits.Count][] - }; - var spreadIndex = -1; - HashSet pvs = []; - - foreach (var hit in hits) - { - spreadIndex++; - var index = -1; - - hitscanEvent.Effects[spreadIndex] = new Effect[hit.Count]; - ref var effects = ref hitscanEvent.Effects[spreadIndex]; - - foreach (var item in hit) - { - var (fromCoordinates, distance, angle, hitEntity) = item; - - var fromXform = Transform(fromCoordinates.EntityId); - - var gridUid = fromXform.GridUid; - - if (gridUid != fromCoordinates.EntityId && TryComp(gridUid, out TransformComponent? gridXform)) - { - var (_, gridRot, gridInvMatrix) = TransformSystem.GetWorldPositionRotationInvMatrix(gridXform); - var map = _transform.ToMapCoordinates(fromCoordinates); - fromCoordinates = new EntityCoordinates(gridUid.Value, Vector2.Transform(map.Position, gridInvMatrix)); - angle -= gridRot; - } - else - { - angle -= _transform.GetWorldRotation(fromXform); - } - - index++; - effects[index] = new Effect - { - Angle = angle, - Distance = distance, - }; - ref var effect = ref effects[index]; - - if (distance >= 1f) - { - var muzzleCoords = fromCoordinates.Offset(angle.ToVec().Normalized() / 2); - var travelCoords = fromCoordinates.Offset(angle.ToVec() * (distance + 0.5f) / 2); - effect.MuzzleCoordinates = GetNetCoordinates(muzzleCoords); - effect.TravelCoordinates = GetNetCoordinates(travelCoords); - } - var impactCoords = fromCoordinates.Offset(angle.ToVec() * distance); - effect.ImpactCoordinates = GetNetCoordinates(impactCoords); - - if (hitEntity is not null) - { - if (hitscan.Reflective == ReflectType.NonEnergy) - { - if (TryComp(hitEntity, out var bloodstream)) - { - Timer.Spawn(200, () => - { - var color = _proto.Index(bloodstream.BloodReagent).SubstanceColor; - // A flash of the neuralyzer, then a man in a black suit says that you didn’t see any “vector crutch” here, and if you did—read it again. - var coords = fromCoordinates.Offset((angle.ToVec() * (distance + 1.3f)) + new Vector2(-0.5f, -0.5f)); - _decals.TryAddDecal(_rand.Pick(_bloodDecals), coords, out _, color, angle + Angle.FromDegrees(-45), cleanable: true); - }); - } - else - { - effect.ImpactEnt = GetNetEntity(hitEntity.Value); - } - } - } - - pvs.Add(fromCoordinates); - } - } - - - if (pvs.Count > 0) - { - var filter = Filter.Empty(); - foreach (var pos in pvs.Where(x => x.IsValid(EntityManager))) - filter.Merge(Filter.Pvs(pos, entityMan: EntityManager)); - - RaiseNetworkEvent(hitscanEvent, filter); - } - } - - #endregion } diff --git a/Content.Server/_Starlight/Actions/Stasis/StasisSystem.cs b/Content.Server/_Starlight/Actions/Stasis/StasisSystem.cs index 061b1626b5..efcd70354a 100644 --- a/Content.Server/_Starlight/Actions/Stasis/StasisSystem.cs +++ b/Content.Server/_Starlight/Actions/Stasis/StasisSystem.cs @@ -10,6 +10,8 @@ using Content.Shared.Mobs.Components; using Content.Shared._Starlight.Actions.Stasis; using Content.Shared.Body.Components; using Robust.Shared.Player; +using Content.Shared.Damage.Systems; +using Content.Shared.Damage.Components; namespace Content.Server._Starlight.Actions.Stasis; diff --git a/Content.Server/_Starlight/Combat/Ranged/PierceSystem.cs b/Content.Server/_Starlight/Combat/Ranged/PierceSystem.cs index a1bffa3832..54fb0d65ee 100644 --- a/Content.Server/_Starlight/Combat/Ranged/PierceSystem.cs +++ b/Content.Server/_Starlight/Combat/Ranged/PierceSystem.cs @@ -9,6 +9,7 @@ using Content.Shared._Starlight.Combat.Ranged.Pierce; using Content.Shared.Armor; using Content.Shared.Damage; using Content.Shared.Inventory; +using Content.Shared.Weapons.Hitscan.Events; namespace Content.Server._Starlight.Combat.Ranged; diff --git a/Content.Server/_Starlight/Energy/Supermatter/SupermatterSystem.cs b/Content.Server/_Starlight/Energy/Supermatter/SupermatterSystem.cs index b9862c5a25..24679de501 100644 --- a/Content.Server/_Starlight/Energy/Supermatter/SupermatterSystem.cs +++ b/Content.Server/_Starlight/Energy/Supermatter/SupermatterSystem.cs @@ -6,7 +6,9 @@ using Content.Server.Radio.EntitySystems; using Content.Shared.Abilities.Goliath; using Content.Shared.Atmos; using Content.Shared.Damage; +using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; +using Content.Shared.Damage.Systems; using Content.Shared.Ghost; using Content.Shared.Interaction; using Content.Shared.Projectiles; diff --git a/Content.Server/_Starlight/Medical/Surgery/OrganSystem.cs b/Content.Server/_Starlight/Medical/Surgery/OrganSystem.cs index b8cd4a0c1b..e444088b07 100644 --- a/Content.Server/_Starlight/Medical/Surgery/OrganSystem.cs +++ b/Content.Server/_Starlight/Medical/Surgery/OrganSystem.cs @@ -1,6 +1,8 @@ using Content.Server.Humanoid; using Content.Shared._Sunrise.Antags.Abductor; using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Eye.Blinding.Components; using Content.Shared.Eye.Blinding.Systems; using Content.Shared.Speech.Muting; @@ -58,9 +60,9 @@ public sealed partial class OrganSystem : EntitySystem { if (!TryComp(args.Body, out var bodyDamageable)) return; - var change = _damageableSystem.TryChangeDamage(args.Body, ent.Comp.Damage, true, false, bodyDamageable); + var change = _damageableSystem.ChangeDamage(args.Body, ent.Comp.Damage, true, false); if (change is not null) - _damageableSystem.TryChangeDamage(ent.Owner, change.Invert(), true, false, ent.Comp); + _damageableSystem.ChangeDamage(ent.Owner, change.Invert(), true, false); } private void OnOrganExtracted(Entity ent, ref SurgeryOrganExtracted args) { @@ -68,11 +70,10 @@ public sealed partial class OrganSystem : EntitySystem || damageRule.Damage is null || !TryComp(args.Body, out var bodyDamageable)) return; - var change = _damageableSystem.TryChangeDamage(args.Body, damageRule.Damage.Invert(), true, false, bodyDamageable); + var change = _damageableSystem.ChangeDamage(args.Body, damageRule.Damage.Invert(), true, false); if (change is not null) - _damageableSystem.TryChangeDamage(ent.Owner, change.Invert(), true, false, ent.Comp); + _damageableSystem.ChangeDamage(ent.Owner, change.Invert(), true, false); } - private void OnTongueImplanted(Entity ent, ref SurgeryOrganImplantationCompleted args) { if (HasComp(args.Body) || !ent.Comp.IsMuted) return; diff --git a/Content.Server/_Starlight/Medical/Surgery/SurgerySystem.Steps.cs b/Content.Server/_Starlight/Medical/Surgery/SurgerySystem.Steps.cs index d84849638c..c8a2200d4d 100644 --- a/Content.Server/_Starlight/Medical/Surgery/SurgerySystem.Steps.cs +++ b/Content.Server/_Starlight/Medical/Surgery/SurgerySystem.Steps.cs @@ -13,12 +13,14 @@ using Content.Shared.Traits.Assorted; using Microsoft.CodeAnalysis; using Content.Server._Starlight.Medical.Limbs; using Content.Server.Administration.Systems; +using Content.Shared.Bed.Sleep; +using Content.Shared.Damage.Components; namespace Content.Server.Starlight.Medical.Surgery; // Based on the RMC14. // https://github.com/RMC-14/RMC-14 -// +// //This file is already overloaded with responsibilities, //it’s time to break its functionality into different systems. //However, I don’t want to touch the official systems, so I need to come up with extensions for them. @@ -27,6 +29,7 @@ public sealed partial class SurgerySystem : SharedSurgerySystem [Dependency] private readonly IComponentFactory _compFactory = default!; [Dependency] private readonly LimbSystem _limbSystem = default!; [Dependency] private readonly StarlightEntitySystem _entity = default!; + [Dependency] private readonly SleepingSystem _sleeping = default!; public void InitializeSteps() { @@ -135,11 +138,11 @@ public sealed partial class SurgerySystem : SharedSurgerySystem private void OnStepEmoteEffectComplete(Entity ent, ref SurgeryStepEvent args) { - - if (!HasComp(args.Body)) - { - _chat.TryEmoteWithChat(args.Body, ent.Comp.Emote); - } + + if (!HasComp(args.Body) && !HasComp(args.Body)) + _chat.TryEmoteWithChat(args.Body, ent.Comp.Emote); + else + _sleeping.TryWaking(args.Body); // If the patient sleeping without n2o or reagents, wake them up. } private void OnStepSpawnComplete(Entity ent, ref SurgeryStepEvent args) @@ -148,7 +151,7 @@ public sealed partial class SurgerySystem : SharedSurgerySystem SpawnAtPosition(ent.Comp.Entity, xform.Coordinates); } - private void OnStepAttachLimbComplete(Entity _, string slot, ref SurgeryStepEvent args) + private void OnStepAttachLimbComplete(Entity _, string slot, ref SurgeryStepEvent args) => args.IsCancelled = args.Tools.Count == 0 || !(args.Tools.FirstOrDefault() is var limdId) || !TryComp(limdId, out var limb) @@ -157,16 +160,16 @@ public sealed partial class SurgerySystem : SharedSurgerySystem || !_limbSystem.AttachLimb((args.Body, humanoid), slot, (args.Part, part), (limdId, limb)); private void OnStepAttachItemComplete(Entity ent, string slot, ref SurgeryStepEvent args) - => args.IsCancelled = args.Tools.Count == 0 - || !(args.Tools.FirstOrDefault() is var itemId) - || !TryComp(itemId, out MetaDataComponent? metadata) - || HasComp(itemId) - || !TryComp(args.Part, out BodyPartComponent? limb) + => args.IsCancelled = args.Tools.Count == 0 + || !(args.Tools.FirstOrDefault() is var itemId) + || !TryComp(itemId, out MetaDataComponent? metadata) + || HasComp(itemId) + || !TryComp(args.Part, out BodyPartComponent? limb) || !_limbSystem.AttachItem(args.Body, slot, (args.Part, limb), (itemId, metadata)); private void OnStepAmputationComplete(Entity ent, ref SurgeryStepEvent args) { - if (_entity.TryEntity(args.Body, out var body) + if (_entity.TryEntity(args.Body, out var body) && _entity.TryEntity(args.Part, out var limb)) _limbSystem.Amputatate(body, limb); } diff --git a/Content.Server/_Starlight/Medical/Surgery/SurgerySystem.cs b/Content.Server/_Starlight/Medical/Surgery/SurgerySystem.cs index f909f0b343..ab62a3ccd6 100644 --- a/Content.Server/_Starlight/Medical/Surgery/SurgerySystem.cs +++ b/Content.Server/_Starlight/Medical/Surgery/SurgerySystem.cs @@ -14,6 +14,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Utility; using Content.Server.Administration.Systems; +using Content.Shared.Damage.Systems; namespace Content.Server.Starlight.Medical.Surgery; // Based on the RMC14. diff --git a/Content.Server/_Starlight/Weapon/WeaponDismantleOnShootSystem.cs b/Content.Server/_Starlight/Weapon/WeaponDismantleOnShootSystem.cs index 2e3c34d9ae..d937565346 100644 --- a/Content.Server/_Starlight/Weapon/WeaponDismantleOnShootSystem.cs +++ b/Content.Server/_Starlight/Weapon/WeaponDismantleOnShootSystem.cs @@ -17,6 +17,7 @@ using Content.Shared.Wieldable; using Content.Shared.Weapons.Ranged.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; +using Content.Shared.Damage.Systems; namespace Content.Server._Starlight.Weapon.Systems; public sealed partial class WeaponDismantleOnShootSystem : SharedWeaponDismantleOnShootSystem @@ -43,9 +44,12 @@ public sealed partial class WeaponDismantleOnShootSystem : SharedWeaponDismantle if (DismantleCheck(ent, ref args) == false) return; + if (!args.Shooter.HasValue) + return; + //apply the damage to the shooter //get the shooters damageable component - Damageable.TryChangeDamage(args.Shooter, ent.Comp.SelfDamage, origin:args.Shooter); + Damageable.TryChangeDamage(args.Shooter.Value, ent.Comp.SelfDamage, origin: args.Shooter.Value); //we need the user past this point if (!args.Shooter.HasValue) @@ -58,7 +62,7 @@ public sealed partial class WeaponDismantleOnShootSystem : SharedWeaponDismantle if (!TryComp(ent, out var gunComponent)) return; - + var toCoordinates = gunComponent.ShootCoordinates; if (toCoordinates == null) diff --git a/Content.Server/_Sunrise/Animations/EmoteAnimationSystem.cs b/Content.Server/_Sunrise/Animations/EmoteAnimationSystem.cs index 33c4a1817c..aa19c23a98 100644 --- a/Content.Server/_Sunrise/Animations/EmoteAnimationSystem.cs +++ b/Content.Server/_Sunrise/Animations/EmoteAnimationSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Chat; using Content.Shared.Chat.Prototypes; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; +using Content.Shared.Damage.Systems; using Content.Shared.Emoting; using Content.Shared.Gravity; using Content.Shared.Standing; @@ -88,7 +89,7 @@ public sealed class EmoteAnimationSystem : EntitySystem if (emoteId == "FallOnNeck") { var damage = new DamageSpecifier(_prototypeManager.Index("Blunt"), 100); - _damageableSystem.TryChangeDamage(uid, damage, true, useVariance: false, useModifier: false); + _damageableSystem.ChangeDamage(uid, damage, true, useVariance: false, ignoreGlobalModifiers: true); } component.AnimationId = emoteId; diff --git a/Content.Server/_Sunrise/Anomaly/Systems/AnomalyAutoInjectorSystem.cs b/Content.Server/_Sunrise/Anomaly/Systems/AnomalyAutoInjectorSystem.cs index 1836439338..e2f3df5901 100644 --- a/Content.Server/_Sunrise/Anomaly/Systems/AnomalyAutoInjectorSystem.cs +++ b/Content.Server/_Sunrise/Anomaly/Systems/AnomalyAutoInjectorSystem.cs @@ -23,6 +23,7 @@ using Content.Shared.Humanoid; using System.Diagnostics.CodeAnalysis; using Content.Shared._Sunrise.Drugs; using Content.Shared.Anomaly.Components; +using Content.Shared.Damage.Systems; namespace Content.Server._Sunrise.Anomaly.Systems; diff --git a/Content.Server/_Sunrise/Antags/Abductor/EntitySystems/AbductorSystem.Organs.cs b/Content.Server/_Sunrise/Antags/Abductor/EntitySystems/AbductorSystem.Organs.cs index 756b35fba3..5b293532ca 100644 --- a/Content.Server/_Sunrise/Antags/Abductor/EntitySystems/AbductorSystem.Organs.cs +++ b/Content.Server/_Sunrise/Antags/Abductor/EntitySystems/AbductorSystem.Organs.cs @@ -22,6 +22,7 @@ using Content.Shared._Sunrise.VentCraw; using Content.Shared.CombatMode.Pacification; using Content.Shared.Starlight.Medical.Surgery.Events; using Content.Server.Objectives.Components; +using Content.Shared.Damage.Systems; namespace Content.Server._Sunrise.Antags.Abductor; diff --git a/Content.Server/_Sunrise/AoEHeal/AoEHealSystem.cs b/Content.Server/_Sunrise/AoEHeal/AoEHealSystem.cs index 62504aa3e8..752f140253 100644 --- a/Content.Server/_Sunrise/AoEHeal/AoEHealSystem.cs +++ b/Content.Server/_Sunrise/AoEHeal/AoEHealSystem.cs @@ -1,5 +1,7 @@ using System.Linq; using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Mobs.Systems; using Content.Shared.Whitelist; using Robust.Shared.Timing; @@ -45,7 +47,7 @@ public sealed class AoEHealSystem : EntitySystem target.Comp.Damage.GetTotal() < threshold * (1f - aoEHealComponent.Threshold)) // Не лечим если урона мало continue; - _damageableSystem.TryChangeDamage(target, aoEHealComponent.Damage); + _damageableSystem.TryChangeDamage(target.Owner, aoEHealComponent.Damage); } } } diff --git a/Content.Server/_Sunrise/BloodCult/Items/Systems/BloodCultWeaponSystem.cs b/Content.Server/_Sunrise/BloodCult/Items/Systems/BloodCultWeaponSystem.cs index 33773a1006..18fbd96738 100644 --- a/Content.Server/_Sunrise/BloodCult/Items/Systems/BloodCultWeaponSystem.cs +++ b/Content.Server/_Sunrise/BloodCult/Items/Systems/BloodCultWeaponSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Body.Systems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.FixedPoint; using Content.Shared.Interaction; using Content.Shared.Mobs.Components; diff --git a/Content.Server/_Sunrise/BloodCult/Items/Systems/CultBloodSpearSystem.cs b/Content.Server/_Sunrise/BloodCult/Items/Systems/CultBloodSpearSystem.cs index 27db13ef9f..73064237e5 100644 --- a/Content.Server/_Sunrise/BloodCult/Items/Systems/CultBloodSpearSystem.cs +++ b/Content.Server/_Sunrise/BloodCult/Items/Systems/CultBloodSpearSystem.cs @@ -7,6 +7,7 @@ using Content.Shared._Sunrise.BloodCult.Items; using Content.Shared.Actions; using Content.Shared.Actions.Components; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.Mobs.Components; using Content.Shared.Popups; using Content.Shared.Stunnable; diff --git a/Content.Server/_Sunrise/BloodCult/Items/Systems/CultBloodSpellSystem.cs b/Content.Server/_Sunrise/BloodCult/Items/Systems/CultBloodSpellSystem.cs index 3f2225d0f4..bfda86c54d 100644 --- a/Content.Server/_Sunrise/BloodCult/Items/Systems/CultBloodSpellSystem.cs +++ b/Content.Server/_Sunrise/BloodCult/Items/Systems/CultBloodSpellSystem.cs @@ -10,7 +10,9 @@ using Content.Shared.Body.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Damage; +using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; +using Content.Shared.Damage.Systems; using Content.Shared.Examine; using Content.Shared.FixedPoint; using Content.Shared.Fluids.Components; diff --git a/Content.Server/_Sunrise/BloodCult/Items/Systems/CultRobeModifierSystem.cs b/Content.Server/_Sunrise/BloodCult/Items/Systems/CultRobeModifierSystem.cs index 9206696441..af2ae6130e 100644 --- a/Content.Server/_Sunrise/BloodCult/Items/Systems/CultRobeModifierSystem.cs +++ b/Content.Server/_Sunrise/BloodCult/Items/Systems/CultRobeModifierSystem.cs @@ -1,6 +1,8 @@ using Content.Server._Sunrise.BloodCult.Items.Components; using Content.Shared._Sunrise.BloodCult.Components; using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Inventory.Events; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; diff --git a/Content.Server/_Sunrise/BloodCult/Pylon/PylonSystem.cs b/Content.Server/_Sunrise/BloodCult/Pylon/PylonSystem.cs index a3122143e5..363b198620 100644 --- a/Content.Server/_Sunrise/BloodCult/Pylon/PylonSystem.cs +++ b/Content.Server/_Sunrise/BloodCult/Pylon/PylonSystem.cs @@ -6,6 +6,8 @@ using Content.Shared._Sunrise.BloodCult.Components; using Content.Shared._Sunrise.BloodCult.Pylon; using Content.Shared.Body.Components; using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Doors.Components; using Content.Shared.Interaction; using Content.Shared.Maps; @@ -210,7 +212,7 @@ public sealed class PylonSystem : EntitySystem _blood.TryModifyBleedAmount((playerEntity, bloodstream), -comp.BleedReductionAmount); } - if (_blood.GetBloodLevelPercentage((playerEntity, bloodstream)) < bloodstream.BloodMaxVolume) + if (_blood.GetBloodLevel((playerEntity, bloodstream)) < bloodstream.BloodReferenceSolution.MaxVolume) { _blood.TryModifyBloodLevel((playerEntity, bloodstream), comp.BloodRefreshAmount); } diff --git a/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Actions.cs b/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Actions.cs index 7c68dbc49c..7697a9073b 100644 --- a/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Actions.cs +++ b/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Actions.cs @@ -381,7 +381,7 @@ namespace Content.Server._Sunrise.BloodCult.Runes.Systems if (!_entityManager.TryGetComponent(material, out var stackNew)) return; - stackNew.Count = count; + _stack.SetCount((material, stackNew), count); _popupSystem.PopupEntity(Loc.GetString($"Пласталь превращается в {MetaData(material).EntityName}!"), args.Performer, diff --git a/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Rune.cs b/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Rune.cs index b85682605d..9612c92b19 100644 --- a/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Rune.cs +++ b/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Rune.cs @@ -18,6 +18,7 @@ using Content.Shared._Sunrise.BloodCult.Runes; using Content.Shared._Sunrise.BloodCult.UI; using Content.Shared.Atmos.Components; using Content.Shared.Body.Components; +using Content.Shared.Chat; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Coordinates; using Content.Shared.Cuffs.Components; @@ -564,7 +565,7 @@ namespace Content.Server._Sunrise.BloodCult.Runes.Systems HealCultist(target); if (TryComp(target, out var cuffs) && cuffs.Container.ContainedEntities.Count >= 1) - _cuffable.Uncuff(target, cuffs.LastAddedCuffs, cuffs.LastAddedCuffs); + _cuffable.Uncuff(target, cuffs.Container.ContainedEntities[^1], cuffs.Container.ContainedEntities[^1]); return true; } diff --git a/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.cs b/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.cs index 32536732ae..c28a14fdd0 100644 --- a/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.cs +++ b/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.cs @@ -20,6 +20,7 @@ using Content.Shared.Alert; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Containers.ItemSlots; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.Inventory; using Content.Shared.Maps; using Content.Shared.Movement.Pulling.Systems; @@ -54,6 +55,7 @@ namespace Content.Server._Sunrise.BloodCult.Runes.Systems [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly EmpSystem _empSystem = default!; [Dependency] private readonly EntityManager _entityManager = default!; + [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] private readonly EuiManager _euiManager = default!; [Dependency] private readonly FlammableSystem _flammableSystem = default!; [Dependency] private readonly FlashSystem _flashSystem = default!; diff --git a/Content.Server/_Sunrise/BloodCult/Structures/RunicDoorSystem.cs b/Content.Server/_Sunrise/BloodCult/Structures/RunicDoorSystem.cs index 59ade0e9b1..03ae2bd4ca 100644 --- a/Content.Server/_Sunrise/BloodCult/Structures/RunicDoorSystem.cs +++ b/Content.Server/_Sunrise/BloodCult/Structures/RunicDoorSystem.cs @@ -2,6 +2,7 @@ using Content.Shared._Sunrise.BloodCult.Components; using Content.Shared._Sunrise.BloodCult.Structures; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.Doors; using Content.Shared.Stunnable; using Content.Shared.Throwing; diff --git a/Content.Server/_Sunrise/Boss/Systems/DamageOnCollideSystem.cs b/Content.Server/_Sunrise/Boss/Systems/DamageOnCollideSystem.cs index aab7523259..79de4db31e 100644 --- a/Content.Server/_Sunrise/Boss/Systems/DamageOnCollideSystem.cs +++ b/Content.Server/_Sunrise/Boss/Systems/DamageOnCollideSystem.cs @@ -1,6 +1,8 @@ using Content.Shared._Sunrise.Boss.Components; using Content.Shared._Sunrise.Boss.Systems; using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Throwing; using Content.Shared.Whitelist; using Robust.Shared.Physics.Components; @@ -54,7 +56,7 @@ public sealed class DamageOnCollideSystem : SharedDamageOnCollideSystem public void Damage(EntityUid uid, DamageOnCollideComponent component) { - if (_whitelist.IsBlacklistPass(component.Blacklist, uid)) + if (_whitelist.IsWhitelistFail(component.Blacklist, uid)) return; _damageable.TryChangeDamage(uid, component.Damage); } diff --git a/Content.Server/_Sunrise/Boss/Systems/HellSpawnInvincibilitySystem.cs b/Content.Server/_Sunrise/Boss/Systems/HellSpawnInvincibilitySystem.cs index 9e631aa8b5..9aea728c98 100644 --- a/Content.Server/_Sunrise/Boss/Systems/HellSpawnInvincibilitySystem.cs +++ b/Content.Server/_Sunrise/Boss/Systems/HellSpawnInvincibilitySystem.cs @@ -3,6 +3,7 @@ using Content.Shared._Sunrise.Boss.Systems; using Content.Shared.Actions; using Content.Shared.Actions.Components; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Robust.Shared.Timing; namespace Content.Server._Sunrise.Boss.Systems; diff --git a/Content.Server/_Sunrise/Boss/Systems/HellSpawnRushSystem.cs b/Content.Server/_Sunrise/Boss/Systems/HellSpawnRushSystem.cs index 3f8b11ff2e..c1c7e119b2 100644 --- a/Content.Server/_Sunrise/Boss/Systems/HellSpawnRushSystem.cs +++ b/Content.Server/_Sunrise/Boss/Systems/HellSpawnRushSystem.cs @@ -4,6 +4,8 @@ using Content.Shared._Sunrise.Boss.Events; using Content.Shared.Actions; using Content.Shared.Camera; using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Throwing; using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; @@ -61,9 +63,9 @@ public sealed class HellSpawnRushSystem : EntitySystem var query = _lookup.GetEntitiesInRange(Transform(ent.Owner).Coordinates, 1.3f); foreach (var entity in query) { - if (_whitelist.IsBlacklistPass(ent.Comp.Blacklist, entity)) + if (_whitelist.IsWhitelistFail(ent.Comp.Blacklist, entity)) continue; - _damageable.TryChangeDamage(entity, ent.Comp.ThrowHitDamageDict); + _damageable.TryChangeDamage(entity.Owner, ent.Comp.ThrowHitDamageDict); } QueueDel(ent.Comp.RuneUid); diff --git a/Content.Server/_Sunrise/CarpQueen/CarpQueenSystem.cs b/Content.Server/_Sunrise/CarpQueen/CarpQueenSystem.cs index f5a9a129fb..8f673f4adb 100644 --- a/Content.Server/_Sunrise/CarpQueen/CarpQueenSystem.cs +++ b/Content.Server/_Sunrise/CarpQueen/CarpQueenSystem.cs @@ -18,6 +18,8 @@ using Content.Shared.NPC.Systems; using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; +using Content.Shared.Chat; namespace Content.Server._Sunrise.CarpQueen; diff --git a/Content.Server/_Sunrise/CarpQueen/CarpServantBiteSystem.cs b/Content.Server/_Sunrise/CarpQueen/CarpServantBiteSystem.cs index 40a27d8b2d..2da997a22f 100644 --- a/Content.Server/_Sunrise/CarpQueen/CarpServantBiteSystem.cs +++ b/Content.Server/_Sunrise/CarpQueen/CarpServantBiteSystem.cs @@ -49,7 +49,7 @@ public sealed class CarpServantBiteSystem : EntitySystem if (solution.Volume > FixedPoint2.Zero) { - _bloodstream.TryAddToChemicals((target, bloodstream), solution); + _bloodstream.TryAddToBloodstream((target, bloodstream), solution); } } } diff --git a/Content.Server/_Sunrise/CarpQueen/CarpServantRetaliationSystem.cs b/Content.Server/_Sunrise/CarpQueen/CarpServantRetaliationSystem.cs index a56925e1b5..905ff906d4 100644 --- a/Content.Server/_Sunrise/CarpQueen/CarpServantRetaliationSystem.cs +++ b/Content.Server/_Sunrise/CarpQueen/CarpServantRetaliationSystem.cs @@ -1,6 +1,7 @@ using Content.Shared._Sunrise.CarpQueen; using Content.Shared.Damage; using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Mobs.Components; using Content.Shared.NPC.Components; using Content.Shared.NPC.Systems; diff --git a/Content.Server/_Sunrise/CartridgeLoader/Cartridges/PlantAnalyzerCartridgeSystem.cs b/Content.Server/_Sunrise/CartridgeLoader/Cartridges/PlantAnalyzerCartridgeSystem.cs index 4f0b5e140b..48a891e844 100644 --- a/Content.Server/_Sunrise/CartridgeLoader/Cartridges/PlantAnalyzerCartridgeSystem.cs +++ b/Content.Server/_Sunrise/CartridgeLoader/Cartridges/PlantAnalyzerCartridgeSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Botany.Components; +using Content.Shared.Botany.Components; using Content.Shared.CartridgeLoader; using Robust.Server.GameObjects; diff --git a/Content.Server/_Sunrise/Chat/Sanitization/ChatSanitizationEvents.cs b/Content.Server/_Sunrise/Chat/Sanitization/ChatSanitizationEvents.cs index a28cd610d8..14c430919d 100644 --- a/Content.Server/_Sunrise/Chat/Sanitization/ChatSanitizationEvents.cs +++ b/Content.Server/_Sunrise/Chat/Sanitization/ChatSanitizationEvents.cs @@ -1,4 +1,5 @@ using Content.Server.Chat.Systems; +using Content.Shared.Chat; namespace Content.Server._Sunrise.Chat.Sanitization; diff --git a/Content.Server/_Sunrise/Chat/Sanitization/ChatSanitizationSystem.AntiSpam.cs b/Content.Server/_Sunrise/Chat/Sanitization/ChatSanitizationSystem.AntiSpam.cs index 439430e841..843e9342bb 100644 --- a/Content.Server/_Sunrise/Chat/Sanitization/ChatSanitizationSystem.AntiSpam.cs +++ b/Content.Server/_Sunrise/Chat/Sanitization/ChatSanitizationSystem.AntiSpam.cs @@ -1,5 +1,6 @@ using Content.Server.Chat.Systems; using Content.Shared._Sunrise.SunriseCCVars; +using Content.Shared.Chat; using Content.Shared.GameTicking; using Content.Shared.Popups; using Content.Shared.Speech.Muting; diff --git a/Content.Server/_Sunrise/Chat/SayWithIntervalSystem.cs b/Content.Server/_Sunrise/Chat/SayWithIntervalSystem.cs index 3b3629fd2c..e125ab5c82 100644 --- a/Content.Server/_Sunrise/Chat/SayWithIntervalSystem.cs +++ b/Content.Server/_Sunrise/Chat/SayWithIntervalSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Chat.Systems; +using Content.Shared.Chat; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; diff --git a/Content.Server/_Sunrise/DamageOverlay/DamageOverlaySystem.cs b/Content.Server/_Sunrise/DamageOverlay/DamageOverlaySystem.cs index 63c48b1bbf..fd9adb24d8 100644 --- a/Content.Server/_Sunrise/DamageOverlay/DamageOverlaySystem.cs +++ b/Content.Server/_Sunrise/DamageOverlay/DamageOverlaySystem.cs @@ -2,6 +2,7 @@ using Content.Server.Popups; using Content.Shared._Sunrise.DamageOverlay; using Content.Shared._Sunrise.Helpers; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.FixedPoint; using Content.Shared.GameTicking; using Robust.Shared.Map; diff --git a/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs b/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs index e535c95786..d28de0aa33 100644 --- a/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs +++ b/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs @@ -30,7 +30,12 @@ public sealed class DiseaseRoleSystem : SharedDiseaseRoleSystem [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - private static readonly string[] _bloodReagents = { "DiseaseBloodFirst", "DiseaseBloodSecond", "DiseaseBloodThird" }; + private static readonly List _bloodReagents = new() + { + "DiseaseBloodFirst", + "DiseaseBloodSecond", + "DiseaseBloodThird" + }; [ValidatePrototypeId] private const string DiseaseShopId = "ActionDiseaseShop"; @@ -87,7 +92,7 @@ public sealed class DiseaseRoleSystem : SharedDiseaseRoleSystem _sharedCharges.SetCharges((actionId.Value, limitCharges), charges); } } - component.NewBloodReagent = _random.Pick(_bloodReagents); + component.NewBloodReagent.Add(_random.Pick(_bloodReagents)); component.Symptoms.Add("Headache", new SymptomData(1, 4)); } diff --git a/Content.Server/_Sunrise/Disease/SickSystem.cs b/Content.Server/_Sunrise/Disease/SickSystem.cs index 32c2de1110..e9bfce2510 100644 --- a/Content.Server/_Sunrise/Disease/SickSystem.cs +++ b/Content.Server/_Sunrise/Disease/SickSystem.cs @@ -32,6 +32,8 @@ using Content.Shared.Item; using Content.Shared.Medical; using Content.Shared.Speech.Muting; using Content.Shared.Store.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.Chemistry.Components; namespace Content.Server._Sunrise.Disease; public sealed class SickSystem : SharedSickSystem { @@ -103,10 +105,17 @@ public sealed class SickSystem : SharedSickSystem } } - if (!string.IsNullOrEmpty(component.BeforeInfectedBloodReagent) && - TryComp(uid, out var bloodstream)) + if (TryComp(uid, out var stream)) { - _bloodstream.ChangeBloodReagent(uid, component.BeforeInfectedBloodReagent); + var solution = new Solution(); + + foreach (var reagentId in component.BeforeInfectedBloodReagent) + { + // Количество подставь логичное для твоей механики + solution.AddReagent(reagentId, FixedPoint2.New((int)stream.BloodReferenceSolution.MaxVolume)); + } + + _bloodstream.ChangeBloodReagents(uid, solution); } } public override void Update(float frameTime) @@ -124,10 +133,22 @@ public sealed class SickSystem : SharedSickSystem UpdateInfection(uid, component, component.owner, diseaseComp); if (!component.Inited) { - //Infect if (TryComp(uid, out var stream)) - component.BeforeInfectedBloodReagent = stream.BloodReagent; - _bloodstream.ChangeBloodReagent(uid, diseaseComp.NewBloodReagent); + { + foreach (var item in stream.BloodReferenceSolution) + { + component.BeforeInfectedBloodReagent.Add(item.Reagent.Prototype); + } + var solution = new Solution(); + + foreach (var reagentId in diseaseComp.NewBloodReagent) + { + // количество — подставь нужное тебе + solution.AddReagent(reagentId, FixedPoint2.New((int)stream.BloodReferenceSolution.MaxVolume)); + } + + _bloodstream.ChangeBloodReagents(uid, solution); + } RaiseNetworkEvent(new ClientInfectEvent(GetNetEntity(uid), GetNetEntity(component.owner))); diseaseComp.SickOfAllTime++; diff --git a/Content.Server/_Sunrise/EnergyShield/EnergyShieldSystem.cs b/Content.Server/_Sunrise/EnergyShield/EnergyShieldSystem.cs index d671553d17..9ed958b616 100644 --- a/Content.Server/_Sunrise/EnergyShield/EnergyShieldSystem.cs +++ b/Content.Server/_Sunrise/EnergyShield/EnergyShieldSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Popups; using Content.Shared.Power.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; +using Content.Shared.Damage.Systems; namespace Content.Server._Sunrise.EnergyShield; @@ -40,10 +41,10 @@ public sealed class EnergyShieldSystem : EntitySystem return; var cost = totalDamage.Float() * ent.Comp.EnergyCostPerDamage; - _battery.UseCharge(ent, cost, battery); + _battery.UseCharge(ent.Owner, cost); _audio.PlayPvs(ent.Comp.AbsorbSound, ent); - if (battery.CurrentCharge <= 0) + if (battery.ChargeRate <= 0) { _itemToggle.Toggle(ent.Owner); _audio.PlayPvs(ent.Comp.ShutdownSound, ent); @@ -53,7 +54,7 @@ public sealed class EnergyShieldSystem : EntitySystem private void OnToggleAttempt(Entity ent, ref ItemToggleActivateAttemptEvent args) { if (TryComp(ent, out var battery) && - battery.CurrentCharge >= battery.MaxCharge * ent.Comp.MinChargeFractionForActivation) + battery.ChargeRate >= battery.MaxCharge * ent.Comp.MinChargeFractionForActivation) { return; } diff --git a/Content.Server/_Sunrise/Execution/ExecutionSystem.cs b/Content.Server/_Sunrise/Execution/ExecutionSystem.cs index e424cdcce5..cba6bbfedd 100644 --- a/Content.Server/_Sunrise/Execution/ExecutionSystem.cs +++ b/Content.Server/_Sunrise/Execution/ExecutionSystem.cs @@ -23,6 +23,8 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Containers; using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.Damage.Components; namespace Content.Server._Sunrise.Execution; @@ -289,7 +291,7 @@ public sealed class ExecutionSystem : EntitySystem if (!TryComp(weapon, out var melee) && melee!.Damage.GetTotal() > 0.0f) return; - _damageableSystem.TryChangeDamage(victim, melee.Damage * DamageModifier, true, useVariance: false, useModifier: false); + _damageableSystem.ChangeDamage(victim, melee.Damage * DamageModifier, true, useVariance: false, ignoreGlobalModifiers: true); _audioSystem.PlayEntity(melee.HitSound, Filter.Pvs(weapon), weapon, true, AudioParams.Default); if (attacker == victim) @@ -414,7 +416,7 @@ public sealed class ExecutionSystem : EntitySystem } // Gun successfully fired, deal damage - _damageableSystem.TryChangeDamage(victim, damage * DamageModifier, true, useVariance: false, useModifier: false); + _damageableSystem.ChangeDamage(victim, damage * DamageModifier, true, useVariance: false, ignoreGlobalModifiers: true); _audioSystem.PlayEntity(component.SoundGunshot, Filter.Pvs(weapon), weapon, false, AudioParams.Default); // Popups diff --git a/Content.Server/_Sunrise/ExpCollars/ExpCollarsSystem.cs b/Content.Server/_Sunrise/ExpCollars/ExpCollarsSystem.cs index db74f36853..ee3dfd34ba 100644 --- a/Content.Server/_Sunrise/ExpCollars/ExpCollarsSystem.cs +++ b/Content.Server/_Sunrise/ExpCollars/ExpCollarsSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Clothing; using Content.Shared.Clothing.Components; using Content.Shared.Clothing.EntitySystems; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Mobs; diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Abilities.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Abilities.cs index d7c60285ef..615752be60 100644 --- a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Abilities.cs +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Abilities.cs @@ -157,14 +157,17 @@ public sealed partial class FleshCultSystem } else { - if (!component.BloodWhitelist.Contains(bloodstream.BloodReagent)) + foreach (var reagent in bloodstream.BloodReferenceSolution.Contents) { - _popup.PopupEntity( - Loc.GetString("flesh-cultist-devout-target-not-have-flesh"), - uid, uid); - return; + if (!component.BloodWhitelist.Contains(reagent.Reagent.Prototype)) + { + _popup.PopupEntity( + Loc.GetString("flesh-cultist-devout-target-not-have-flesh"), + uid, uid); + return; + } } - if (bloodstream.BloodMaxVolume < 30) + if ((int)bloodstream.BloodReferenceSolution.MaxVolume < 30) { _popup.PopupEntity( Loc.GetString("flesh-cultist-devout-target-invalid"), @@ -172,7 +175,7 @@ public sealed partial class FleshCultSystem return; } } - var saturation = MatchSaturation(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); + var saturation = MatchSaturation((int)bloodstream.BloodReferenceSolution.MaxVolume / 100, hasAppearance); if (TryComp(uid, out var fleshCultistComponent) && fleshCultistComponent.Hunger + saturation >= fleshCultistComponent.MaxHunger) { @@ -290,9 +293,9 @@ public sealed partial class FleshCultSystem hasAppearance = true; } - var saturation = MatchSaturation(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); - var evolutionPoint = MatchEvolutionPoint(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); - var healPoint = MatchHealPoint(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); + var saturation = MatchSaturation((int)bloodstream.BloodReferenceSolution.MaxVolume / 100, hasAppearance); + var evolutionPoint = MatchEvolutionPoint((int)bloodstream.BloodReferenceSolution.MaxVolume / 100, hasAppearance); + var healPoint = MatchHealPoint((int)bloodstream.BloodReferenceSolution.MaxVolume / 100, hasAppearance); RemComp(args.Args.Target.Value); @@ -418,7 +421,7 @@ public sealed partial class FleshCultSystem if (TryComp(uid, out var cuffableComponent) && cuffableComponent.CuffedHandCount > 0) { - _cuffable.Uncuff(uid, uid, cuffableComponent.LastAddedCuffs); + _cuffable.Uncuff(uid, uid, cuffableComponent.Container.ContainedEntities[^1]); } foreach (var hand in _handsSystem.EnumerateHands(uid)) diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Cultist.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Cultist.cs index 5d633d727b..3a3b1a4228 100644 --- a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Cultist.cs +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Cultist.cs @@ -270,7 +270,7 @@ public sealed partial class FleshCultSystem private void OnColdTempImmunityMutation(EntityUid uid, FleshCultistComponent component, FleshCultistColdTempImmunityMutationEvent args) { - if (TryComp(uid, out var tempComponent)) + if (TryComp(uid, out var tempComponent)) tempComponent.ColdDamageThreshold = 0; } diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.cs index 58ee39773c..031af48bb6 100644 --- a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.cs +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.cs @@ -15,6 +15,7 @@ using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Alert; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.DoAfter; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; diff --git a/Content.Server/_Sunrise/Flyswatter/FlyswatterSystem.cs b/Content.Server/_Sunrise/Flyswatter/FlyswatterSystem.cs index 4bf96adaa0..58326ad949 100644 --- a/Content.Server/_Sunrise/Flyswatter/FlyswatterSystem.cs +++ b/Content.Server/_Sunrise/Flyswatter/FlyswatterSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Shared._Sunrise.Flyswatter; using Content.Shared.Body.Components; using Content.Shared.Weapons.Melee; @@ -23,7 +24,12 @@ public sealed class FlyswatterSystem : EntitySystem if (flyswatter.InsectDamageMultiplier <= 1f) return; - if (component.BloodReagent != flyswatter.InsectBloodReagent) + // BloodstreamComponent doesn't have a single BloodReagent field. + // Inspect the bloodstream's reference solution contents for the reagent + // prototype ID configured on the flyswatter. This avoids calling methods + // on the Solution instance which may be restricted by component access. + var contents = component.BloodReferenceSolution?.Contents; + if (contents == null || !contents.Any(q => q.Reagent.Prototype == flyswatter.InsectBloodReagent)) return; // 0 смысла если не бьет diff --git a/Content.Server/_Sunrise/Heartbeat/Systems/HeartbeatSystem.Crit.cs b/Content.Server/_Sunrise/Heartbeat/Systems/HeartbeatSystem.Crit.cs index 66ff9f4107..d5ea28cef6 100644 --- a/Content.Server/_Sunrise/Heartbeat/Systems/HeartbeatSystem.Crit.cs +++ b/Content.Server/_Sunrise/Heartbeat/Systems/HeartbeatSystem.Crit.cs @@ -1,5 +1,7 @@ using Content.Server._Sunrise.Heartbeat.Components; using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Mobs; namespace Content.Server._Sunrise.Heartbeat.Systems; diff --git a/Content.Server/_Sunrise/Heartbeat/Systems/HeartbeatSystem.cs b/Content.Server/_Sunrise/Heartbeat/Systems/HeartbeatSystem.cs index 34cf06e212..23866cd3d4 100644 --- a/Content.Server/_Sunrise/Heartbeat/Systems/HeartbeatSystem.cs +++ b/Content.Server/_Sunrise/Heartbeat/Systems/HeartbeatSystem.cs @@ -1,6 +1,7 @@ using Content.Server._Sunrise.Heartbeat.Components; using Content.Shared._Sunrise.Heartbeat; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.GameTicking; using Content.Shared.Mobs; using Robust.Server.Audio; diff --git a/Content.Server/_Sunrise/Mech/SunriseMechSystem.cs b/Content.Server/_Sunrise/Mech/SunriseMechSystem.cs index 17c3fb7423..702220b3ea 100644 --- a/Content.Server/_Sunrise/Mech/SunriseMechSystem.cs +++ b/Content.Server/_Sunrise/Mech/SunriseMechSystem.cs @@ -17,6 +17,7 @@ using Robust.Shared.Timing; using Content.Shared._Sunrise.Mech; using Content.Shared.Coordinates; using Content.Shared.Emp; +using Content.Shared.Damage.Systems; namespace Content.Server._Sunrise.Mech; @@ -86,7 +87,7 @@ public sealed partial class SunriseMechSystem : EntitySystem ent.Comp.NextPulseTime = curTime + ent.Comp.CooldownTime; - _damageable.TryChangeDamage(ent, ent.Comp.EmpDamage); + _damageable.TryChangeDamage(ent.Owner, ent.Comp.EmpDamage); EnsureComp(ent); } diff --git a/Content.Server/_Sunrise/Mood/MoodSystem.cs b/Content.Server/_Sunrise/Mood/MoodSystem.cs index 6e72d8ff40..aeb93422bc 100644 --- a/Content.Server/_Sunrise/Mood/MoodSystem.cs +++ b/Content.Server/_Sunrise/Mood/MoodSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Alert; using Content.Shared.CCVar; using Content.Shared.Chat; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.FixedPoint; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; diff --git a/Content.Server/_Sunrise/Paws/PawsSystem.cs b/Content.Server/_Sunrise/Paws/PawsSystem.cs index af221642ca..7df985099d 100644 --- a/Content.Server/_Sunrise/Paws/PawsSystem.cs +++ b/Content.Server/_Sunrise/Paws/PawsSystem.cs @@ -1,6 +1,7 @@ using Content.Server._Sunrise.NoEmotions; using Content.Server.Chat.Systems; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; diff --git a/Content.Server/_Sunrise/PlanetPrison/PlanetPrisonStationComponent.cs b/Content.Server/_Sunrise/PlanetPrison/PlanetPrisonStationComponent.cs index ba7ef40907..e61a63faeb 100644 --- a/Content.Server/_Sunrise/PlanetPrison/PlanetPrisonStationComponent.cs +++ b/Content.Server/_Sunrise/PlanetPrison/PlanetPrisonStationComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Maps; +using Content.Shared.Maps; using Content.Shared.Parallax.Biomes; using Content.Shared.Whitelist; using Robust.Shared.Map; diff --git a/Content.Server/_Sunrise/PlanetPrison/PlanetPrisonStationSystem.cs b/Content.Server/_Sunrise/PlanetPrison/PlanetPrisonStationSystem.cs index 47ad53857f..5bcec81af2 100644 --- a/Content.Server/_Sunrise/PlanetPrison/PlanetPrisonStationSystem.cs +++ b/Content.Server/_Sunrise/PlanetPrison/PlanetPrisonStationSystem.cs @@ -7,6 +7,7 @@ using Content.Server.Shuttles.Systems; using Content.Shared._Sunrise.Shuttles; using Content.Shared._Sunrise.SunriseCCVars; using Content.Shared.Light.Components; +using Content.Shared.Maps; using Content.Shared.Salvage; using Content.Shared.Shuttles.Components; using Robust.Server.GameObjects; diff --git a/Content.Server/_Sunrise/Smile/SmileSlimeSystem.cs b/Content.Server/_Sunrise/Smile/SmileSlimeSystem.cs index 037de44718..650cbbd9c1 100644 --- a/Content.Server/_Sunrise/Smile/SmileSlimeSystem.cs +++ b/Content.Server/_Sunrise/Smile/SmileSlimeSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Body.Components; using Content.Shared.DoAfter; using Content.Shared.Movement.Systems; using Content.Shared.Zombies; +using Content.Shared.Damage.Systems; namespace Content.Server._Sunrise.Smile; @@ -105,7 +106,7 @@ public sealed class SmileSlimeSystem : EntitySystem _entMan.SpawnEntity("EffectHearts", targetXform.Coordinates); _audio.PlayPvs(comp.SoundSpecifier, targetXform.Coordinates); - _damageableSystem.TryChangeDamage(args.Target, comp.DamageSpecifier, true, false); + _damageableSystem.TryChangeDamage(args.Target.Value, comp.DamageSpecifier, true, false); args.Handled = true; } diff --git a/Content.Server/_Sunrise/SponsorLoadout/SponsorLoadoutPrototype.cs b/Content.Server/_Sunrise/SponsorLoadout/SponsorLoadoutPrototype.cs index ddd50663a3..edd4187d3e 100644 --- a/Content.Server/_Sunrise/SponsorLoadout/SponsorLoadoutPrototype.cs +++ b/Content.Server/_Sunrise/SponsorLoadout/SponsorLoadoutPrototype.cs @@ -4,20 +4,20 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Server._Sunrise.SponsorLoadout; -[Prototype("sponsorLoadout")] -public sealed class SponsorLoadoutPrototype : IPrototype +[Prototype] +public sealed partial class SponsorLoadoutPrototype : IPrototype { - [IdDataField] public string ID { get; } = default!; + [IdDataField] public string ID { get; private set; } = default!; [DataField(required: true)] public ProtoId Equipment; [DataField("whitelistJobs", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List? WhitelistJobs { get; } + public List? WhitelistJobs { get; private set; } [DataField("blacklistJobs", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List? BlacklistJobs { get; } + public List? BlacklistJobs { get; private set; } [DataField("speciesRestriction")] - public List? SpeciesRestrictions { get; } + public List? SpeciesRestrictions { get; private set; } } diff --git a/Content.Server/_Sunrise/StationCentcomm/StationCentcommComponent.cs b/Content.Server/_Sunrise/StationCentcomm/StationCentcommComponent.cs index 020a22e6a9..4cf397f8ef 100644 --- a/Content.Server/_Sunrise/StationCentcomm/StationCentcommComponent.cs +++ b/Content.Server/_Sunrise/StationCentcomm/StationCentcommComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Maps; +using Content.Shared.Maps; using Content.Shared.Whitelist; using Robust.Shared.Map; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; diff --git a/Content.Server/_Sunrise/StationCentcomm/StationCentcommSystem.cs b/Content.Server/_Sunrise/StationCentcomm/StationCentcommSystem.cs index 4737cb3aa7..5ab927104f 100644 --- a/Content.Server/_Sunrise/StationCentcomm/StationCentcommSystem.cs +++ b/Content.Server/_Sunrise/StationCentcomm/StationCentcommSystem.cs @@ -3,6 +3,7 @@ using Content.Server.GameTicking; using Content.Server.Maps; using Content.Server.Shuttles.Systems; using Content.Shared._Sunrise.AlwaysPoweredMap; +using Content.Shared.Maps; using Robust.Server.GameObjects; using Robust.Shared.Map; using Robust.Shared.Prototypes; diff --git a/Content.Server/_Sunrise/StationGoal/StationGoalPrototype.cs b/Content.Server/_Sunrise/StationGoal/StationGoalPrototype.cs index 9f63b9e710..8a856b366d 100644 --- a/Content.Server/_Sunrise/StationGoal/StationGoalPrototype.cs +++ b/Content.Server/_Sunrise/StationGoal/StationGoalPrototype.cs @@ -4,11 +4,11 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Server._Sunrise.StationGoal { - [Serializable, Prototype("stationGoal")] - public sealed class StationGoalPrototype : IPrototype + [Prototype] + public sealed partial class StationGoalPrototype : IPrototype { [IdDataField] - public string ID { get; } = default!; + public string ID { get; private set; } = default!; [DataField("text")] public string Text { get; set; } = string.Empty; diff --git a/Content.Server/_Sunrise/StatsBoard/StatsBoardSystem.cs b/Content.Server/_Sunrise/StatsBoard/StatsBoardSystem.cs index 9e7aa5ba1f..0f10cce22c 100644 --- a/Content.Server/_Sunrise/StatsBoard/StatsBoardSystem.cs +++ b/Content.Server/_Sunrise/StatsBoard/StatsBoardSystem.cs @@ -29,6 +29,7 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.Localization; +using Content.Shared.Damage.Systems; namespace Content.Server.StatsBoard; diff --git a/Content.Server/_Sunrise/SyndicateTeleporter/SyndicateTeleporterSystem.cs b/Content.Server/_Sunrise/SyndicateTeleporter/SyndicateTeleporterSystem.cs index e1d34b4463..459373bd6f 100644 --- a/Content.Server/_Sunrise/SyndicateTeleporter/SyndicateTeleporterSystem.cs +++ b/Content.Server/_Sunrise/SyndicateTeleporter/SyndicateTeleporterSystem.cs @@ -5,6 +5,8 @@ using Content.Shared._Sunrise.SyndicateTeleporter; using Content.Shared.Charges.Components; using Content.Shared.Charges.Systems; using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Interaction.Events; using Content.Shared.Maps; using Content.Shared.Physics; diff --git a/Content.Server/_Sunrise/TTS/TTSSystem.Sanitize.cs b/Content.Server/_Sunrise/TTS/TTSSystem.Sanitize.cs index 335cfb5929..b1866d5470 100644 --- a/Content.Server/_Sunrise/TTS/TTSSystem.Sanitize.cs +++ b/Content.Server/_Sunrise/TTS/TTSSystem.Sanitize.cs @@ -1,6 +1,7 @@ using System.Text; using System.Text.RegularExpressions; using Content.Server.Chat.Systems; +using Content.Shared.Chat; namespace Content.Server._Sunrise.TTS; diff --git a/Content.Server/_Sunrise/TTS/TTSSystem.cs b/Content.Server/_Sunrise/TTS/TTSSystem.cs index 4768ca0db2..895ba28bcd 100644 --- a/Content.Server/_Sunrise/TTS/TTSSystem.cs +++ b/Content.Server/_Sunrise/TTS/TTSSystem.cs @@ -18,6 +18,7 @@ using Robust.Shared.Configuration; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Content.Shared.Chat; namespace Content.Server._Sunrise.TTS; diff --git a/Content.Server/_Sunrise/Weapons/Melee/Systems/PowerDrainOnMeleeHitSystem.cs b/Content.Server/_Sunrise/Weapons/Melee/Systems/PowerDrainOnMeleeHitSystem.cs index 966277f0a3..604a5d6986 100644 --- a/Content.Server/_Sunrise/Weapons/Melee/Systems/PowerDrainOnMeleeHitSystem.cs +++ b/Content.Server/_Sunrise/Weapons/Melee/Systems/PowerDrainOnMeleeHitSystem.cs @@ -1,11 +1,11 @@ using Content.Server.Power.EntitySystems; -using Content.Server.PowerCell; using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Power.Components; using Content.Shared.PowerCell.Components; using Content.Shared.Weapons.Melee.Events; using Content.Shared._Sunrise.Weapons.Melee.Components; +using Content.Shared.PowerCell; namespace Content.Server._Sunrise.Weapons.Melee.Systems; @@ -38,13 +38,13 @@ public sealed class PowerDrainOnMeleeHitSystem : EntitySystem // Prefer slotted power cell if present if (HasComp(uid)) { - if (!_powerCell.HasCharge(uid, comp.ChargePerHit, null, args.User)) + if (!_powerCell.HasCharge(uid, comp.ChargePerHit, args.User)) { args.Handled = true; return; } - if (!_powerCell.TryUseCharge(uid, comp.ChargePerHit, null, args.User)) + if (!_powerCell.TryUseCharge(uid, comp.ChargePerHit, args.User)) { args.Handled = true; return; @@ -55,13 +55,13 @@ public sealed class PowerDrainOnMeleeHitSystem : EntitySystem // Fall back to direct BatteryComponent on the same entity if (TryComp(uid, out var directBattery)) { - if (directBattery.CurrentCharge < comp.ChargePerHit) + if (directBattery.ChargeRate < comp.ChargePerHit) { args.Handled = true; return; } - if (!_battery.TryUseCharge(uid, comp.ChargePerHit, directBattery)) + if (!_battery.TryUseCharge(uid, comp.ChargePerHit)) { args.Handled = true; return; diff --git a/Content.Server/AbstractAnalyzer/AbstractAnalyzerComponent.cs b/Content.Shared/AbstractAnalyzer/AbstractAnalyzerComponent.cs similarity index 94% rename from Content.Server/AbstractAnalyzer/AbstractAnalyzerComponent.cs rename to Content.Shared/AbstractAnalyzer/AbstractAnalyzerComponent.cs index 1057ff4e0e..18cd6e7f86 100644 --- a/Content.Server/AbstractAnalyzer/AbstractAnalyzerComponent.cs +++ b/Content.Shared/AbstractAnalyzer/AbstractAnalyzerComponent.cs @@ -1,6 +1,6 @@ using Robust.Shared.Audio; -namespace Content.Server.AbstractAnalyzer; +namespace Content.Shared.AbstractAnalyzer; /// /// After scanning, retrieves the target Uid to use with its related UI. @@ -43,7 +43,7 @@ public abstract partial class AbstractAnalyzerComponent : Component public EntityUid? ScannedEntity; /// - /// The maximum range in tiles at which the analyzer can receive continuous updates + /// The maximum range in tiles at which the analyzer can receive continuous updates, a value of null will be infinite range /// [DataField] public float? MaxScanRange = 2.5f; diff --git a/Content.Server/AbstractAnalyzer/AbstractAnalyzerSystem.cs b/Content.Shared/AbstractAnalyzer/AbstractAnalyzerSystem.cs similarity index 87% rename from Content.Server/AbstractAnalyzer/AbstractAnalyzerSystem.cs rename to Content.Shared/AbstractAnalyzer/AbstractAnalyzerSystem.cs index 75e81be323..581ed9edff 100644 --- a/Content.Server/AbstractAnalyzer/AbstractAnalyzerSystem.cs +++ b/Content.Shared/AbstractAnalyzer/AbstractAnalyzerSystem.cs @@ -1,17 +1,16 @@ using System.Diagnostics.CodeAnalysis; -using Content.Server.PowerCell; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Popups; -using Robust.Server.GameObjects; +using Content.Shared.PowerCell; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Timing; -namespace Content.Server.AbstractAnalyzer; +namespace Content.Shared.AbstractAnalyzer; public abstract class AbstractAnalyzerSystem : EntitySystem where TAnalyzerComponent : AbstractAnalyzerComponent @@ -22,9 +21,10 @@ public abstract class AbstractAnalyzerSystem private void OnAfterInteract(Entity uid, ref AfterInteractEvent args) { - if (args.Target == null || !args.CanReach || !ValidScanTarget(args.Target) || !_cell.HasDrawCharge(uid, user: args.User)) + if (args.Target == null || !args.CanReach || !ValidScanTarget(args.Target) || !_cell.HasDrawCharge(uid.Owner, user: args.User)) return; - _audio.PlayPvs(uid.Comp.ScanningBeginSound, uid); + _audio.PlayPredicted(uid.Comp.ScanningBeginSound, uid, null); - var doAfterCancelled = !_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, uid.Comp.ScanDelay, new TAnalyzerDoAfterEvent(), uid, target: args.Target, used: uid) + var doAfterEvent = _typeFactory.CreateInstance(); + var doAfterCancelled = !_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, uid.Comp.ScanDelay, doAfterEvent, uid, target: args.Target, used: uid) { - NeedHand = args.NeedHand, // Sunrise-Edit + NeedHand = true, BreakOnMove = true, }); @@ -97,11 +94,11 @@ public abstract class AbstractAnalyzerSystem uid, ref TAnalyzerDoAfterEvent args) { - if (args.Handled || args.Cancelled || args.Target == null || !_cell.HasDrawCharge(uid, user: args.User)) + if (args.Handled || args.Cancelled || args.Target == null || !_cell.HasDrawCharge(uid.Owner, user: args.User)) return; if (!uid.Comp.Silent) - _audio.PlayPvs(uid.Comp.ScanningEndSound, uid); + _audio.PlayPredicted(uid.Comp.ScanningEndSound, uid, null); OpenUserInterface(args.User, uid); BeginAnalyzingEntity(uid, args.Target.Value); diff --git a/Content.Shared/Botany/PlantAnalyzer/PlantAnalyzerLocalizationHelper.cs b/Content.Shared/Botany/PlantAnalyzer/PlantAnalyzerLocalizationHelper.cs index bedbf3767e..899ba2eee7 100644 --- a/Content.Shared/Botany/PlantAnalyzer/PlantAnalyzerLocalizationHelper.cs +++ b/Content.Shared/Botany/PlantAnalyzer/PlantAnalyzerLocalizationHelper.cs @@ -6,9 +6,11 @@ using Robust.Shared.Prototypes; namespace Content.Shared.Botany.PlantAnalyzer; -public sealed class PlantAnalyzerLocalizationHelper +public sealed class PlantAnalyzerLocalizationHelper : EntitySystem { - public static string GasesToLocalizedStrings(List gases, IPrototypeManager protMan) + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public string GasesToLocalizedStrings(List gases) { if (gases.Count == 0) return ""; @@ -18,35 +20,35 @@ public sealed class PlantAnalyzerLocalizationHelper gasIds.Add((int)gas); List gasesLoc = []; - foreach (var gas in protMan.EnumeratePrototypes()) + foreach (var gas in _prototypeManager.EnumeratePrototypes()) if (gasIds.Contains(int.Parse(gas.ID))) gasesLoc.Add(Loc.GetString(gas.Name)); return ContentLocalizationManager.FormatList(gasesLoc); } - public static string ChemicalsToLocalizedStrings(List ids, IPrototypeManager protMan) + public string ChemicalsToLocalizedStrings(List ids) { if (ids.Count == 0) return ""; List locStrings = []; foreach (var id in ids) - locStrings.Add(protMan.TryIndex(id, out var prototype) ? prototype.LocalizedName : id); + locStrings.Add(_prototypeManager.TryIndex(id, out var prototype) ? prototype.LocalizedName : id); return ContentLocalizationManager.FormatList(locStrings); } - public static (string Singular, string Plural) ProduceToLocalizedStrings(List ids, IPrototypeManager protMan) + public (string Singular, string Plural, string First) ProduceToLocalizedStrings(List ids) { if (ids.Count == 0) - return ("", ""); + return ("", "", ""); List singularStrings = []; List pluralStrings = []; foreach (var id in ids) { - var singular = protMan.TryIndex(id, out var prototype) ? prototype.Name : id.Id; + var singular = _prototypeManager.TryIndex(id, out var prototype) ? prototype.Name : id.Id; var plural = Loc.GetString("plant-analyzer-produce-plural", ("thing", singular)); singularStrings.Add(singular); @@ -55,7 +57,8 @@ public sealed class PlantAnalyzerLocalizationHelper return ( ContentLocalizationManager.FormatListToOr(singularStrings), - ContentLocalizationManager.FormatListToOr(pluralStrings) + ContentLocalizationManager.FormatListToOr(pluralStrings), + singularStrings[0] ); } } diff --git a/Content.Shared/Botany/PlantAnalyzer/PlantAnalyzerScannedUserMessage.cs b/Content.Shared/Botany/PlantAnalyzer/PlantAnalyzerScannedUserMessage.cs index e4a7050cff..61c1a3ec05 100644 --- a/Content.Shared/Botany/PlantAnalyzer/PlantAnalyzerScannedUserMessage.cs +++ b/Content.Shared/Botany/PlantAnalyzer/PlantAnalyzerScannedUserMessage.cs @@ -84,27 +84,19 @@ public sealed class PlantAnalyzerProduceData(int yield, float potency, List "tiny", // 5 should still be tiny + > 5 and < 10 => "small", + >= 10 and < 15 => "below-average", // 10 should be below-average + >= 15 and < 20 => "average", + >= 20 and <= 25 => "above-average", // 25 is the highest starting value + >= 20 and < 30 => "large", + >= 30 and < 40 => "huge", + >= 40 and < 50 => "gigantic", + >= 50 and < 60 => "ludicrous", + _ => "immeasurable" + }; return potencyFtl; } diff --git a/Content.Shared/Chat/SharedChatSystem.cs b/Content.Shared/Chat/SharedChatSystem.cs index 624d3d321c..7ef2bc3999 100644 --- a/Content.Shared/Chat/SharedChatSystem.cs +++ b/Content.Shared/Chat/SharedChatSystem.cs @@ -406,7 +406,8 @@ public abstract partial class SharedChatSystem : EntitySystem ICommonSession? player = null, string? nameOverride = null, bool checkRadioPrefix = true, - bool ignoreActionBlocker = false) + bool ignoreActionBlocker = false, + bool isFormatted = false) //Sunrise { } /// @@ -431,7 +432,8 @@ public abstract partial class SharedChatSystem : EntitySystem ICommonSession? player = null, string? nameOverride = null, bool checkRadioPrefix = true, - bool ignoreActionBlocker = false + bool ignoreActionBlocker = false, + bool isFormatted = false // Sunrise ) { } @@ -467,6 +469,8 @@ public abstract partial class SharedChatSystem : EntitySystem string? sender = null, bool playSound = true, SoundSpecifier? announcementSound = null, + bool playTts = true, // Sunrise-edit + string? announceVoice = null, // Sunrise-edit Color? colorOverride = null ) { } @@ -487,6 +491,8 @@ public abstract partial class SharedChatSystem : EntitySystem EntityUid? source = null, string? sender = null, bool playSound = true, + bool playTts = true, // Sunrise-edit + string? announceVoice = null, // Sunrise-edit SoundSpecifier? announcementSound = null, Color? colorOverride = null) { } @@ -504,6 +510,9 @@ public abstract partial class SharedChatSystem : EntitySystem EntityUid source, string message, string? sender = null, + bool playDefault = true, // Sunrise + bool playTts = true, // Sunrise + string? announceVoice = null, // Sunrise bool playDefaultSound = true, SoundSpecifier? announcementSound = null, Color? colorOverride = null) @@ -533,7 +542,8 @@ public enum InGameICChatType : byte { Speak, Emote, - Whisper + Whisper, + CollectiveMind // Sunrise } /// diff --git a/Content.Shared/Chat/SharedSuicideSystem.cs b/Content.Shared/Chat/SharedSuicideSystem.cs index 9e64039a8d..cf0b480e2f 100644 --- a/Content.Shared/Chat/SharedSuicideSystem.cs +++ b/Content.Shared/Chat/SharedSuicideSystem.cs @@ -42,7 +42,7 @@ public sealed class SharedSuicideSystem : EntitySystem appliedDamageSpecifier.DamageDict[key] = Math.Ceiling((double) (value * lethalAmountOfDamage / totalDamage)); } - _damageableSystem.ChangeDamage(target.AsNullable(), appliedDamageSpecifier, true, origin: target, useModifier: false, useVariance: false); // Sunrise-Edit + _damageableSystem.ChangeDamage(target.AsNullable(), appliedDamageSpecifier, true, origin: target, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit } /// @@ -66,6 +66,6 @@ public sealed class SharedSuicideSystem : EntitySystem } var damage = new DamageSpecifier(damagePrototype, lethalAmountOfDamage); - _damageableSystem.ChangeDamage(target.AsNullable(), damage, true, origin: target, useModifier: false, useVariance: false); // Sunrise-Edit + _damageableSystem.ChangeDamage(target.AsNullable(), damage, true, origin: target, ignoreGlobalModifiers: true, useVariance: false); // Sunrise-Edit } } diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs index 54360ff4fc..39ee2d530a 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs @@ -336,7 +336,7 @@ public sealed class SolutionTransferSystem : EntitySystem /// private bool CanTransfer(SolutionTransferData data) { - var transferAttempt = new SolutionTransferAttemptEvent(data.SourceEntity, data.TargetEntity); + var transferAttempt = new SolutionTransferAttemptEvent(data.SourceEntity, data.TargetEntity, data.Source); // Starlight - add Source parameter // Check if the source is cancelling the transfer RaiseLocalEvent(data.SourceEntity, ref transferAttempt); diff --git a/Content.Shared/Damage/Systems/DamageableSystem.API.cs b/Content.Shared/Damage/Systems/DamageableSystem.API.cs index e1c44f55be..db034ea995 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.API.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.API.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Net.Sockets; +using Content.Shared._Starlight.Medical.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; @@ -123,7 +124,10 @@ public sealed partial class DamageableSystem bool ignoreResistances = false, bool interruptsDoAfters = true, EntityUid? origin = null, - bool ignoreGlobalModifiers = false + bool useVariance = true, // Sunrise + bool ignoreGlobalModifiers = false, + float armorPenetration = 0f, // 🌟Starlight🌟 + bool canHeal = true // 🌟Starlight🌟 ) { var damageDone = new DamageSpecifier(); @@ -140,6 +144,20 @@ public sealed partial class DamageableSystem if (before.Cancelled) return damageDone; + // Sunrise-Start + if (useVariance) + { + var varianceMultiplier = 1f; + if (damage.GetTotal() > 0) + { + var min = 1f - NegativeVariance; + var max = 1f + PositiveVariance; + varianceMultiplier = _random.NextFloat(min, max); + } + damage *= varianceMultiplier; + } + // Sunrise-End + // Apply resistances if (!ignoreResistances) { @@ -151,7 +169,7 @@ public sealed partial class DamageableSystem // TODO DAMAGE // byref struct event. - var ev = new DamageModifyEvent(damage, origin); + var ev = new DamageModifyEvent(damage, origin, armorPenetration, canHeal); //starlight, added armor pen and heal flag RaiseLocalEvent(ent, ev); damage = ev.Damage; @@ -159,6 +177,17 @@ public sealed partial class DamageableSystem return damageDone; } + // 🌟Starlight🌟 start + var finalEv = new DamageBeforeApplyEvent + { + Damage = damage, + Origin = origin + }; + RaiseLocalEvent(ent, finalEv); + if (finalEv.Cancelled) + return damage; + // 🌟Starlight🌟 end + if (!ignoreGlobalModifiers) damage = ApplyUniversalAllModifiers(damage); diff --git a/Content.Shared/Damage/Systems/DamageableSystem.Events.cs b/Content.Shared/Damage/Systems/DamageableSystem.Events.cs index 3e985ba204..d879af3701 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.Events.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.Events.cs @@ -1,3 +1,4 @@ +using Content.Shared._Sunrise.SunriseCCVars; using Content.Shared.CCVar; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -22,6 +23,11 @@ public sealed partial class DamageableSystem _appearanceQuery = GetEntityQuery(); _damageableQuery = GetEntityQuery(); + _config.OnValueChanged(SunriseCCVars.DamagePositiveVariance, UpdatePositiveVariance, true); // Sunrise-Edit + _config.OnValueChanged(SunriseCCVars.DamageNegativeVariance, UpdateNegativeVariance, true); // Sunrise-Edit + _config.OnValueChanged(SunriseCCVars.DamageModifier, UpdateDamageModifier, true); // Sunrise-Edit + _config.OnValueChanged(SunriseCCVars.HealModifier, UpdateHealModifier, true); // Sunrise-Edit + // Damage modifier CVars are updated and stored here to be queried in other systems. // Note that certain modifiers requires reloading the guidebook. Subs.CVar( @@ -119,6 +125,28 @@ public sealed partial class DamageableSystem ); } + // Sunrise-Start + private void UpdatePositiveVariance(float value) + { + PositiveVariance = value; + } + + private void UpdateNegativeVariance(float value) + { + NegativeVariance = value; + } + + private void UpdateHealModifier(float value) + { + HealModifier = value; + } + + private void UpdateDamageModifier(float value) + { + DamageModifier = value; + } + // Sunrise-End + /// /// Initialize a damageable component /// @@ -216,7 +244,7 @@ public record struct BeforeDamageChangedEvent(DamageSpecifier Damage, EntityUid? /// /// For example, armor. /// -public sealed class DamageModifyEvent(DamageSpecifier damage, EntityUid? origin = null) +public sealed class DamageModifyEvent(DamageSpecifier damage, EntityUid? origin = null, float armorPenetration = 0f, bool canHeal = false) // 🌟Starlight🌟 : EntityEventArgs, IInventoryRelayEvent { // Whenever locational damage is a thing, this should just check only that bit of armour. @@ -224,6 +252,9 @@ public sealed class DamageModifyEvent(DamageSpecifier damage, EntityUid? origin public readonly DamageSpecifier OriginalDamage = damage; public DamageSpecifier Damage = damage; + public float ArmorPenetration = armorPenetration; // 🌟Starlight🌟 + public bool CanHeal = canHeal; // 🌟Starlight🌟 + public EntityUid? Origin = origin; //Starlight, but this is clearly a upstream bug } public sealed class DamageChangedEvent : EntityEventArgs diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 2b4c5ad360..452eedda38 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -3,10 +3,12 @@ using Content.Shared.Chemistry; using Content.Shared.Damage.Components; using Content.Shared.Explosion.EntitySystems; using Content.Shared.Mobs.Systems; +using Content.Shared.Random; using Robust.Shared.Configuration; using Robust.Shared.GameStates; using Robust.Shared.Network; using Robust.Shared.Prototypes; +using Robust.Shared.Random; using Robust.Shared.Utility; namespace Content.Shared.Damage.Systems; @@ -15,6 +17,7 @@ public sealed partial class DamageableSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly IRobustRandom _random = default!; // Sunrise [Dependency] private readonly INetManager _netMan = default!; [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; [Dependency] private readonly IConfigurationManager _config = default!; @@ -35,6 +38,10 @@ public sealed partial class DamageableSystem : EntitySystem public float UniversalThrownDamageModifier { get; private set; } = 1f; public float UniversalTopicalsHealModifier { get; private set; } = 1f; public float UniversalMobDamageModifier { get; private set; } = 1f; + public float PositiveVariance; // Sunrise-Edit + public float NegativeVariance; // Sunrise-Edit + public float DamageModifier = 1f; // Sunrise-Edit + public float HealModifier = 1f; // Sunrise-Edit /// /// If the damage in a DamageableComponent was changed this function should be called. diff --git a/Content.Shared/Humanoid/Prototypes/SpeciesPrototype.cs b/Content.Shared/Humanoid/Prototypes/SpeciesPrototype.cs index cdc35ae8c3..46146427f8 100644 --- a/Content.Shared/Humanoid/Prototypes/SpeciesPrototype.cs +++ b/Content.Shared/Humanoid/Prototypes/SpeciesPrototype.cs @@ -57,7 +57,7 @@ public sealed partial class SpeciesPrototype : IPrototype public string SpriteSet { get; private set; } = default!; [DataField(required: true)] - public List BodyTypes { get; } = default!; + public List BodyTypes { get; private set; } = default!; /// /// Default skin tone for this species. This applies for non-human skin tones. diff --git a/Content.Shared/Kitchen/BeingMicrowavedEvent.cs b/Content.Shared/Kitchen/BeingMicrowavedEvent.cs index c053eda997..ce207d2929 100644 --- a/Content.Shared/Kitchen/BeingMicrowavedEvent.cs +++ b/Content.Shared/Kitchen/BeingMicrowavedEvent.cs @@ -3,8 +3,11 @@ namespace Content.Shared.Kitchen; /// /// Raised on an entity when it is inside a microwave and it starts cooking. /// -public sealed class BeingMicrowavedEvent(EntityUid microwave, EntityUid? user) : HandledEntityEventArgs +public sealed class BeingMicrowavedEvent(EntityUid microwave, EntityUid? user, bool heating, bool irradiating) : HandledEntityEventArgs { public EntityUid Microwave = microwave; public EntityUid? User = user; + + public bool BeingHeated = heating; //Sunrise-Edit + public bool BeingIrradiated = irradiating; //Sunrise-Edit } diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index 61a295d489..15a2c8be4d 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -357,7 +357,10 @@ public sealed class LockSystem : EntitySystem return true; if (!quiet) - _sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-has-user-access-fail"), user, user); + { + var denyReason = accessEv.DenyReason ?? Loc.GetString(_defaultDenyReason); + _sharedPopupSystem.PopupClient(denyReason, ent, user); + } return false; } @@ -366,12 +369,6 @@ public sealed class LockSystem : EntitySystem if (HasComp(user)) return true; - if (!quiet) - { - var denyReason = accessEv.DenyReason ?? Loc.GetString(_defaultDenyReason); - _sharedPopupSystem.PopupClient(denyReason, ent, user); - } - return false; } diff --git a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs index 9ae3ddff25..6e0abb5d7e 100644 --- a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs @@ -16,6 +16,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared._Sunrise.SunriseCCVars; using Content.Shared.Damage; using Content.Shared.Mobs.Systems; +using Content.Shared.Damage.Systems; namespace Content.Shared.Nutrition.EntitySystems; diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 27824496c6..c344a780d3 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -89,7 +89,7 @@ namespace Content.Shared.Roles // Sunrise-Start [DataField("radioBold")] - public bool RadioIsBold { get; } + public bool RadioIsBold { get; private set; } // Sunrise-End /// @@ -173,7 +173,7 @@ namespace Content.Shared.Roles /// Для космической тюрьмы /// [DataField("alwaysUseSpawner")] - public bool AlwaysUseSpawner { get; } + public bool AlwaysUseSpawner { get; private set; } [DataField] public SpriteSpecifier PreviewIcon = new SpriteSpecifier.Rsi(new ResPath("/Textures/_Sunrise/Interface/Misc/job_preview.rsi"), "test"); diff --git a/Content.Shared/StatusIcon/StatusIconPrototype.cs b/Content.Shared/StatusIcon/StatusIconPrototype.cs index 679f89f3ee..66d062438a 100644 --- a/Content.Shared/StatusIcon/StatusIconPrototype.cs +++ b/Content.Shared/StatusIcon/StatusIconPrototype.cs @@ -225,12 +225,12 @@ public sealed partial class SickIconPrototype : StatusIconPrototype, IInheriting { /// [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] - public string[]? Parents { get; } + public string[]? Parents { get; private set; } /// [NeverPushInheritance] [AbstractDataField] - public bool Abstract { get; } + public bool Abstract { get; private set; } } // Sunrise-End diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanAmmoComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanAmmoComponent.cs new file mode 100644 index 0000000000..3993d5ad20 --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Components/HitscanAmmoComponent.cs @@ -0,0 +1,11 @@ +using Content.Shared.Weapons.Ranged; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// This component is used to indicate an entity is shootable from a hitscan weapon. +/// This is placed on the laser entity being shot, not the gun itself. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanAmmoComponent : Component, IShootable; diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicDamageComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicDamageComponent.cs new file mode 100644 index 0000000000..2b4d8fbd84 --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicDamageComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared.Damage; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// Hitscan entities that have this component will do the damage specified to hit targets (Who didn't reflect it). +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanBasicDamageComponent : Component +{ + /// + /// How much damage the hitscan weapon will do when hitting a target. + /// + [DataField(required: true)] + public DamageSpecifier Damage; + + // Starlight start + /// + /// Should damage done by this hitscan ignore resistances? + /// + [DataField] + public bool IgnoreResistances = false; + + /// + /// How much armor should this projectile ignore? + /// + [DataField] + public float ArmorPenetration = 0f; + // Starlight end +} diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicEffectsComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicEffectsComponent.cs new file mode 100644 index 0000000000..05209f3b9e --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicEffectsComponent.cs @@ -0,0 +1,29 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// System or basic "effects" like sounds and hit markers for hitscans. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanBasicEffectsComponent : Component +{ + /// + /// This will turn hit entities this color briefly. + /// + [DataField] + public Color? HitColor = Color.Red; + + /// + /// Sound that plays upon the thing being hit. + /// + [DataField] + public SoundSpecifier? Sound; + + /// + /// Force the hitscan sound to play rather than playing the entity's override sound (if it exists). + /// + [DataField] + public bool ForceSound; +} diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicRaycastComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicRaycastComponent.cs new file mode 100644 index 0000000000..e04ab0da4f --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicRaycastComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Physics; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// A basic raycast system that will shoot in a straight line when triggered. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanBasicRaycastComponent : Component +{ + /// + /// Maximum distance the raycast will travel before giving up. Reflections will reset the distance traveled + /// + [DataField] + public float MaxDistance = 20.0f; + + /// + /// The collision mask the hitscan ray uses to collide with other objects. See the enum for more information + /// + [DataField] + public CollisionGroup CollisionMask = CollisionGroup.Opaque; +} diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicVisualsComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicVisualsComponent.cs new file mode 100644 index 0000000000..c7d6a88f53 --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicVisualsComponent.cs @@ -0,0 +1,48 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Utility; + +#region Starlight +using Content.Shared.Starlight.Utility; +using Robust.Shared.Serialization; +#endregion Starlight + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// Provides basic visuals for hitscan weapons - works with +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanBasicVisualsComponent : Component +{ + /// + /// The muzzle flash from the hitscan weapon. + /// + [DataField] + public SpriteSpecifier? MuzzleFlash; + + /// + /// The "travel" sprite, this gets repeated until it hits the target. + /// + [DataField] + public SpriteSpecifier? TravelFlash; + + /// + /// The sprite that gets shown on the impact of the laser. + /// + [DataField] + public SpriteSpecifier? ImpactFlash; + + // Starlight start + /// + /// The bullet that gets displayed for the hitscan rapidly for the client + /// + [DataField] + public ExtendedSpriteSpecifier? Bullet; + + /// + /// The speed of the projectile to display if Bullet has a value + /// + [DataField] + public float Speed = 315f; // 9mm bullet speed + // Starlight end +} diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanReflectComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanReflectComponent.cs new file mode 100644 index 0000000000..a63f3ab670 --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Components/HitscanReflectComponent.cs @@ -0,0 +1,29 @@ +using Content.Shared.Weapons.Reflect; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// Hitscan entities with this component will get reflected by certain things (E.G energy swords). +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanReflectComponent : Component +{ + /// + /// The reflective type, will only reflect from entities that have a matching reflection type. + /// + [DataField] + public ReflectType ReflectiveType = ReflectType.Energy; + + /// + /// The maximum number of reflections the laser will make. + /// + [DataField] + public int MaxReflections = 3; + + /// + /// Current number of times this hitscan entity was reflected. Will not be more than + /// + [DataField] + public int CurrentReflections; +} diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanStaminaDamageComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanStaminaDamageComponent.cs new file mode 100644 index 0000000000..35eb398682 --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Components/HitscanStaminaDamageComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// Hitscan entities that have this component will deal stamina damage to the target. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanStaminaDamageComponent : Component +{ + /// + /// How much stamania damage the hitscan weapon will do when hitting a target. + /// + [DataField] + public float StaminaDamage = 10.0f; +} diff --git a/Content.Shared/Weapons/Hitscan/Events/HitscanEvents.cs b/Content.Shared/Weapons/Hitscan/Events/HitscanEvents.cs new file mode 100644 index 0000000000..28465a7cbd --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Events/HitscanEvents.cs @@ -0,0 +1,136 @@ +using System.Numerics; +using Content.Shared.Damage; +using Robust.Shared.Map; + +namespace Content.Shared.Weapons.Hitscan.Events; + +/// +/// Raised on the hitscan entity when "fired". This could be from reflections or from the gun. This is the catalyst that +/// other systems will listen for to actually shoot the gun. +/// +[ByRefEvent] +public record struct HitscanTraceEvent +{ + /// + /// Location the hitscan was fired from. + /// + public EntityCoordinates FromCoordinates; + + /// + /// Direction that the ray was fired towards. + /// + public Vector2 ShotDirection; + + /// + /// Gun that was fired - this will always be the original weapon even if reflected. + /// + public EntityUid Gun; + + /// + /// Player who shot the gun, if null the gun was fired by itself. + /// + public EntityUid? Shooter; + + /// + /// Target that was being aimed at (Not necessarily hit). + /// + public HashSet? Target; + + // Starlight start + /// + /// The effect list being used + /// + public List? OutputTrace; + // Starlight end +} + +/// +/// All data known data for when a hitscan is actually fired. +/// +public record struct HitscanRaycastFiredData +{ + /// + /// Direction that the ray was fired towards. + /// + public Vector2 ShotDirection; + + /// + /// The entity that got hit, if null the raycast didn't hit anyone. + /// + public EntityUid? HitEntity; + + /// + /// Gun that fired the raycast. + /// + public EntityUid Gun; + + /// + /// Player who shot the gun, if null the gun was fired by itself. + /// + public EntityUid? Shooter; + + // Starlight start + /// + /// The location of the impact, if null the raycast didn't hit anyone. + /// + public Vector2? HitPosition; + + /// + /// A list of all hitscans in the trace, used for final rendering. + /// + public List OutputTrace; + // Starlight end +} + +/// +/// Try to hit the targeted entity with a hitscan laser. Stuff like the reflection system should listen for this and +/// cancel the event if the laser was reflected. +/// +[ByRefEvent] +public struct AttemptHitscanRaycastFiredEvent +{ + /// + /// Data for the hitscan that was fired. + /// + public HitscanRaycastFiredData Data; + + /// + /// Set to true the hitscan is cancelled (e.g. due to reflection). + /// Cancelled hitscans should not apply damage or trigger follow-up effects. + /// + public bool Cancelled; +} + +/// +/// Results of a hitscan raycast and will be raised on the raycast entity on itself. Stuff like the damage system should +/// listen for this. At this point we KNOW the laser hit the entity. +/// +[ByRefEvent] +public struct HitscanRaycastFiredEvent +{ + /// + /// Data for the hitscan that was fired. + /// + public HitscanRaycastFiredData Data; +} + +[ByRefEvent] +public record struct HitscanDamageDealtEvent +{ + /// + /// Target that was dealt damage. + /// + public EntityUid Target; + + /// + /// The amount of damage that the target was dealt. + /// + public DamageSpecifier DamageDealt; + + // Starlight begin + /// + /// Data for the hitscan that was fired. + /// + public HitscanRaycastFiredData Data; + // Starlight end +} diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicDamageSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicDamageSystem.cs new file mode 100644 index 0000000000..e46f424299 --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicDamageSystem.cs @@ -0,0 +1,49 @@ +using Content.Shared.Damage.Systems; +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; + +namespace Content.Shared.Weapons.Hitscan.Systems; + +public sealed class HitscanBasicDamageSystem : EntitySystem +{ + [Dependency] private readonly DamageableSystem _damage = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHitscanHit); + } + + private void OnHitscanHit(Entity ent, ref HitscanRaycastFiredEvent args) + { + if (args.Data.HitEntity == null) + return; + + var dmg = ent.Comp.Damage * _damage.UniversalHitscanDamageModifier; + + // var damageDealt = _damage.TryChangeDamage(args.Data.HitEntity.Value, dmg, origin: args.Data.Gun); // Starlight - we redefine this + // Starlight start + var damageDealt = _damage.ChangeDamage( + args.Data.HitEntity.Value, + dmg, + ignoreResistances: ent.Comp.IgnoreResistances, + origin: args.Data.Gun, + armorPenetration: ent.Comp.ArmorPenetration, + canHeal: false + ); + // Starlight end + + if (damageDealt == null) + return; + + var damageEvent = new HitscanDamageDealtEvent + { + Target = args.Data.HitEntity.Value, + DamageDealt = damageDealt, + Data = args.Data, // Starlight + }; + + RaiseLocalEvent(ent, ref damageEvent); + } +} diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicEffectsSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicEffectsSystem.cs new file mode 100644 index 0000000000..4713734be7 --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicEffectsSystem.cs @@ -0,0 +1,36 @@ +using Content.Shared.Damage; +using Content.Shared.Effects; +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Player; + +namespace Content.Shared.Weapons.Hitscan.Systems; + +public sealed class HitscanBasicEffectsSystem : EntitySystem +{ + [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; + [Dependency] private readonly SharedGunSystem _gun = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHitscanDamageDealt); + } + + private void OnHitscanDamageDealt(Entity ent, ref HitscanDamageDealtEvent args) + { + if (Deleted(args.Target)) + return; + + if (ent.Comp.HitColor != null && args.DamageDealt.GetTotal() != 0) + { + _color.RaiseEffect(ent.Comp.HitColor.Value, + new List { args.Target }, + Filter.Pvs(args.Target, entityManager: EntityManager)); + } + + _gun.PlayImpactSound(args.Target, args.DamageDealt, ent.Comp.Sound, ent.Comp.ForceSound); + } +} diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs new file mode 100644 index 0000000000..c29e3009af --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs @@ -0,0 +1,261 @@ +using System.Numerics; +using Content.Shared.Administration.Logs; +using Content.Shared.Damage.Components; +using Content.Shared.Database; +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; +using Robust.Shared.Utility; + +#region Starlight +using System.Linq; +using Content.Shared.Body.Components; +using Content.Shared.Mech.Components; +using Content.Shared.Weapons.Reflect; +using Robust.Shared.Maths; +using Robust.Shared.Prototypes; +#endregion Starlight + +namespace Content.Shared.Weapons.Hitscan.Systems; + +public sealed class HitscanBasicRaycastSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly ISharedAdminLogManager _log = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + +#region Starlight + [Dependency] private readonly IPrototypeManager _proto = default!; +#endregion Starlight + + private EntityQuery _visualsQuery; + + private EntityQuery _reflectQuery; // Starlight + private EntityQuery _bloodQuery; // Starlight + + public override void Initialize() + { + base.Initialize(); + + _visualsQuery = GetEntityQuery(); + + _reflectQuery = GetEntityQuery(); // Starlight + + SubscribeLocalEvent(OnHitscanFired); + } + + private void OnHitscanFired(Entity ent, ref HitscanTraceEvent args) + { + var shooter = args.Shooter ?? args.Gun; + // Starlight start - handle the shooter being the mech, not the pilot + if (shooter != null && TryComp(shooter, out var pilotA)) + shooter = pilotA.Mech; + // Starlight end + var mapCords = _transform.ToMapCoordinates(args.FromCoordinates); + var ray = new CollisionRay(mapCords.Position, args.ShotDirection, (int) ent.Comp.CollisionMask); + var rayCastResults = _physics.IntersectRay(mapCords.MapId, ray, ent.Comp.MaxDistance, shooter, false); + + var targets = args.Target; + // If you are in a container, use the raycast result + // Otherwise: + // 1.) Hit the first entity that is in the targeted set. + // 2.) Hit the first entity that doesn't require you to aim at it specifically to be hit. + var result = _container.IsEntityOrParentInContainer(shooter) + ? rayCastResults.FirstOrNull() + : rayCastResults.FirstOrNull(hit => + // If we have explicit targets, prefer any hit that is one of them. + (targets != null && hit.HitEntity is { } h && targets.Contains(h)) + // Otherwise allow hitting entities that don't require being specifically targeted. + || CompOrNull(hit.HitEntity)?.Active != true); + + var distanceTried = result?.Distance ?? ent.Comp.MaxDistance; + + // Starlight start + var isRoot = false; + if (args.OutputTrace is null) + { + args.OutputTrace = new List(); + isRoot = true; + } + // Starlight end + + // Do visuals without an event. They should always happen and putting it on the attempt event is weird! + // If more stuff gets added here, it should probably be turned into an event. + // FireEffects(args.FromCoordinates, distanceTried, args.ShotDirection.ToAngle(), ent.Owner); // Starlight - comment out, as we want to aggregate these + + args.OutputTrace.Add(GenerateTraceStep(args.FromCoordinates, distanceTried, args.ShotDirection.ToAngle())); // Starlight - add the visuals for this particular leg of the hitscan into the trace + + // Admin logging + if (result?.HitEntity != null) + { + _log.Add(LogType.HitScanHit, + $"{ToPrettyString(shooter):user} hit {ToPrettyString(result.Value.HitEntity):target}" + + $" using {ToPrettyString(args.Gun):entity}."); + } + + var data = new HitscanRaycastFiredData + { + ShotDirection = args.ShotDirection, + Gun = args.Gun, + Shooter = args.Shooter, + HitEntity = result?.HitEntity, + OutputTrace = args.OutputTrace, // Starlight + }; + + var attemptEvent = new AttemptHitscanRaycastFiredEvent { Data = data }; + RaiseLocalEvent(ent, ref attemptEvent); + + if (attemptEvent.Cancelled) + { // Starlight start - added block with additional command before return + if (isRoot) + FireEffects(ent, args.OutputTrace); + // Starlight end + return; + } // Starlight + + var hitEvent = new HitscanRaycastFiredEvent { Data = data }; + RaiseLocalEvent(ent, ref hitEvent); + + // Starlight start + if (isRoot) + FireEffects(ent, args.OutputTrace); + // Starlight end + } + + private HitscanTrace GenerateTraceStep(EntityCoordinates fromCoordinates, float distance, Angle shotAngle) + { + var fromXform = Transform(fromCoordinates.EntityId); + + var gridUid = fromXform.GridUid; + if (gridUid != fromCoordinates.EntityId && TryComp(gridUid, out TransformComponent? gridXform)) + { + var (_, gridRot, gridInvMatrix) = _transform.GetWorldPositionRotationInvMatrix(gridXform); + var map = _transform.ToMapCoordinates(fromCoordinates); + fromCoordinates = new EntityCoordinates(gridUid.Value, Vector2.Transform(map.Position, gridInvMatrix)); + shotAngle -= gridRot; + } + else + { + shotAngle -= _transform.GetWorldRotation(fromXform); + } + + var shotVec = shotAngle.ToVec().Normalized(); + + return new() { + Angle = shotAngle, + Distance = distance, + // We don't draw muzzle or travel effects if we're at point-blank range, just impact effects + MuzzleCoordinates = distance > 1f ? GetNetCoordinates(fromCoordinates.Offset(shotVec / 2)) : null, + TravelCoordinates = distance > 1f ? GetNetCoordinates(fromCoordinates.Offset(shotVec * (distance + 0.5f) / 2)) : null, + ImpactCoordinates = GetNetCoordinates(fromCoordinates.Offset(shotVec * distance)), + }; + } + + private void FireEffects(EntityUid hitscan, List traces) + { + if (!_visualsQuery.TryComp(hitscan, out var visuals)) + { + // We're not going to display anything, so don't fire the event to display things + return; + } + + // Trigger the render + var hitscanEvent = new SharedGunSystem.HitscanEvent + { + MuzzleFlash = visuals.MuzzleFlash, + TravelFlash = visuals.TravelFlash, + ImpactFlash = visuals.ImpactFlash, + Bullet = visuals.Bullet, + Speed = visuals.Speed, + Traces = traces, + }; + + // Figure out who might see the event on any of the bounces + var filter = Filter.Empty(); + var sampledPositions = traces + .Select(x => GetCoordinates(x.MuzzleCoordinates)) + .Where(x => x is not null) + .Select(x => x!.Value) + .Where(x => x.IsValid(EntityManager)) + .ToList(); + foreach (var pos in sampledPositions) + filter.Merge(Filter.Pvs(pos, entityMan: EntityManager)); + + RaiseNetworkEvent(hitscanEvent, filter); + } + + /* Starlight - comment out upstream FireEffects + /// + /// Create visual effects for the fired hitscan weapon. + /// + /// Location to start the effect. + /// Distance of the hitscan shot. + /// Angle of the shot. + /// The hitscan entity itself. + private void FireEffects(EntityCoordinates fromCoordinates, float distance, Angle shotAngle, EntityUid hitscanUid) + { + if (distance == 0 || !_visualsQuery.TryComp(hitscanUid, out var vizComp)) + return; + + var sprites = new List<(NetCoordinates coordinates, Angle angle, SpriteSpecifier sprite, float scale)>(); + var fromXform = Transform(fromCoordinates.EntityId); + + // We'll get the effects relative to the grid / map of the firer + // Look you could probably optimise this a bit with redundant transforms at this point. + + var gridUid = fromXform.GridUid; + if (gridUid != fromCoordinates.EntityId && TryComp(gridUid, out TransformComponent? gridXform)) + { + var (_, gridRot, gridInvMatrix) = _transform.GetWorldPositionRotationInvMatrix(gridXform); + var map = _transform.ToMapCoordinates(fromCoordinates); + fromCoordinates = new EntityCoordinates(gridUid.Value, Vector2.Transform(map.Position, gridInvMatrix)); + shotAngle -= gridRot; + } + else + { + shotAngle -= _transform.GetWorldRotation(fromXform); + } + + if (distance >= 1f) + { + if (vizComp.MuzzleFlash != null) + { + var coords = fromCoordinates.Offset(shotAngle.ToVec().Normalized() / 2); + var netCoords = GetNetCoordinates(coords); + + sprites.Add((netCoords, shotAngle, vizComp.MuzzleFlash, 1f)); + } + + if (vizComp.TravelFlash != null) + { + var coords = fromCoordinates.Offset(shotAngle.ToVec() * (distance + 0.5f) / 2); + var netCoords = GetNetCoordinates(coords); + + sprites.Add((netCoords, shotAngle, vizComp.TravelFlash, distance - 1.5f)); + } + } + + if (vizComp.ImpactFlash != null) + { + var coords = fromCoordinates.Offset(shotAngle.ToVec() * distance); + var netCoords = GetNetCoordinates(coords); + + sprites.Add((netCoords, shotAngle.FlipPositive(), vizComp.ImpactFlash, 1f)); + } + + if (sprites.Count > 0) + { + RaiseNetworkEvent(new SharedGunSystem.HitscanEvent + { + Sprites = sprites, + }, Filter.Pvs(fromCoordinates, entityMan: EntityManager)); + } + } + */// Starlight - end of commenting out upstream FireEffects +} diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanReflectSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanReflectSystem.cs new file mode 100644 index 0000000000..ab2dfd0aa5 --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Systems/HitscanReflectSystem.cs @@ -0,0 +1,51 @@ +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; +using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.Weapons.Reflect; +using Robust.Shared.Random; + +namespace Content.Shared.Weapons.Hitscan.Systems; + +public sealed class HitscanReflectSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHitscanHit); + } + + private void OnHitscanHit(Entity hitscan, ref AttemptHitscanRaycastFiredEvent args) + { + var data = args.Data; + + if (hitscan.Comp.ReflectiveType == ReflectType.None || data.HitEntity == null) + return; + + if (hitscan.Comp.CurrentReflections >= hitscan.Comp.MaxReflections) + return; + + var ev = new HitScanReflectAttemptEvent(data.Shooter ?? data.Gun, data.Gun, hitscan.Comp.ReflectiveType, data.ShotDirection, false); + RaiseLocalEvent(data.HitEntity.Value, ref ev); + + if (!ev.Reflected) + return; + + hitscan.Comp.CurrentReflections++; + + args.Cancelled = true; + + var fromEffect = Transform(data.HitEntity.Value).Coordinates; + + var hitFiredEvent = new HitscanTraceEvent + { + FromCoordinates = fromEffect, + ShotDirection = ev.Direction, + Gun = data.Gun, + Shooter = data.HitEntity.Value, + OutputTrace = data.OutputTrace, // Starlight + }; + + RaiseLocalEvent(hitscan, ref hitFiredEvent); + } +} diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanStunSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanStunSystem.cs new file mode 100644 index 0000000000..8c1d1b45fb --- /dev/null +++ b/Content.Shared/Weapons/Hitscan/Systems/HitscanStunSystem.cs @@ -0,0 +1,25 @@ +using Content.Shared.Damage.Systems; +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; + +namespace Content.Shared.Weapons.Hitscan.Systems; + +public sealed class HitscanStunSystem : EntitySystem +{ + [Dependency] private readonly SharedStaminaSystem _stamina = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHitscanHit); + } + + private void OnHitscanHit(Entity hitscan, ref HitscanRaycastFiredEvent args) + { + if (args.Data.HitEntity == null) + return; + + _stamina.TakeStaminaDamage(args.Data.HitEntity.Value, hitscan.Comp.StaminaDamage, source: args.Data.Shooter ?? args.Data.Gun); + } +} diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 19661f202b..fe541486e2 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -502,6 +502,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem // If I do not come back later to fix Light Attacks being Heavy Attacks you can throw me in the spider pit -Errant var damage = GetDamage(meleeUid, user, component) * GetHeavyDamageModifier(meleeUid, user, component); var target = GetEntity(ev.Target); + var resistanceBypass = GetResistanceBypass(meleeUid, user, component); // For consistency with wide attacks stuff needs damageable. if (Deleted(target) || @@ -560,8 +561,6 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem RaiseLocalEvent(target.Value, attackedEvent); var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage + attackedEvent.BonusDamage, hitEvent.ModifiersList); - var resistanceBypass = GetResistanceBypass(meleeUid, user, component); // Sunrise-edit ЛКМ пробивает броню - var damageResult = Damageable.TryChangeDamage(target, modifiedDamage, origin:user, ignoreResistances:resistanceBypass); if (Damageable.TryChangeDamage(target.Value, modifiedDamage, out var damageResult, origin:user, ignoreResistances:resistanceBypass)) { @@ -594,6 +593,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem } } + protected abstract void DoDamageEffect(List targets, EntityUid? user, TransformComponent targetXform); private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session) diff --git a/Content.Shared/Weapons/Ranged/Components/HitscanBatteryAmmoProviderComponent.cs b/Content.Shared/Weapons/Ranged/Components/HitscanBatteryAmmoProviderComponent.cs deleted file mode 100644 index be9d9805a8..0000000000 --- a/Content.Shared/Weapons/Ranged/Components/HitscanBatteryAmmoProviderComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Weapons.Ranged.Components; - -[RegisterComponent, NetworkedComponent] -public sealed partial class HitscanBatteryAmmoProviderComponent : BatteryAmmoProviderComponent -{ - [ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Prototype = default!; -} diff --git a/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs b/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs index f85c648308..146f1fbc46 100644 --- a/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.GameStates; diff --git a/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs b/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs index d3f0e5f468..05bf0ac83b 100644 --- a/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs @@ -13,6 +13,7 @@ using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Robust.Shared.Network; // Starlight-edit namespace Content.Shared.Weapons.Ranged.Systems; @@ -22,8 +23,9 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; - [Dependency] private readonly SharedGunSystem _gun = default!; [Dependency] private readonly SharedItemSystem _item = default!; + [Dependency] private readonly INetManager _net = default!; // Starlight-edit + [Dependency] private readonly SharedGunSystem _gun = default!; public override void Initialize() { @@ -32,8 +34,8 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem SubscribeLocalEvent(OnUseInHandEvent); SubscribeLocalEvent>(OnGetVerb); SubscribeLocalEvent(OnExamined); - SubscribeLocalEvent(OnInteractHandEvent); - SubscribeLocalEvent(OnShootAttempt); + SubscribeLocalEvent(OnInteractHandEvent); // Starlight-edit + SubscribeLocalEvent(OnShootAttempt); // Starlight-edit } private void OnExamined(EntityUid uid, BatteryWeaponFireModesComponent component, ExaminedEvent args) @@ -43,25 +45,21 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem var fireMode = GetMode(component); + // Starlight-start if (TryGetAmmoProvider(uid, out var ammoProvider) && ammoProvider != null) { - if (ammoProvider is ProjectileBatteryAmmoProviderComponent projectileAmmo) + if (ammoProvider is BatteryAmmoProviderComponent projectileAmmo) { if (!_prototypeManager.TryIndex(fireMode.Prototype, out var projectile)) return; args.PushMarkup(Loc.GetString("gun-set-fire-mode", ("mode", projectile.Name))); } - else if (ammoProvider is HitscanBatteryAmmoProviderComponent hitscanAmmo) - { - if (!_prototypeManager.TryIndex(fireMode.Prototype, out var hitscan)) - return; - - args.PushMarkup(Loc.GetString("gun-set-fire-mode", ("mode", hitscan.Name))); - } } + // Starlight-end } + private BatteryWeaponFireMode GetMode(BatteryWeaponFireModesComponent component) => component.FireModes[component.CurrentFireMode]; // Starlight-edit: Lambda private void OnGetVerb(EntityUid uid, BatteryWeaponFireModesComponent component, GetVerbsEvent args) { @@ -77,7 +75,7 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem if (!_accessReaderSystem.IsAllowed(args.User, uid)) return; - if (!TryGetAmmoProvider(uid, out var ammoProvider) && ammoProvider == null) + if (!TryGetAmmoProvider(uid, out var ammoProvider) && ammoProvider == null) // Starlight-edit return; for (var i = 0; i < component.FireModes.Count; i++) @@ -85,7 +83,8 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem var fireMode = component.FireModes[i]; var index = i; - if (ammoProvider is ProjectileBatteryAmmoProviderComponent projectileAmmo) + // Starlight-start + if (ammoProvider is BatteryAmmoProviderComponent projectileAmmo) { var entProto = _prototypeManager.Index(fireMode.Prototype); @@ -105,26 +104,7 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem args.Verbs.Add(v); } - else if (ammoProvider is HitscanBatteryAmmoProviderComponent hitscanAmmo) - { - var entProto = _prototypeManager.Index(fireMode.Prototype); - - var v = new Verb - { - Priority = 1, - Category = VerbCategory.SelectType, - Text = entProto.Name, - Disabled = i == component.CurrentFireMode, - Impact = LogImpact.Low, - DoContactInteraction = true, - Act = () => - { - SetFireMode(uid, component, index, args.User); - } - }; - - args.Verbs.Add(v); - } + // Starlight-end } } @@ -163,9 +143,11 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem private void SetFireMode(EntityUid uid, BatteryWeaponFireModesComponent component, int index, EntityUid? user = null) { + // Starlight-start + if (_net.IsClient) + return; // Why? Conditions is server side only, we can't fully move this to server, so we just drop client here + var fireMode = component.FireModes[index]; - component.CurrentFireMode = index; - Dirty(uid, component); if (fireMode.Conditions != null && user != null) { @@ -173,17 +155,17 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem var conditionsMet = fireMode.Conditions.All(condition => condition.Condition(conditionArgs)); if (!conditionsMet) - { - if (TryComp(user, out var actor)) - _popupSystem.PopupEntity(Loc.GetString("gun-alert-level-condition"), - uid, actor.PlayerSession); return; - } } + // Starlight-end + component.CurrentFireMode = index; + Dirty(uid, component); + + // Starlight-start if (TryGetAmmoProvider(uid, out var ammoProvider) && ammoProvider != null) { - if (ammoProvider is ProjectileBatteryAmmoProviderComponent projectileAmmo) + if (ammoProvider is BatteryAmmoProviderComponent projectileAmmo) { if (!_prototypeManager.TryIndex(fireMode.Prototype, out var prototype)) return; @@ -197,55 +179,40 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem projectileAmmo.Capacity = (int)Math.Round(projectileAmmo.Capacity / fireCostDiff); Dirty(uid, projectileAmmo); - _popupSystem.PopupPredicted(Loc.GetString("gun-set-fire-mode", ("mode", prototype.Name)), uid, user); + if (user != null) + _popupSystem.PopupPredicted(Loc.GetString("gun-set-fire-mode", ("mode", prototype.Name)), uid, user); } - else if (ammoProvider is HitscanBatteryAmmoProviderComponent hitscanAmmo) - { - if (!_prototypeManager.TryIndex(fireMode.Prototype, out var hitscan)) - return; - - var oldFireCost = hitscanAmmo.FireCost; - hitscanAmmo.Prototype = fireMode.Prototype; - hitscanAmmo.FireCost = fireMode.FireCost; - - float fireCostDiff = (float)fireMode.FireCost / (float)oldFireCost; - hitscanAmmo.Shots = (int)Math.Round(hitscanAmmo.Shots / fireCostDiff); - hitscanAmmo.Capacity = (int)Math.Round(hitscanAmmo.Capacity / fireCostDiff); - Dirty(uid, hitscanAmmo); - - _popupSystem.PopupPredicted(Loc.GetString("gun-set-fire-mode", ("mode", hitscan.Name)), uid, user); - } - - var updateClientAmmoEvent = new UpdateClientAmmoEvent(); - RaiseLocalEvent(uid, ref updateClientAmmoEvent); if (fireMode.HeldPrefix != null) _item.SetHeldPrefix(uid, fireMode.HeldPrefix); } + // Starlight-end + + if (TryComp(uid, out BatteryAmmoProviderComponent? batteryAmmoProviderComponent)) + { + batteryAmmoProviderComponent.Prototype = fireMode.Prototype; + batteryAmmoProviderComponent.FireCost = fireMode.FireCost; + + Dirty(uid, batteryAmmoProviderComponent); + + _gun.UpdateShots((uid, batteryAmmoProviderComponent)); + } } + # region Starlight + private bool TryGetAmmoProvider(EntityUid uid, out object? ammoProvider) { ammoProvider = null; - if (TryComp(uid, out var projectileProvider)) + if (TryComp(uid, out var provider)) { - ammoProvider = projectileProvider; - return true; - } - - if (TryComp(uid, out var hitscanProvider)) - { - ammoProvider = hitscanProvider; + ammoProvider = provider; return true; } return false; } - private BatteryWeaponFireMode GetMode(BatteryWeaponFireModesComponent component) - { - return component.FireModes[component.CurrentFireMode]; - } private void OnShootAttempt(EntityUid uid, BatteryWeaponFireModesComponent component, ref AttemptShootEvent args) { @@ -258,9 +225,7 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem var conditionsMet = fireMode.Conditions.All(condition => condition.Condition(conditionArgs)); if (!conditionsMet) - { SetFireMode(uid, component, 0, args.User); - } } } @@ -284,4 +249,6 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem SetFireMode(uid, component, index, user); } + + # endregion Starlight } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index b59983b0c3..d5c71a532c 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -1,5 +1,4 @@ using Content.Shared.DoAfter; -using Content.Shared.Emp; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; @@ -9,7 +8,8 @@ using Content.Shared.Weapons.Ranged.Events; using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Serialization; -using Robust.Shared.Prototypes; // 🌟Starlight🌟 +using Robust.Shared.Prototypes; +using Content.Shared.Emp; // 🌟Starlight🌟 namespace Content.Shared.Weapons.Ranged.Systems; @@ -18,7 +18,7 @@ public abstract partial class SharedGunSystem [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; - [MustCallBase] + protected virtual void InitializeBallistic() { SubscribeLocalEvent(OnBallisticInit); @@ -31,8 +31,7 @@ public abstract partial class SharedGunSystem SubscribeLocalEvent(OnBallisticInteractUsing); SubscribeLocalEvent(OnBallisticAfterInteract); SubscribeLocalEvent(OnBallisticAmmoFillDoAfter); - //SubscribeLocalEvent(OnBallisticUse); - + // SubscribeLocalEvent(OnBallisticUse); // Sunrise edit SubscribeLocalEvent(OnBallisticRefillerMapInit); SubscribeLocalEvent(OnRefillerEmpPulsed); } @@ -41,30 +40,39 @@ public abstract partial class SharedGunSystem { entity.Comp.NextAutoRefill = Timing.CurTime + entity.Comp.AutoRefillRate; } - // Sunrise-Start // private void OnBallisticUse(EntityUid uid, BallisticAmmoProviderComponent component, UseInHandEvent args) // { // if (args.Handled) // return; - // + // ManualCycle(uid, component, TransformSystem.GetMapCoordinates(uid), args.User); // args.Handled = true; // } - public void BallisticAmmoCockGun(EntityUid user, EntityUid uid, BallisticAmmoProviderComponent component) { ManualCycle(uid, component, TransformSystem.GetMapCoordinates(uid), user); } // Sunrise-End - private void OnBallisticInteractUsing(EntityUid uid, BallisticAmmoProviderComponent component, InteractUsingEvent args) { if (args.Handled) return; - if (TryBallisticInsert((uid, component), args.Used, args.User)) - args.Handled = true; + if (_whitelistSystem.IsWhitelistFailOrNull(component.Whitelist, args.Used)) + return; + + if (GetBallisticShots(component) >= component.Capacity) + return; + + component.Entities.Add(args.Used); + Containers.Insert(args.Used, component.Container); + // Not predicted so + Audio.PlayPredicted(component.SoundInsert, uid, args.User); + args.Handled = true; + UpdateBallisticAppearance(uid, component); + UpdateAmmoCount(args.Target); + DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.Entities)); } private void OnBallisticAfterInteract(EntityUid uid, BallisticAmmoProviderComponent component, AfterInteractEvent args) @@ -272,7 +280,8 @@ public abstract partial class SharedGunSystem { for (var i = 0; i < args.Shots; i++) { - EntityUid? ammoEntity = null; + EntityUid entity; + if (component.Entities.Count > 0) { entity = component.Entities[^1]; @@ -299,11 +308,7 @@ public abstract partial class SharedGunSystem { component.UnspawnedCount--; DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.UnspawnedCount)); - ammoEntity = Spawn(component.Proto, args.Coordinates); - } - - if (ammoEntity is { } ent) - { + entity = Spawn(component.Proto, args.Coordinates); if (TryComp(uid, out var gun)) { @@ -315,11 +320,7 @@ public abstract partial class SharedGunSystem } } - args.Ammo.Add((ent, EnsureShootable(ent))); - if (TryComp(uid, out var refiller)) - { - PauseSelfRefill((uid, refiller)); - } + args.Ammo.Add((entity, EnsureShootable(entity))); } } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs index 48677b9588..a97bb81353 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs @@ -9,6 +9,7 @@ using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Robust.Shared.Map; using Robust.Shared.Prototypes; +using Content.Shared.Power.Components; namespace Content.Shared.Weapons.Ranged.Systems; @@ -63,23 +64,6 @@ public abstract partial class SharedGunSystem { var shots = Math.Min(args.Shots, ent.Comp.Shots); - return null; - } - - if (component is HitscanBatteryAmmoProviderComponent hitscan) - { - var dmg = ProtoManager.Index(hitscan.Prototype).Damage; - return dmg == null ? dmg : dmg * Damageable.UniversalHitscanDamageModifier; - } - - return null; - } - - private void OnBatteryTakeAmmo(EntityUid uid, BatteryAmmoProviderComponent component, TakeAmmoEvent args) - { - var shots = Math.Min(args.Shots, component.Shots); - - // Don't dirty if it's an empty fire. if (shots == 0) return; @@ -102,23 +86,79 @@ public abstract partial class SharedGunSystem /// public void TakeCharge(Entity ent, int shots = 1) { - // Take charge from either the BatteryComponent or PowerCellSlotComponent. + // Take charge from either the BatteryComponent, PredictedBatteryComponent or PowerCellSlotComponent. var ev = new ChangeChargeEvent(-ent.Comp.FireCost * shots); RaiseLocalEvent(ent, ref ev); - // UpdateShots is already called by the resulting ChargeChangedEvent + // UpdateShots is already called by the resulting PredictedBatteryChargeChangedEvent or ChargeChangedEvent } private (EntityUid? Entity, IShootable) GetShootable(BatteryAmmoProviderComponent component, EntityCoordinates coordinates) { - switch (component) + + var ent = Spawn(component.Prototype, coordinates); + return (ent, EnsureShootable(ent)); + } + + public void UpdateShots(Entity ent) + { + var oldShots = ent.Comp.Shots; + var oldCapacity = ent.Comp.Capacity; + (var newShots, var newCapacity) = GetShots(ent); + + // Only dirty if necessary. + if (oldShots == newShots && oldCapacity == newCapacity) + return; + + ent.Comp.Shots = newShots; + if (newCapacity > 0) // Don't make the capacity 0 when removing a power cell as this will make it be visualized as full instead of empty. + ent.Comp.Capacity = newCapacity; + + // Update the ammo counter predictively if the change was predicted. On the server this does nothing. + UpdateAmmoCount(ent.Owner); + + Dirty(ent); // Dirtying will update the client's ammo counter in an AfterAutoHandleStateEvent subscription in case it was not predicted. + + if (!TryComp(ent, out var appearance)) + return; + + // Update the visuals. + Appearance.SetData(ent.Owner, AmmoVisuals.HasAmmo, newShots != 0, appearance); + Appearance.SetData(ent.Owner, AmmoVisuals.AmmoCount, newShots, appearance); + if (newCapacity > 0) // Don't make the capacity 0 when removing a power cell as this will make it be visualized as full instead of empty. + Appearance.SetData(ent.Owner, AmmoVisuals.AmmoMax, newCapacity, appearance); + } + + // For server side changes the client's ammo counter needs to be updated as well. + private void OnAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + UpdateAmmoCount(ent); // Need to have prediction set to true because the state is applied repeatedly while prediction is running. + } + + // For when a power cell gets inserted or removed. + private void OnPowerCellChanged(Entity ent, ref PowerCellChangedEvent args) + { + UpdateShots(ent); + } + + + // For unpredicted batteries. + private void OnChargeChanged(Entity ent, ref ChargeChangedEvent args) + { + // Update the visuals and charge counter UI. + UpdateShots(ent); + // No need to queue an update here since unpredicted batteries already update periodically as they charge/discharge. + UpdateNextUpdate(ent, args.CurrentCharge, args.MaxCharge, args.CurrentChargeRate); + } + + private void UpdateNextUpdate(Entity ent, float currentCharge, float maxCharge, float currentChargeRate) + { + // Don't queue any updates if charge is constant. + ent.Comp.NextUpdate = null; + // ETA of the next full charge. + if (currentChargeRate > 0f && currentCharge != maxCharge) { - case ProjectileBatteryAmmoProviderComponent proj: - var ent = Spawn(proj.Prototype, coordinates); - return (ent, EnsureShootable(ent)); - case HitscanBatteryAmmoProviderComponent hitscan: - return (null, ProtoManager.Index(hitscan.Prototype)); - default: - throw new ArgumentOutOfRangeException(); + ent.Comp.NextUpdate = Timing.CurTime + TimeSpan.FromSeconds((ent.Comp.FireCost - (currentCharge % ent.Comp.FireCost)) / currentChargeRate); + ent.Comp.ChargeTime = TimeSpan.FromSeconds(ent.Comp.FireCost / currentChargeRate); } else if (currentChargeRate < 0f && currentCharge != 0f) { @@ -131,15 +171,12 @@ public abstract partial class SharedGunSystem // Shots are only chached, not a DataField, so we need to refresh this when the game is loaded. private void OnBatteryStartup(Entity ent, ref ComponentStartup args) { - if (_netManager.IsClient && !IsClientSide(ent.Owner)) - return; // Don't overwrite the server state in cases where the battery is not predicted. - UpdateShots(ent); } /// /// Gets the current and maximum amount of shots from this entity's battery. - /// This works for BatteryComponent and PowercellSlotComponent. + /// This works for BatteryComponent, PredictedBatteryComponent and PowercellSlotComponent. /// public (int, int) GetShots(Entity ent) { @@ -152,7 +189,8 @@ public abstract partial class SharedGunSystem } /// - /// Update loop for refreshing the ammo counter for charging/draining batteries. + /// Update loop for refreshing the ammo counter for charging/draining predicted batteries. + /// This is not needed for unpredicted batteries since those already raise ChargeChangedEvent periodically. /// private void UpdateBattery(float frameTime) { diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 4fa8941982..ea1fb9b237 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -42,6 +42,7 @@ using Robust.Shared.Random; using Robust.Shared.Serialization; using Robust.Shared.Timing; using Robust.Shared.Utility; +using Content.Shared.Weapons.Hitscan.Events; namespace Content.Shared.Weapons.Ranged.Systems; @@ -724,14 +725,25 @@ public abstract partial class SharedGunSystem : EntitySystem protected abstract void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null); + public abstract void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound); + /// /// Used for animated effects on the client. /// [Serializable, NetSerializable] public sealed class HitscanEvent : EntityEventArgs { - public ProtoId Hitscan; - public required Effect[][] Effects { get; init; } + // Starlight - comment out the upstream Sprites list in favor of tracking the hitscan and its traces + // public List<(NetCoordinates coordinates, Angle angle, SpriteSpecifier Sprite, float Distance)> Sprites = new(); + // Starilght start - we add the traces block below, and use that instead of the sprite list above. + public required List Traces; + + // The following set of properties are copied from the HitscanBasicVisualsComponent + public SpriteSpecifier? MuzzleFlash; + public SpriteSpecifier? TravelFlash; + public SpriteSpecifier? ImpactFlash; + public ExtendedSpriteSpecifier? Bullet; + public required float Speed; } [Serializable, NetSerializable] public struct Effect diff --git a/Content.Shared/Zombies/ZombieComponent.cs b/Content.Shared/Zombies/ZombieComponent.cs index 5e7bd68a48..44007e8392 100644 --- a/Content.Shared/Zombies/ZombieComponent.cs +++ b/Content.Shared/Zombies/ZombieComponent.cs @@ -2,6 +2,7 @@ using Content.Shared.Chat.Prototypes; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Damage; +using Content.Shared.FixedPoint; using Content.Shared.Humanoid; using Content.Shared.Roles; using Content.Shared.StatusIcon; diff --git a/Content.Shared/_Starlight/Effects/DisplacementEffect.cs b/Content.Shared/_Starlight/Effects/DisplacementEffect.cs index c60de3fd8e..a4d31ddf35 100644 --- a/Content.Shared/_Starlight/Effects/DisplacementEffect.cs +++ b/Content.Shared/_Starlight/Effects/DisplacementEffect.cs @@ -12,7 +12,7 @@ namespace Content.Shared._Starlight.Effects; [Prototype("displacementEffect")] public sealed partial class DisplacementEffect : IPrototype { - [IdDataField] public string ID { get; } = null!; + [IdDataField] public string ID { get; private set; } = null!; [DataField("displacement", required: true)] public DisplacementData Displacement = null!; -} \ No newline at end of file +} diff --git a/Content.Shared/_Starlight/Medical/Items/EntitySystems/PatchSystem.cs b/Content.Shared/_Starlight/Medical/Items/EntitySystems/PatchSystem.cs index 3ede6c551f..eeab2412aa 100644 --- a/Content.Shared/_Starlight/Medical/Items/EntitySystems/PatchSystem.cs +++ b/Content.Shared/_Starlight/Medical/Items/EntitySystems/PatchSystem.cs @@ -14,6 +14,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Random; using Robust.Shared.Network; using Robust.Shared.Timing; +using Content.Shared.Damage.Components; namespace Content.Shared.Medical.Items.EntitySystems; @@ -27,7 +28,7 @@ public sealed class PatchSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - + public override void Initialize() { base.Initialize(); @@ -35,7 +36,7 @@ public sealed class PatchSystem : EntitySystem SubscribeLocalEvent(OnPatchAfterInteract); SubscribeLocalEvent(OnDoAfter); } - + private void OnPatchUse(Entity ent, ref UseInHandEvent args) { if (args.Handled) @@ -44,7 +45,7 @@ public sealed class PatchSystem : EntitySystem if (TryApply(ent, ent.Comp, args.User, args.User)) args.Handled = true; } - + private void OnPatchAfterInteract(Entity ent, ref AfterInteractEvent args) { if (args.Handled || !args.CanReach || args.Target == null) @@ -53,7 +54,7 @@ public sealed class PatchSystem : EntitySystem if (TryApply(ent, ent.Comp, args.User, args.Target.Value)) args.Handled = true; } - + private bool TryApply(EntityUid patch, PatchComponent patchComponent, EntityUid user, EntityUid target) { if (!TryComp(target, out var targetDamage)) @@ -85,31 +86,31 @@ public sealed class PatchSystem : EntitySystem _doAfter.TryStartDoAfter(doAfterEventArgs); return true; } - + private void OnDoAfter(Entity entity, ref PatchDoAfterEvent args) { if (!TryComp(args.Used, out PatchComponent? patchComponent)) return; - + var patch = args.Used.Value; if (args.Handled || args.Cancelled) return; - + var patchUser = EnsureComp(entity); patchUser.NextUpdateTime = _gameTiming.CurTime + patchUser.Delay; - + Entity? solutionEntity = null; if (_solutionContainerSystem.ResolveSolution(patch, patchComponent.SolutionContainer, ref solutionEntity, out var solution)) { foreach(var reagent in solution.Contents) patchUser.ReagentsToInsert.Add(reagent); // Move reagents from entity container to user component. } - + if (!_netManager.IsClient) QueueDel(patch); // Remove entity after moving reagents to PatchUser. } - + public override void Update(float frameTime) { base.Update(frameTime); @@ -119,15 +120,15 @@ public sealed class PatchSystem : EntitySystem { if (_gameTiming.CurTime < patchUser.NextUpdateTime) continue; - + if (patchUser.ReagentsToInsert.Count == 0) { RemComp(uid); continue; } - + patchUser.NextUpdateTime += patchUser.Delay; - + if (_solutionContainerSystem.TryGetInjectableSolution(uid, out var injectableSolution, out _) && injectableSolution != null) { for (int i = patchUser.ReagentsToInsert.Count - 1; i >= 0; i--) @@ -149,7 +150,7 @@ public sealed class PatchSystem : EntitySystem _solutionContainerSystem.Inject(uid, injectableSolution.Value, solutionToInject); } } - + } } -} \ No newline at end of file +} diff --git a/Content.Shared/_Starlight/Medical/Surgery/SharedSurgerySystem.cs b/Content.Shared/_Starlight/Medical/Surgery/SharedSurgerySystem.cs index 5ac67034d1..6ff460cb06 100644 --- a/Content.Shared/_Starlight/Medical/Surgery/SharedSurgerySystem.cs +++ b/Content.Shared/_Starlight/Medical/Surgery/SharedSurgerySystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Body.Systems; using Content.Shared.Buckle.Components; using Content.Shared.Climbing.Systems; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Content.Shared.DoAfter; using Content.Shared.GameTicking; using Content.Shared.Hands.EntitySystems; diff --git a/Content.Shared/_Starlight/Utility/EmpProperties.cs b/Content.Shared/_Starlight/Utility/EmpProperties.cs index 0d12e2610d..dfcaa07e67 100644 --- a/Content.Shared/_Starlight/Utility/EmpProperties.cs +++ b/Content.Shared/_Starlight/Utility/EmpProperties.cs @@ -8,10 +8,10 @@ namespace Content.Shared.Starlight.Utility; [DataDefinition] public partial class EmpProperties { - + [DataField("range"), ViewVariables(VVAccess.ReadWrite)] public float Range = 1.0f; - + /// /// How much energy will be consumed per battery in range /// @@ -22,12 +22,12 @@ public partial class EmpProperties /// How long it disables targets in seconds /// [DataField("disableDuration"), ViewVariables(VVAccess.ReadWrite)] - public float DisableDuration = 60f; + public TimeSpan DisableDuration = TimeSpan.FromSeconds(60); - public EmpProperties(float range, float energyConsumption, float disableDuration) + public EmpProperties(float range, float energyConsumption, TimeSpan disableDuration) { Range = range; EnergyConsumption = energyConsumption; DisableDuration = disableDuration; } -} \ No newline at end of file +} diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanCrawlerTargetEffectsComponent.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanCrawlerTargetEffectsComponent.cs new file mode 100644 index 0000000000..b642d90589 --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanCrawlerTargetEffectsComponent.cs @@ -0,0 +1,40 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// Hitscan entities that have this component will do additional effects to targets that are able to crawl. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanCrawlerTargetEffectsComponent : Component +{ + /// + /// How long the hitscan weapon will stun a target. + /// + [DataField] + public TimeSpan StunDuration = TimeSpan.Zero; + + /// + /// How long the hitscan weapon will knock down a target. + /// + [DataField] + public TimeSpan KnockdownDuration = TimeSpan.Zero; + + /// + /// How long the hitscan weapon will slow a target. + /// + [DataField] + public TimeSpan SlowDuration = TimeSpan.Zero; + + /// + /// During the slow from this weapon, how much walking will be modified. + /// + [DataField] + public float WalkSpeedMultiplier = 1f; + + /// + /// During the slow from this weapon, how much running will be modified. + /// + [DataField] + public float RunSpeedMultiplier = 1f; +} diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanEmpEffectComponent.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanEmpEffectComponent.cs new file mode 100644 index 0000000000..72bcaba1b7 --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanEmpEffectComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; +using Content.Shared.Starlight.Utility; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// Hitscan entities that have this component will cause an EMP pulse when striking a target. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanEmpEffectComponent : Component +{ + /// + /// The properties of the emp release when striking a target + /// + [DataField] + public EmpProperties Emp; +} diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanIgniteEffectComponent.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanIgniteEffectComponent.cs new file mode 100644 index 0000000000..d5fe0eb288 --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanIgniteEffectComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// Hitscan entities that have this component will attempt to heat and ignite the target. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanIgniteEffectComponent : Component +{ + /// + /// The effective temperature of the projectile + /// + [DataField] + public float Temperature = 1050f; +} diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanPierceComponent.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanPierceComponent.cs new file mode 100644 index 0000000000..74306ea52a --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanPierceComponent.cs @@ -0,0 +1,29 @@ +using Content.Shared._Starlight.Combat.Ranged.Pierce; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// Hitscan entities that have this will be able to pierce through things like people which have PiercableComponent. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanPierceComponent : Component +{ + /// + /// The chance of a ricochet occurring. + /// + [DataField] + public float Chance = 0.1f; + + /// + /// The maximum deviation in radians when this projectile pierces + /// + [DataField] + public float Deviation = 0.1f; + + /// + /// The level of material this projectile can pierce through + /// + [DataField] + public PierceLevel PierceLevel = PierceLevel.Flesh; +} diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanRicochetComponent.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanRicochetComponent.cs new file mode 100644 index 0000000000..13fbffe746 --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Components/HitscanRicochetComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Hitscan.Components; + +/// +/// Hitscan entities that have this will be able to ricochet off of things like walls which have RicochetableComponent. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class HitscanRicochetComponent : Component +{ + /// + /// The chance of a ricochet occurring. + /// + [DataField] + public float Chance = 0f; +} diff --git a/Content.Shared/_Starlight/Weapon/HitScanPierceAttemptEvent.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Events/HitScanPierceAttemptEvent.cs similarity index 86% rename from Content.Shared/_Starlight/Weapon/HitScanPierceAttemptEvent.cs rename to Content.Shared/_Starlight/Weapon/Hitscan/Events/HitScanPierceAttemptEvent.cs index cba86c6a02..e6db6f0467 100644 --- a/Content.Shared/_Starlight/Weapon/HitScanPierceAttemptEvent.cs +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Events/HitScanPierceAttemptEvent.cs @@ -3,7 +3,7 @@ using Content.Shared._Starlight.Combat.Ranged.Pierce; using Content.Shared.Inventory; using Content.Shared.Weapons.Reflect; -namespace Content.Shared._Starlight.Weapon; +namespace Content.Shared.Weapons.Hitscan.Events; [ByRefEvent] public record struct HitScanPierceAttemptEvent(PierceLevel Level, bool Pierced) : IInventoryRelayEvent diff --git a/Content.Shared/_Starlight/Weapon/HitScanRicochetAttemptEvent.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Events/HitScanRicochetAttemptEvent.cs similarity index 100% rename from Content.Shared/_Starlight/Weapon/HitScanRicochetAttemptEvent.cs rename to Content.Shared/_Starlight/Weapon/Hitscan/Events/HitScanRicochetAttemptEvent.cs diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Events/HitscanTrace.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Events/HitscanTrace.cs new file mode 100644 index 0000000000..ba93e8bbf4 --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Events/HitscanTrace.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Serialization; + +namespace Content.Shared.Weapons.Hitscan.Events; + +[Serializable, NetSerializable] +public struct HitscanTrace +{ + public Angle Angle; + public float Distance; + + public NetCoordinates? MuzzleCoordinates; + public NetCoordinates? TravelCoordinates; + public NetCoordinates ImpactCoordinates; + public NetEntity? ImpactedEnt; +} diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanCrawlerTargetEffectsSystem.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanCrawlerTargetEffectsSystem.cs new file mode 100644 index 0000000000..4d95f74f06 --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanCrawlerTargetEffectsSystem.cs @@ -0,0 +1,41 @@ +using Content.Shared.Damage.Systems; +using Content.Shared.Movement.Systems; +using Content.Shared.Stunnable; +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; + +namespace Content.Shared.Weapons.Hitscan.Systems; + +public sealed class HitscanCrawlerTargetEffectsSystem : EntitySystem +{ + [Dependency] private readonly SharedStunSystem _stunSystem = default!; + [Dependency] private readonly MovementModStatusSystem _movementMod = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHitscanHit); + } + + private void OnHitscanHit(Entity hitscan, ref HitscanRaycastFiredEvent args) + { + if (args.Data.HitEntity == null) + return; + + if (TryComp(args.Data.HitEntity.Value, out var standing)) + { + _stunSystem.TryAddStunDuration(args.Data.HitEntity.Value, hitscan.Comp.StunDuration); + + _stunSystem.TryKnockdown((args.Data.HitEntity.Value, standing), hitscan.Comp.KnockdownDuration, true); + + _movementMod.TryUpdateMovementSpeedModDuration( + args.Data.HitEntity.Value, + MovementModStatusSystem.TaserSlowdown, + hitscan.Comp.SlowDuration, + hitscan.Comp.WalkSpeedMultiplier, + hitscan.Comp.RunSpeedMultiplier + ); + } + } +} diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanEmpEffectSystem.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanEmpEffectSystem.cs new file mode 100644 index 0000000000..70d93955f7 --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanEmpEffectSystem.cs @@ -0,0 +1,28 @@ +using Robust.Shared.GameObjects; +using Content.Shared.Damage.Systems; +using Content.Shared.Emp; +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; + +namespace Content.Shared.Weapons.Hitscan.Systems; + +public sealed class HitscanEmpEffectSystem : EntitySystem +{ + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedEmpSystem _emp = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHitscanHit); + } + + private void OnHitscanHit(Entity hitscan, ref HitscanRaycastFiredEvent args) + { + if (args.Data.HitEntity == null) + return; + + _emp.EmpPulse(_transform.GetMapCoordinates(args.Data.HitEntity.Value), hitscan.Comp.Emp.Range, hitscan.Comp.Emp.EnergyConsumption, hitscan.Comp.Emp.DisableDuration); + } +} diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanPierceSystem.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanPierceSystem.cs new file mode 100644 index 0000000000..c1396d8ce3 --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanPierceSystem.cs @@ -0,0 +1,85 @@ +using Content.Shared.Actions; +using Content.Shared.Armor; +using Content.Shared.Cuffs.Components; +using Content.Shared.Damage.Components; +using Content.Shared.Damage; +using Content.Shared.DoAfter; +using Content.Shared.Inventory; +using Content.Shared.Starlight.Medical.Surgery; +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; +using Content.Shared.Weapons.Melee.Events; +using Content.Shared._Starlight.Combat.Ranged.Pierce; +using Content.Shared._Starlight.Weapon; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server._Starlight.Combat.Ranged; + +public sealed partial class PierceSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _rand = default!; + + private EntityQuery _reflectQuery; + + public override void Initialize() + { + _reflectQuery = GetEntityQuery(); + + SubscribeLocalEvent(OnHitscanHit); + SubscribeLocalEvent(OnPierceablePierce); + SubscribeLocalEvent>(OnArmorPierce); + base.Initialize(); + } + + private void OnHitscanHit(Entity hitscan, ref HitscanRaycastFiredEvent args) + { + var data = args.Data; + + if (hitscan.Comp.Chance <= 0 || data.HitEntity == null) + return; + + if (hitscan.Comp.Chance < 1 && !_rand.Prob(hitscan.Comp.Chance)) + return; + + // If we're at our maximum recursion depth, don't try to pierce + if (!_reflectQuery.TryComp(hitscan.Owner, out var reflect) || reflect.CurrentReflections > reflect.MaxReflections) + return; + + var ev = new HitScanPierceAttemptEvent(hitscan.Comp.PierceLevel, true); + RaiseLocalEvent(data.HitEntity.Value, ref ev); + + if (!ev.Pierced) + return; + + reflect.CurrentReflections++; + + var fromEffect = Transform(data.HitEntity.Value).Coordinates; + + // Give it a little bit of swim + var random = _rand.NextFloat(-hitscan.Comp.Deviation, hitscan.Comp.Deviation); + + var hitFiredEvent = new HitscanTraceEvent + { + FromCoordinates = fromEffect, + ShotDirection = (data.ShotDirection.ToAngle() + random).ToVec(), + Gun = data.Gun, + Shooter = data.HitEntity.Value, + OutputTrace = data.OutputTrace, + }; + + RaiseLocalEvent(hitscan, ref hitFiredEvent); + } + + private void OnArmorPierce(Entity ent, ref InventoryRelayedEvent args) + { + if ((byte)ent.Comp.Level > (byte)args.Args.Level) + args.Args.Pierced = false; + } + + private void OnPierceablePierce(Entity ent, ref HitScanPierceAttemptEvent args) + { + if ((byte)ent.Comp.Level > (byte)args.Level) + args.Pierced = false; + } +} diff --git a/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanRicochetSystem.cs b/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanRicochetSystem.cs new file mode 100644 index 0000000000..bbe9a5cea1 --- /dev/null +++ b/Content.Shared/_Starlight/Weapon/Hitscan/Systems/HitscanRicochetSystem.cs @@ -0,0 +1,176 @@ +using Content.Shared.Weapons.Hitscan.Components; +using Content.Shared.Weapons.Hitscan.Events; +using Content.Shared._Starlight.Combat.Ranged.Pierce; +using Content.Shared._Starlight.Weapon; +using Robust.Shared.GameObjects; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics; +using Robust.Shared.Random; +using System.Linq; +using System.Numerics; + +namespace Content.Server._Starlight.Combat.Ranged; + +public sealed partial class HitscanRicochetSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _rand = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + private EntityQuery _reflectQuery; + + public override void Initialize() + { + _reflectQuery = GetEntityQuery(); + + SubscribeLocalEvent(OnHitscanHit); + SubscribeLocalEvent(OnRicochetPierce); + base.Initialize(); + } + + private void OnHitscanHit(Entity hitscan, ref AttemptHitscanRaycastFiredEvent args) + { + var data = args.Data; + + if (hitscan.Comp.Chance <= 0 || data.HitEntity == null || data.HitPosition == null) + return; + + // If we're at our maximum recursion depth, don't try to pierce + if (!_reflectQuery.TryComp(hitscan.Owner, out var reflect) || reflect.CurrentReflections > reflect.MaxReflections) + return; + + var ev = new HitScanRicochetAttemptEvent(hitscan.Comp.Chance, data.HitPosition.Value, data.ShotDirection, false); + RaiseLocalEvent(data.HitEntity.Value, ref ev); + + if (!ev.Ricocheted) + return; + + reflect.CurrentReflections++; + + args.Cancelled = true; + + var fromEffect = Transform(data.HitEntity.Value).Coordinates; + + var hitFiredEvent = new HitscanTraceEvent + { + FromCoordinates = fromEffect, + ShotDirection = ev.Dir, + Gun = data.Gun, + Shooter = data.HitEntity.Value, + OutputTrace = data.OutputTrace, + }; + + RaiseLocalEvent(hitscan, ref hitFiredEvent); + } + + private void OnRicochetPierce(Entity ent, ref HitScanRicochetAttemptEvent args) + { + if (!TryComp(ent, out var fixtures) + || fixtures.Fixtures.Count == 0 + || fixtures.Fixtures.FirstOrDefault().Value?.Shape is not PolygonShape shape) + return; + + var chance = Math.Clamp(args.Chance * ent.Comp.Chance, 0f, 1f); + if (chance == 0) return; + + var invMatrix = _transform.GetInvWorldMatrix(ent.Owner); + + var localFrom = Vector2.Transform(args.Pos, invMatrix); + + var invNoTrans = invMatrix; + invNoTrans.M31 = 0f; + invNoTrans.M32 = 0f; + + var localDir = Vector2.Transform(args.Dir, invNoTrans).Normalized(); + + if (!RayCastPolygon(shape, localFrom, localDir, + out var tMin, out var edgeIndex, out var ptLocal)) return; + + var localNormal = shape.Normals[edgeIndex]; + + var dot = Vector2.Dot(localDir, localNormal); + + var clampedDot = Math.Clamp(MathF.Abs(dot), 0f, 1f); + + var angleFactor = 2f * (1f - clampedDot); + + chance = Math.Clamp(args.Chance * angleFactor, 0f, 1f); + if(!_rand.Prob(chance)) return; + + // R = D - 2*(D·N)*N + var reflectedLocal = localDir - (2f * dot * localNormal); + + var matrix = _transform.GetWorldMatrix(ent.Owner); + var matrixNoTrans = matrix; + matrixNoTrans.M31 = 0f; + matrixNoTrans.M32 = 0f; + + var reflectedWorld = Vector2.Transform(reflectedLocal, matrixNoTrans).Normalized(); + + args.Dir = reflectedWorld; + args.Ricocheted = true; + } + + private bool RayCastPolygon( + PolygonShape polygon, + Vector2 origin, + Vector2 dir, + out float tMin, + out int edgeIndex, + out Vector2 ptLocal, + float maxT = float.MaxValue) + { + tMin = float.MaxValue; + edgeIndex = -1; + ptLocal = default; + + var verts = polygon.Vertices; + var count = polygon.VertexCount; + + for (var i = 0; i < count; i++) + { + var next = (i + 1) % count; + var v0 = verts[i]; + var v1 = verts[next]; + + if (RayCastSegment(origin, dir, v0, v1, out var t) && t >= 0f && t < maxT) + { + if (t < tMin) + { + tMin = t; + edgeIndex = i; + } + } + } + + if (edgeIndex < 0) + return false; + + ptLocal = origin + (dir * tMin); + return true; + } + private bool RayCastSegment(Vector2 origin, Vector2 dir, Vector2 v0, Vector2 v1, out float t) + { + t = 0f; + + var edge = v1 - v0; + var denom = Cross2D(edge, dir); + + if (MathF.Abs(denom) < 1e-6f) + return false; + + var diff = origin - v0; + + var s = Cross2D(diff, dir) / denom; + if (s is < 0f or > 1f) + return false; + + var tRay = Cross2D(diff, edge) / denom; + if (tRay < 0f) + return false; + + t = tRay; + return true; + } + + private float Cross2D(Vector2 a, Vector2 b) => (a.X * b.Y) - (a.Y * b.X); +} diff --git a/Content.Shared/_Sunrise/Abilities/Felinid/FelinidLickingSystem.cs b/Content.Shared/_Sunrise/Abilities/Felinid/FelinidLickingSystem.cs index db2d992562..333fa6423b 100644 --- a/Content.Shared/_Sunrise/Abilities/Felinid/FelinidLickingSystem.cs +++ b/Content.Shared/_Sunrise/Abilities/Felinid/FelinidLickingSystem.cs @@ -13,6 +13,8 @@ using Content.Shared.Nutrition.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Serialization; using System.Diagnostics.CodeAnalysis; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; namespace Content.Shared._Sunrise.Abilities.Felinid; diff --git a/Content.Shared/_Sunrise/BloodCult/BloodCultPrototype.cs b/Content.Shared/_Sunrise/BloodCult/BloodCultPrototype.cs index c97225800a..a3e6eb228e 100644 --- a/Content.Shared/_Sunrise/BloodCult/BloodCultPrototype.cs +++ b/Content.Shared/_Sunrise/BloodCult/BloodCultPrototype.cs @@ -2,13 +2,13 @@ namespace Content.Shared._Sunrise.BloodCult; -[Prototype("bloodCult")] -public sealed class BloodCultPrototype : IPrototype +[Prototype] +public sealed partial class BloodCultPrototype : IPrototype { [IdDataField] - public string ID { get; } = null!; + public string ID { get; private set; } = null!; - [DataField( required: true)] + [DataField(required: true)] public BloodCultType? CultType; [DataField(required: true)] diff --git a/Content.Shared/_Sunrise/BloodCult/Structures/CultStructurePrototype.cs b/Content.Shared/_Sunrise/BloodCult/Structures/CultStructurePrototype.cs index d253744fe0..9beffba5af 100644 --- a/Content.Shared/_Sunrise/BloodCult/Structures/CultStructurePrototype.cs +++ b/Content.Shared/_Sunrise/BloodCult/Structures/CultStructurePrototype.cs @@ -4,21 +4,21 @@ using Robust.Shared.Utility; namespace Content.Shared._Sunrise.BloodCult.Structures; -[Prototype("cultStructure")] -public sealed class CultStructurePrototype : IPrototype +[Prototype] +public sealed partial class CultStructurePrototype : IPrototype { - [DataField("structureId", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer), required: true)] public string StructureId = string.Empty; [DataField("name", required: true)] public string StructureName = string.Empty; - [DataField("icon", required: true)] - public SpriteSpecifier Icon { get; } = default!; + [DataField(required: true)] + public SpriteSpecifier Icon { get; private set; } = default!; [IdDataField] - public string ID { get; } = default!; + public string ID { get; private set; } = default!; - [DataField( required: true)] + [DataField(required: true)] public BloodCultType? CultType; } diff --git a/Content.Shared/_Sunrise/BodyType.cs b/Content.Shared/_Sunrise/BodyType.cs index d6a37b104a..d6c24e20ad 100644 --- a/Content.Shared/_Sunrise/BodyType.cs +++ b/Content.Shared/_Sunrise/BodyType.cs @@ -3,14 +3,14 @@ using Robust.Shared.Prototypes; namespace Content.Shared._Sunrise; -[Prototype("bodyType")] -public sealed class BodyTypePrototype : IPrototype +[Prototype] +public sealed partial class BodyTypePrototype : IPrototype { [IdDataField] - public string ID { get; } = default!; + public string ID { get; private set; } = default!; [DataField(required: true)] - public string Name { get; } = default!; + public string Name = default!; [DataField(required: true)] public Dictionary Sprites = new(); diff --git a/Content.Shared/_Sunrise/Boss/Systems/SpiralMovementSystem.cs b/Content.Shared/_Sunrise/Boss/Systems/SpiralMovementSystem.cs index 4943319fa6..660ff0d8dd 100644 --- a/Content.Shared/_Sunrise/Boss/Systems/SpiralMovementSystem.cs +++ b/Content.Shared/_Sunrise/Boss/Systems/SpiralMovementSystem.cs @@ -1,5 +1,6 @@ using Content.Shared._Sunrise.Boss.Components; using Content.Shared.Damage; +using Content.Shared.Damage.Systems; using Robust.Shared.Map; using Robust.Shared.Physics.Systems; using Robust.Shared.Timing; diff --git a/Content.Shared/_Sunrise/CollectiveMind/CollectiveMindPrototype.cs b/Content.Shared/_Sunrise/CollectiveMind/CollectiveMindPrototype.cs index 4b4d2d44d6..e17233ceda 100644 --- a/Content.Shared/_Sunrise/CollectiveMind/CollectiveMindPrototype.cs +++ b/Content.Shared/_Sunrise/CollectiveMind/CollectiveMindPrototype.cs @@ -2,7 +2,7 @@ using Robust.Shared.Prototypes; namespace Content.Shared._Sunrise.CollectiveMind; -[Prototype("collectiveMind")] +[Prototype] public sealed partial class CollectiveMindPrototype : IPrototype { [DataField("name")] @@ -24,5 +24,5 @@ public sealed partial class CollectiveMindPrototype : IPrototype public bool ShowAuthor { get; private set; } = false; [IdDataField, ViewVariables] - public string ID { get; } = default!; + public string ID { get; private set; } = default!; } diff --git a/Content.Shared/_Sunrise/Disease/DiseaseRoleComponent.cs b/Content.Shared/_Sunrise/Disease/DiseaseRoleComponent.cs index ed2910886f..414552cf9f 100644 --- a/Content.Shared/_Sunrise/Disease/DiseaseRoleComponent.cs +++ b/Content.Shared/_Sunrise/Disease/DiseaseRoleComponent.cs @@ -37,8 +37,8 @@ public sealed partial class DiseaseRoleComponent : Component [DataField] public int SickOfAllTime = 0; - [DataField("newBloodReagent", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string NewBloodReagent = "ZombieBlood"; + [DataField("newBloodReagent")] + public List NewBloodReagent = new() { "ZombieBlood" }; } [Serializable, NetSerializable] @@ -46,7 +46,7 @@ public struct SymptomData { [DataField("minLevel")] public int MinLevel; - + [DataField("maxLevel")] public int MaxLevel; diff --git a/Content.Shared/_Sunrise/Disease/SickComponent.cs b/Content.Shared/_Sunrise/Disease/SickComponent.cs index e710263c7e..0b7473f0e9 100644 --- a/Content.Shared/_Sunrise/Disease/SickComponent.cs +++ b/Content.Shared/_Sunrise/Disease/SickComponent.cs @@ -30,5 +30,5 @@ public sealed partial class SickComponent : Component public TimeSpan StadyDelay = TimeSpan.FromMinutes(5); [DataField("beforeInfectedBloodReagent")] - public string BeforeInfectedBloodReagent = string.Empty; + public List BeforeInfectedBloodReagent = new(); } diff --git a/Content.Shared/_Sunrise/GhostTheme/GhostThemePrototype.cs b/Content.Shared/_Sunrise/GhostTheme/GhostThemePrototype.cs index f38cf17ae5..fd06842aa2 100644 --- a/Content.Shared/_Sunrise/GhostTheme/GhostThemePrototype.cs +++ b/Content.Shared/_Sunrise/GhostTheme/GhostThemePrototype.cs @@ -5,11 +5,11 @@ using Robust.Shared.Utility; namespace Content.Shared._Sunrise.GhostTheme; -[Prototype("ghostTheme")] -public sealed class GhostThemePrototype : IPrototype +[Prototype] +public sealed partial class GhostThemePrototype : IPrototype { [IdDataField] - public string ID { get; } = default!; + public string ID { get; private set; } = default!; [DataField("name")] public string Name { get; private set; } = string.Empty; diff --git a/Content.Shared/_Sunrise/Lobby/LobbyBackgroundPrototype.cs b/Content.Shared/_Sunrise/Lobby/LobbyBackgroundPrototype.cs deleted file mode 100644 index bbbc003738..0000000000 --- a/Content.Shared/_Sunrise/Lobby/LobbyBackgroundPrototype.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Utility; - -namespace Content.Server.GameTicking.Prototypes; - -/// -/// Prototype for a lobby background the game can choose. -/// -[Prototype] -public sealed partial class LobbyBackgroundPrototype : IPrototype -{ - /// - [IdDataField] - public string ID { get; set; } = default!; - - /// - /// The sprite to use as the background. This should ideally be 1920x1080. - /// - [DataField("background", required: true)] - public ResPath Background = default!; -} diff --git a/Content.Shared/_Sunrise/Mood/MoodCategoryPrototype.cs b/Content.Shared/_Sunrise/Mood/MoodCategoryPrototype.cs index f6d67dc770..fffd6b544a 100644 --- a/Content.Shared/_Sunrise/Mood/MoodCategoryPrototype.cs +++ b/Content.Shared/_Sunrise/Mood/MoodCategoryPrototype.cs @@ -3,8 +3,8 @@ namespace Content.Shared._Sunrise.Mood; [Prototype] -public sealed class MoodCategoryPrototype : IPrototype +public sealed partial class MoodCategoryPrototype : IPrototype { [IdDataField] - public string ID { get; } = default!; + public string ID { get; private set; } = default!; } diff --git a/Content.Shared/_Sunrise/Mood/MoodEffectPrototype.cs b/Content.Shared/_Sunrise/Mood/MoodEffectPrototype.cs index 510323ae85..f1befddaae 100644 --- a/Content.Shared/_Sunrise/Mood/MoodEffectPrototype.cs +++ b/Content.Shared/_Sunrise/Mood/MoodEffectPrototype.cs @@ -3,10 +3,10 @@ namespace Content.Shared._Sunrise.Mood; [Prototype] -public sealed class MoodEffectPrototype : IPrototype +public sealed partial class MoodEffectPrototype : IPrototype { [IdDataField] - public string ID { get; } = default!; + public string ID { get; private set; } = default!; public string Description => Loc.GetString($"mood-effect-{ID}"); diff --git a/Content.Shared/_Sunrise/Pets/PetSelectionPrototype.cs b/Content.Shared/_Sunrise/Pets/PetSelectionPrototype.cs index d408febbae..9537828b40 100644 --- a/Content.Shared/_Sunrise/Pets/PetSelectionPrototype.cs +++ b/Content.Shared/_Sunrise/Pets/PetSelectionPrototype.cs @@ -4,11 +4,11 @@ using Robust.Shared.Prototypes; namespace Content.Shared._Sunrise.Pets; -[Prototype("petSelection")] -public sealed class PetSelectionPrototype : IPrototype +[Prototype] +public sealed partial class PetSelectionPrototype : IPrototype { [IdDataField] - public string ID { get; } = default!; + public string ID { get; private set; } = default!; [DataField] public LocId Name { get; private set; } diff --git a/Content.Shared/_Sunrise/Roadmap/RoadmapVersionsPrototype.cs b/Content.Shared/_Sunrise/Roadmap/RoadmapVersionsPrototype.cs index b1f2193c33..54f2afecc3 100644 --- a/Content.Shared/_Sunrise/Roadmap/RoadmapVersionsPrototype.cs +++ b/Content.Shared/_Sunrise/Roadmap/RoadmapVersionsPrototype.cs @@ -4,9 +4,9 @@ using Robust.Shared.Serialization; namespace Content.Shared._Sunrise.Roadmap; [Prototype] -public sealed class RoadmapVersionsPrototype : IPrototype +public sealed partial class RoadmapVersionsPrototype : IPrototype { - [IdDataField] public string ID { get; } = default!; + [IdDataField] public string ID { get; private set; } = default!; [DataField] public string Fork { get; set; } = "SUNRISE"; diff --git a/Content.Shared/_Sunrise/TTS/TTSVoicePrototype.cs b/Content.Shared/_Sunrise/TTS/TTSVoicePrototype.cs index b7dec269ed..f3fc7a04c4 100644 --- a/Content.Shared/_Sunrise/TTS/TTSVoicePrototype.cs +++ b/Content.Shared/_Sunrise/TTS/TTSVoicePrototype.cs @@ -4,28 +4,28 @@ using Robust.Shared.Prototypes; namespace Content.Shared._Sunrise.TTS; [Prototype("ttsVoice")] -public sealed class TTSVoicePrototype : IPrototype +public sealed partial class TTSVoicePrototype : IPrototype { [IdDataField] - public string ID { get; } = default!; + public string ID { get; private set; } = default!; [DataField] - public string Name { get; } = string.Empty; + public string Name = string.Empty; [DataField(required: true)] - public Sex Sex { get; } + public Sex Sex; [ViewVariables(VVAccess.ReadWrite)] [DataField(required: true)] - public string Speaker { get; } = string.Empty; + public string Speaker = string.Empty; [ViewVariables(VVAccess.ReadWrite)] [DataField(required: true)] - public string Provider { get; } = string.Empty; + public string Provider = string.Empty; [DataField] - public bool RoundStart { get; } = true; + public bool RoundStart = true; [DataField] - public bool SponsorOnly { get; } + public bool SponsorOnly; } diff --git a/Resources/Locale/en-US/_strings/botany/components/plant-analyzer-component.ftl b/Resources/Locale/en-US/_strings/botany/components/plant-analyzer-component.ftl index 5bfffaf154..a6ff89db06 100644 --- a/Resources/Locale/en-US/_strings/botany/components/plant-analyzer-component.ftl +++ b/Resources/Locale/en-US/_strings/botany/components/plant-analyzer-component.ftl @@ -1,5 +1,4 @@ plant-analyzer-component-no-seed = no plant found - plant-analyzer-component-health = Health: plant-analyzer-component-age = Age: plant-analyzer-component-water = Water: @@ -7,23 +6,19 @@ plant-analyzer-component-nutrition = Nutrition: plant-analyzer-component-toxins = Toxins: plant-analyzer-component-pests = Pests: plant-analyzer-component-weeds = Weeds: - plant-analyzer-component-alive = [color=green]ALIVE[color] plant-analyzer-component-dead = [color=red]DEAD[color] plant-analyzer-component-unviable = [color=red]UNVIABLE[color] plant-analyzer-component-mutating = [color=#00ff5f]MUTATING[color] plant-analyzer-component-kudzu = [color=red]KUDZU[color] - plant-analyzer-soil = There is some [color=white]{$chemicals}[/color] in this {$holder} that {$count -> [one]has *[other]have } not been absorbed. plant-analyzer-soil-empty = There are no unabsorbed chemicals in this {$holder}. - -plant-analyzer-component-environemt = This [color=green]{$seedName}[/color] requires an atmosphere at a pressure level of [color=lightblue]{$kpa}kPa ± {$kpaTolerance}kPa[/color], temperature of [color=lightsalmon]{$temp}°k ± {$tempTolerance}°k[/color] and a light level of [color=white]{$lightLevel} ± {$lightTolerance}[/color]. +plant-analyzer-component-environemt = This [color=green]{$seedName}[/color] requires an atmosphere at a pressure level of [color=lightblue]{$kpa} kPa ± {$kpaTolerance} kPa[/color], temperature of [color=lightsalmon]{$temp} K ± {$tempTolerance} K[/color] and a light level of [color=white]{$lightLevel} ± {$lightTolerance}[/color]. plant-analyzer-component-environemt-void = This [color=green]{$seedName}[/color] has to be grown [bolditalic]in the vacuum of space[/bolditalic] at a light level of [color=white]{$lightLevel} ± {$lightTolerance}[/color]. -plant-analyzer-component-environemt-gas = This [color=green]{$seedName}[/color] requires an atmosphere containing [bold]{$gases}[/bold] at a pressure level of [color=lightblue]{$kpa}kPa ± {$kpaTolerance}kPa[/color], temperature of [color=lightsalmon]{$temp}°k ± {$tempTolerance}°k[/color] and a light level of [color=white]{$lightLevel} ± {$lightTolerance}[/color]. - +plant-analyzer-component-environemt-gas = This [color=green]{$seedName}[/color] requires an atmosphere containing [bold]{$gases}[/bold] at a pressure level of [color=lightblue]{$kpa} kPa ± {$kpaTolerance} kPa[/color], temperature of [color=lightsalmon]{$temp} K ± {$tempTolerance} K[/color] and a light level of [color=white]{$lightLevel} ± {$lightTolerance}[/color]. plant-analyzer-produce-plural = {MAKEPLURAL($thing)} plant-analyzer-output = {$yield -> [0]{$gasCount -> @@ -50,7 +45,6 @@ plant-analyzer-output = {$yield -> *[other]{" "}There are trace amounts of [color=white]{$chemicals}[/color] in its stem. } } - plant-analyzer-potency-tiny = tiny plant-analyzer-potency-small = small plant-analyzer-potency-below-average = below-average sized @@ -61,45 +55,45 @@ plant-analyzer-potency-huge = huge plant-analyzer-potency-gigantic = gigantic plant-analyzer-potency-ludicrous = ludicrously large plant-analyzer-potency-immeasurable = immeasurably large - plant-analyzer-print = Print plant-analyzer-printout-missing = N/A -plant-analyzer-printout = [color=#9FED58][head=2]Plant Analyzer Report[/head][/color]{$nl - }──────────────────────────────{$nl - }[bullet/] Species: {$seedName}{$nl - }{$indent}[bullet/] Viable: {$viable -> +plant-analyzer-printout = + {"[color=#9FED58][head=2]Plant Analyzer Report[/head][/color]"} + ────────────────────────────── + {"[bullet/]"} Species: {$seedName} + {" "}[bullet/] Viable: {$viable -> [no][color=red]No[/color] [yes][color=green]Yes[/color] *[other]{LOC("plant-analyzer-printout-missing")} - }{$nl - }{$indent}[bullet/] Endurance: {$endurance}{$nl - }{$indent}[bullet/] Lifespan: {$lifespan}{$nl - }{$indent}[bullet/] Product: [color=#a4885c]{$produce}[/color]{$nl - }{$indent}[bullet/] Kudzu: {$kudzu -> + } + {" "}[bullet/] Endurance: {$endurance} + {" "}[bullet/] Lifespan: {$lifespan} + {" "}[bullet/] Product: [color=#a4885c]{$produce}[/color] + {" "}[bullet/] Kudzu: {$kudzu -> [no][color=green]No[/color] [yes][color=red]Yes[/color] *[other]{LOC("plant-analyzer-printout-missing")} - }{$nl - }[bullet/] Growth profile:{$nl - }{$indent}[bullet/] Water: [color=cyan]{$water}[/color]{$nl - }{$indent}[bullet/] Nutrition: [color=orange]{$nutrients}[/color]{$nl - }{$indent}[bullet/] Toxins: [color=yellowgreen]{$toxins}[/color]{$nl - }{$indent}[bullet/] Pests: [color=magenta]{$pests}[/color]{$nl - }{$indent}[bullet/] Weeds: [color=red]{$weeds}[/color]{$nl - }[bullet/] Environmental profile:{$nl - }{$indent}[bullet/] Composition: [bold]{$gasesIn}[/bold]{$nl - }{$indent}[bullet/] Pressure: [color=lightblue]{$kpa}kPa ± {$kpaTolerance}kPa[/color]{$nl - }{$indent}[bullet/] Temperature: [color=lightsalmon]{$temp}°k ± {$tempTolerance}°k[/color]{$nl - }{$indent}[bullet/] Light: [color=gray][bold]{$lightLevel} ± {$lightTolerance}[/bold][/color]{$nl - }[bullet/] Flowers: {$yield -> + } + {"[bullet/]"} Growth profile: + {" "}[bullet/] Water: [color=cyan]{$water}[/color] + {" "}[bullet/] Nutrition: [color=orange]{$nutrients}[/color] + {" "}[bullet/] Toxins: [color=yellowgreen]{$toxins}[/color] + {" "}[bullet/] Pests: [color=magenta]{$pests}[/color] + {" "}[bullet/] Weeds: [color=red]{$weeds}[/color] + {"[bullet/]"} Environmental profile: + {" "}[bullet/] Composition: [bold]{$gasesIn}[/bold] + {" "}[bullet/] Pressure: [color=lightblue]{$kpa} kPa ± {$kpaTolerance} kPa[/color] + {" "}[bullet/] Temperature: [color=lightsalmon]{$temp} K ± {$tempTolerance} K[/color] + {" "}[bullet/] Light: [color=gray][bold]{$lightLevel} ± {$lightTolerance}[/bold][/color] + {"[bullet/]"} Flowers: {$yield -> [-1]{LOC("plant-analyzer-printout-missing")} [0][color=red]0[/color] *[other][color=lightgreen]{$yield} {$potency}[/color] - }{$nl - }[bullet/] Seeds: {$seeds -> + } + {"[bullet/]"} Seeds: {$seeds -> [no][color=red]No[/color] [yes][color=green]Yes[/color] *[other]{LOC("plant-analyzer-printout-missing")} - }{$nl - }[bullet/] Chemicals: [color=gray][bold]{$chemicals}[/bold][/color]{$nl - }[bullet/] Emissions: [bold]{$gasesOut}[/bold] + } + {"[bullet/]"} Chemicals: [color=gray][bold]{$chemicals}[/bold][/color] + {"[bullet/]"} Emissions: [bold]{$gasesOut}[/bold] diff --git a/Resources/Maps/_Sunrise/Station/convex.yml b/Resources/Maps/_Sunrise/Station/convex.yml index 75b85f1396..2b30991a9e 100644 --- a/Resources/Maps/_Sunrise/Station/convex.yml +++ b/Resources/Maps/_Sunrise/Station/convex.yml @@ -242866,7 +242866,7 @@ entities: projectileProto: null - type: Item heldPrefix: disabler - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider fireCost: 50 proto: BulletDisabler - proto: WeaponLaserCarbinePractice diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/Weapons/Gun/combat.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/Weapons/Gun/combat.yml index b458d83c0e..4443821518 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Mech/Weapons/Gun/combat.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/Weapons/Gun/combat.yml @@ -15,7 +15,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser3.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: Pulse # Sunrise-Edit fireCost: 80 - type: Appearance @@ -38,7 +38,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: IgnitionRedLaser # Sunrise-Edit fireCost: 35 - type: Appearance @@ -84,7 +84,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedHeavyLaser # Sunrise-Edit fireCost: 28 - type: Appearance @@ -113,7 +113,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaser fireCost: 14 - type: Appearance @@ -192,7 +192,7 @@ - SemiAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/taser2.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: TaserBolt fireCost: 40 - type: Appearance @@ -294,7 +294,7 @@ path: /Audio/Weapons/Guns/Gunshots/laser.ogg soundEmpty: path: /Audio/Weapons/Guns/Empty/empty.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: EmpPulse fireCost: 400 - type: Appearance @@ -326,7 +326,7 @@ path: /Audio/Weapons/Guns/Gunshots/laser.ogg soundEmpty: path: /Audio/Weapons/Guns/Empty/empty.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaserBeam fireCost: 20 - type: Appearance diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/Weapons/Gun/debug.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/Weapons/Gun/debug.yml index 0783ed4f47..3f7297eebe 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Mech/Weapons/Gun/debug.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/Weapons/Gun/debug.yml @@ -49,7 +49,7 @@ - state: mag-unshaded-4 map: ["enum.GunVisualLayers.MagUnshaded"] shader: unshaded - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedMediumLaser fireCost: 19 - type: MagazineVisuals @@ -87,7 +87,7 @@ - SemiAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/taser2.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 19 - type: MagazineVisuals diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 075f5bcc3d..f8f1f5e2f8 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -549,7 +549,7 @@ projectileSpeed: 35 # any higher and this causes issues in lag soundGunshot: path: /Audio/Weapons/Guns/Gunshots/taser2.ogg - - type: HitscanBatteryAmmoProvider # Sunrise-Edit + - type: BatteryAmmoProvider # Sunrise-Edit proto: DisablerBoltPractice fireCost: 50 - type: Tag @@ -578,7 +578,7 @@ slots: - suitStorage - Belt - - type: HitscanBatteryAmmoProvider # Sunrise-Edit + - type: BatteryAmmoProvider # Sunrise-Edit proto: DisablerBolt fireCost: 62.5 - type: GuideHelp @@ -616,7 +616,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/taser2.ogg - - type: HitscanBatteryAmmoProvider # Sunrise-Edit + - type: BatteryAmmoProvider # Sunrise-Edit proto: DisablerBoltSmg fireCost: 25 - type: MagazineVisuals diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml index d46b1df0db..89e5484fb6 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml @@ -149,7 +149,7 @@ - type: ContainerContainer containers: ballistic-ammo: !type:Container - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: BulletRifleTraceSP fireCost: 100 - type: Battery diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml index 64cd174798..c70794ca3a 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml @@ -139,7 +139,7 @@ # - type: ContainerContainer # containers: # ballistic-ammo: !type:Container - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: BulletPistolTraceSP fireCost: 100 - type: Battery diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml b/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml index 7e89905c21..f5119f9fa8 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml @@ -801,7 +801,7 @@ whitelist: tags: - PowerCage - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedShuttleMediumLaser fireCost: 30 diff --git a/Resources/Prototypes/_Starlight/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/_Starlight/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index f9e562609c..4da2a288c0 100644 --- a/Resources/Prototypes/_Starlight/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/_Starlight/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -33,7 +33,7 @@ magState: disable steps: 5 zeroVisible: true - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 50 - type: BatteryWeaponFireModes @@ -79,7 +79,7 @@ autoRechargeRate: 15 autoRechargePause: true autoRechargePauseTime: 10 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 50 - type: BatteryWeaponFireModes @@ -142,7 +142,7 @@ magState: disable steps: 5 zeroVisible: true - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 50 - type: BatteryWeaponFireModes @@ -232,7 +232,7 @@ autoRechargeRate: 2 autoRechargePause: true autoRechargePauseTime: 60 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: EmpPulse fireCost: 125 - type: MagazineVisuals @@ -271,7 +271,7 @@ autoRechargeRate: 2 autoRechargePause: true autoRechargePauseTime: 60 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: EmpPulse fireCost: 250 - type: MagazineVisuals @@ -299,7 +299,7 @@ selectedMode: SemiAuto availableModes: - SemiAuto - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: IgnitionRedLaser fireCost: 62.5 - type: StaticPrice @@ -320,7 +320,7 @@ - state: mag-unshaded-4 map: ["enum.GunVisualLayers.MagUnshaded"] shader: unshaded - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DecloneBolt fireCost: 200 - type: MagazineVisuals @@ -355,7 +355,7 @@ magState: stun steps: 5 zeroVisible: true - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: TaserBolt fireCost: 100 - type: BatteryWeaponFireModes @@ -394,7 +394,7 @@ magState: disable steps: 5 zeroVisible: true - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 75 - type: BatteryWeaponFireModes @@ -433,7 +433,7 @@ - Back - type: Gun fireRate: 0.1 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: SniperBolt fireCost: 100 - type: MagazineVisuals @@ -466,7 +466,7 @@ magState: destroy steps: 5 zeroVisible: true - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DestroyBeam fireCost: 100 - type: BatteryWeaponFireModes diff --git a/Resources/Prototypes/_Starlight/Entities/Objects/Weapons/Melee/cyberlimb.yml b/Resources/Prototypes/_Starlight/Entities/Objects/Weapons/Melee/cyberlimb.yml index b5b60a9b45..a8e0d4a425 100644 --- a/Resources/Prototypes/_Starlight/Entities/Objects/Weapons/Melee/cyberlimb.yml +++ b/Resources/Prototypes/_Starlight/Entities/Objects/Weapons/Melee/cyberlimb.yml @@ -319,7 +319,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLightLaser fireCost: 75 - type: BatterySelfRecharger diff --git a/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Mech/Weapons/Gun/combat.yml b/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Mech/Weapons/Gun/combat.yml index df27f4135c..bfa680d2a6 100644 --- a/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Mech/Weapons/Gun/combat.yml +++ b/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Mech/Weapons/Gun/combat.yml @@ -213,7 +213,7 @@ collection: syndi_plasma params: volume: -1 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaser # Sunrise-TODO: Нужен товый прототип для данной пушки fireCost: 25 - type: Appearance diff --git a/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Mech/Weapons/Gun/misc.yml b/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Mech/Weapons/Gun/misc.yml index b15a5cea57..7df3d29539 100644 --- a/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Mech/Weapons/Gun/misc.yml +++ b/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Mech/Weapons/Gun/misc.yml @@ -41,7 +41,7 @@ - SemiAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedMediumLaser fireCost: 100 - type: Appearance diff --git a/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Robotics/borg_tools.yml b/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Robotics/borg_tools.yml index c9d58ec758..fb0daa5822 100644 --- a/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Robotics/borg_tools.yml +++ b/Resources/Prototypes/_Sunrise/Entities/Objects/Specific/Robotics/borg_tools.yml @@ -65,7 +65,7 @@ - type: Gun soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 100 - type: BatteryWeaponFireModes @@ -568,7 +568,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg - - type: HitscanBatteryAmmoProvider #ProjectileBatteryAmmoProvider + - type: BatteryAmmoProvider #ProjectileBatteryAmmoProvider proto: Pulse fireCost: 200 - type: Battery @@ -606,7 +606,7 @@ volume: -1 availableModes: - FullAuto - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaser # Sunrise-TODO: Нужен товый прототип для данной пушки fireCost: 50 - type: Battery @@ -646,7 +646,7 @@ volume: 10 soundEmpty: path: /Audio/Weapons/Guns/Empty/lmg_empty.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: BulletRifleTraceSP fireCost: 50 - type: Battery @@ -756,7 +756,7 @@ - FullAuto soundEmpty: path: /Audio/Weapons/Guns/Empty/lmg_empty.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: BulletPistolTraceSP fireCost: 50 - type: Battery @@ -901,7 +901,7 @@ - type: Gun soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedHeavyLaser #no levelcode system, because I give em disablersmg fireCost: 100 - type: Battery @@ -941,7 +941,7 @@ - FullAuto soundGunshot: collection: glock22 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: BulletPistolTraceRubber fireCost: 100 - type: BatteryWeaponFireModes # No AP, or Uranium rounds because can be created roundstart. @@ -1019,7 +1019,7 @@ - SemiAuto soundGunshot: collection: galil - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: BulletRifleTraceSP fireCost: 100 - type: BatteryWeaponFireModes # No AP, or Uranium rounds for balance reasons. Also no Levelcode system, and rubber bullets, because fck em. diff --git a/Resources/Prototypes/_Sunrise/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/_Sunrise/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index e5a760efff..1446a45b03 100644 --- a/Resources/Prototypes/_Sunrise/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/_Sunrise/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -20,7 +20,7 @@ - FullAuto soundGunshot: path: /Audio/_Sunrise/Weapons/Guns/Lasers/EG_R.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLightLaser fireCost: 30 - type: ItemSlots @@ -117,7 +117,7 @@ - type: Battery maxCharge: 1500 startingCharge: 1500 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 60 - type: BatteryWeaponFireModes @@ -170,7 +170,7 @@ autoRechargeRate: 60 autoRechargePause: true autoRechargePauseTime: 6 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 90 - type: BatteryWeaponFireModes @@ -214,7 +214,7 @@ - state: mag-unshaded-0 map: ["enum.GunVisualLayers.MagUnshaded"] shader: unshaded - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 100 - type: BatteryWeaponFireModes @@ -299,7 +299,7 @@ - type: Battery maxCharge: 1000 startingCharge: 1000 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 50 - type: BatteryWeaponFireModes @@ -351,7 +351,7 @@ - type: Battery maxCharge: 1000 startingCharge: 1000 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 50 - type: BatteryWeaponFireModes @@ -415,7 +415,7 @@ autoRechargeRate: 14 # ~2 minute autoRechargePause: true autoRechargePauseTime: 30 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 49 - type: BatteryWeaponFireModes @@ -453,7 +453,7 @@ - state: mag-unshaded-2 map: ["enum.GunVisualLayers.MagUnshaded"] shader: unshaded - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaser fireCost: 62.5 - type: MagazineVisuals @@ -503,7 +503,7 @@ - type: Battery maxCharge: 3000 startingCharge: 3000 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaser fireCost: 50 - type: Wieldable @@ -548,7 +548,7 @@ - type: Battery maxCharge: 2000 startingCharge: 2000 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaser fireCost: 50 - type: Wieldable @@ -597,7 +597,7 @@ - type: Battery maxCharge: 8000 startingCharge: 8000 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaser fireCost: 50 - type: Wieldable @@ -647,7 +647,7 @@ - type: Battery maxCharge: 2500 startingCharge: 2500 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: XrayLaser fireCost: 100 - type: Wieldable @@ -696,7 +696,7 @@ - type: Battery maxCharge: 1800 startingCharge: 1800 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedHeavyLaser fireCost: 100 - type: Wieldable @@ -744,7 +744,7 @@ volume: 5 availableModes: - FullAuto - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedHeavyLaser # Sunrise-TODO: Нужен товый прототип для данной пушки fireCost: 40 - type: Wieldable @@ -796,7 +796,7 @@ availableModes: - SemiAuto - FullAuto - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaserBeam # Sunrise-TODO: Нужен товый прототип для данной пушки fireCost: 50 - type: BatterySelfRecharger @@ -843,7 +843,7 @@ volume: -1 availableModes: - FullAuto - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLaserBeam # Sunrise-TODO: Нужен товый прототип для данной пушки fireCost: 25 - type: MultiHandedItem @@ -876,7 +876,7 @@ size: Small shape: - 0, 0, 1, 1 - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 50 - type: BatteryWeaponFireModes @@ -927,7 +927,7 @@ - SemiAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser3.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: AdvancedDisablerBolt fireCost: 1000 - type: BatteryWeaponFireModes @@ -981,7 +981,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser3.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: DisablerBolt fireCost: 1500 - type: BatteryWeaponFireModes @@ -1037,7 +1037,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser3.ogg - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: AdvancedDisablerBolt fireCost: 1500 - type: BatteryWeaponFireModes @@ -1100,7 +1100,7 @@ magState: stun steps: 5 zeroVisible: true - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: AdvancedDisablerBolt fireCost: 1500 - type: BatteryWeaponFireModes @@ -1149,7 +1149,7 @@ volume: -2 availableModes: - FullAuto - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLightLaser fireCost: 33 - type: BatterySelfRecharger diff --git a/Resources/Prototypes/_Sunrise/NPCs/pirates.yml b/Resources/Prototypes/_Sunrise/NPCs/pirates.yml index 982cd55740..f278709f6f 100644 --- a/Resources/Prototypes/_Sunrise/NPCs/pirates.yml +++ b/Resources/Prototypes/_Sunrise/NPCs/pirates.yml @@ -2253,7 +2253,7 @@ shader: unshaded - sprite: Objects/Tanks/oxygen.rsi state: equipped-SUITSTORAGE - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedLightLaser fireCost: 1 - type: BatterySelfRecharger @@ -2297,7 +2297,7 @@ shader: unshaded - sprite: Objects/Tanks/oxygen.rsi state: equipped-SUITSTORAGE - - type: HitscanBatteryAmmoProvider + - type: BatteryAmmoProvider proto: RedHeavyLaser fireCost: 1 - type: BatterySelfRecharger