diff --git a/Content.Server/Power/EntitySystems/BatterySystem.cs b/Content.Server/Power/EntitySystems/BatterySystem.cs index cbe61f6671..0567a2222b 100644 --- a/Content.Server/Power/EntitySystems/BatterySystem.cs +++ b/Content.Server/Power/EntitySystems/BatterySystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Cargo.Systems; using Content.Server.Emp; using Content.Shared.Emp; @@ -5,6 +6,7 @@ using Content.Shared.Examine; using Content.Shared.Rejuvenate; using JetBrains.Annotations; +using Robust.Shared.Containers; using Robust.Shared.Utility; namespace Content.Server.Power.EntitySystems @@ -12,6 +14,8 @@ namespace Content.Server.Power.EntitySystems [UsedImplicitly] public sealed class BatterySystem : EntitySystem { + [Dependency] private readonly SharedContainerSystem _containers = default!; // WD EDIT + public override void Initialize() { base.Initialize(); @@ -181,5 +185,33 @@ public bool IsFull(EntityUid uid, BatteryComponent? battery = null) return battery.CurrentCharge / battery.MaxCharge >= 0.99f; } + + // WD EDIT START + public bool TryGetBatteryComponent(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? battery, + [NotNullWhen(true)] out EntityUid? batteryUid) + { + if (TryComp(uid, out battery)) + { + batteryUid = uid; + return true; + } + + if (!_containers.TryGetContainer(uid, "cell_slot", out var container) + || container is not ContainerSlot slot) + { + battery = null; + batteryUid = null; + return false; + } + + batteryUid = slot.ContainedEntity; + + if (batteryUid != null) + return TryComp(batteryUid, out battery); + + battery = null; + return false; + } + // WD EDIT END } } diff --git a/Content.Server/PowerCell/PowerCellSystem.cs b/Content.Server/PowerCell/PowerCellSystem.cs index f45a01b2e1..68f00b2903 100644 --- a/Content.Server/PowerCell/PowerCellSystem.cs +++ b/Content.Server/PowerCell/PowerCellSystem.cs @@ -232,9 +232,9 @@ private void OnCellSlotExamined(EntityUid uid, PowerCellSlotComponent component, OnBatteryExamined(uid, battery, args); } - private void OnBatteryExamined(EntityUid uid, BatteryComponent? component, ExaminedEvent args) + public void OnBatteryExamined(EntityUid uid, BatteryComponent? component, ExaminedEvent args) // WD EDIT { - if (component != null) + if (Resolve(uid, ref component, false)) // WD EDIT { var charge = component.CurrentCharge / component.MaxCharge * 100; args.PushMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{charge:F0}"))); diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 8c7234593f..eedeb3b077 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -210,7 +210,7 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? FireEffects(fromEffect, result.Distance, dir.Normalized().ToAngle(), hitscan, hit); - var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false); + var ev = new HitScanReflectAttemptEvent(user, gunUid, hitscan.Reflective, dir, false, hitscan.Damage); // WD EDIT RaiseLocalEvent(hit, ref ev); if (!ev.Reflected) diff --git a/Content.Server/_White/Blocking/RechargeableBlockingComponent.cs b/Content.Server/_White/Blocking/RechargeableBlockingComponent.cs new file mode 100644 index 0000000000..4ad7a354a9 --- /dev/null +++ b/Content.Server/_White/Blocking/RechargeableBlockingComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server._White.Blocking; + +[RegisterComponent] +public sealed partial class RechargeableBlockingComponent : Component +{ + [DataField] + public float RechargeDelay = 30f; + + [ViewVariables] + public bool Discharged; +} diff --git a/Content.Server/_White/Blocking/RechargeableBlockingSystem.cs b/Content.Server/_White/Blocking/RechargeableBlockingSystem.cs new file mode 100644 index 0000000000..b39908c565 --- /dev/null +++ b/Content.Server/_White/Blocking/RechargeableBlockingSystem.cs @@ -0,0 +1,91 @@ +using Content.Server.Item; +using Content.Server.Popups; +using Content.Server.Power.Components; +using Content.Server.Power.EntitySystems; +using Content.Server.PowerCell; +using Content.Shared.Damage; +using Content.Shared.Examine; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.PowerCell.Components; + +namespace Content.Server._White.Blocking; + +public sealed class RechargeableBlockingSystem : EntitySystem +{ + [Dependency] private readonly BatterySystem _battery = default!; + [Dependency] private readonly ItemToggleSystem _itemToggle = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly PowerCellSystem _powerCell = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnDamageChanged); + SubscribeLocalEvent(AttemptToggle); + SubscribeLocalEvent(OnChargeChanged); + SubscribeLocalEvent(OnPowerCellChanged); + } + + private void OnExamined(EntityUid uid, RechargeableBlockingComponent component, ExaminedEvent args) + { + BatteryComponent? batteryComponent = null; + + if (component.Discharged) + { + args.PushMarkup(Loc.GetString("rechargeable-blocking-discharged")); + if (_battery.TryGetBatteryComponent(uid, out batteryComponent, out var batteryUid) + && TryComp(batteryUid, out var recharger) + && recharger is { AutoRechargeRate: > 0, AutoRecharge: true }) + { + var remainingTime = (int) (component.RechargeDelay - batteryComponent.CurrentCharge) / recharger.AutoRechargeRate; + args.PushMarkup(Loc.GetString("rechargeable-blocking-remaining-time", ("remainingTime", remainingTime))); + } + } + + _powerCell.OnBatteryExamined(uid, batteryComponent, args); + } + + private void OnDamageChanged(EntityUid uid, RechargeableBlockingComponent component, DamageChangedEvent args) + { + if (!_battery.TryGetBatteryComponent(uid, out var batteryComponent, out var batteryUid) + || !_itemToggle.IsActivated(uid) + || args.DamageDelta == null) + return; + + var batteryUse = Math.Min(args.DamageDelta.GetTotal().Float(), batteryComponent.CurrentCharge); + _battery.TryUseCharge(batteryUid.Value, batteryUse, batteryComponent); + } + + private void AttemptToggle(EntityUid uid, RechargeableBlockingComponent component, ref ItemToggleActivateAttemptEvent args) + { + if (!component.Discharged) + return; + + _popup.PopupEntity(Loc.GetString("stunbaton-component-low-charge"), args.User ?? uid); + args.Cancelled = true; + } + private void OnChargeChanged(EntityUid uid, RechargeableBlockingComponent component, ChargeChangedEvent args) + { + CheckCharge(uid, component); + } + + private void OnPowerCellChanged(EntityUid uid, RechargeableBlockingComponent component, PowerCellChangedEvent args) + { + CheckCharge(uid, component); + } + + private void CheckCharge(EntityUid uid, RechargeableBlockingComponent component) + { + if (!_battery.TryGetBatteryComponent(uid, out var battery, out _)) + return; + + if (battery.CurrentCharge < 1) + { + component.Discharged = true; + _itemToggle.TryDeactivate(uid, predicted: false); + } + + if (battery.CurrentCharge > component.RechargeDelay) + component.Discharged = false; + } +} diff --git a/Content.Shared/Weapons/Ranged/Events/HitScanReflectAttempt.cs b/Content.Shared/Weapons/Ranged/Events/HitScanReflectAttempt.cs index 31c9c4cdd4..8a80450307 100644 --- a/Content.Shared/Weapons/Ranged/Events/HitScanReflectAttempt.cs +++ b/Content.Shared/Weapons/Ranged/Events/HitScanReflectAttempt.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Shared.Damage; using Content.Shared.Weapons.Reflect; namespace Content.Shared.Weapons.Ranged.Events; @@ -8,4 +9,5 @@ namespace Content.Shared.Weapons.Ranged.Events; /// and changing where shot will go next /// [ByRefEvent] -public record struct HitScanReflectAttemptEvent(EntityUid? Shooter, EntityUid SourceItem, ReflectType Reflective, Vector2 Direction, bool Reflected); +public record struct HitScanReflectAttemptEvent(EntityUid? Shooter, EntityUid SourceItem, ReflectType Reflective, + Vector2 Direction, bool Reflected, DamageSpecifier? Damage); // WD EDIT diff --git a/Content.Shared/Weapons/Reflect/ReflectComponent.cs b/Content.Shared/Weapons/Reflect/ReflectComponent.cs index 5d8432ac77..14fbafc07b 100644 --- a/Content.Shared/Weapons/Reflect/ReflectComponent.cs +++ b/Content.Shared/Weapons/Reflect/ReflectComponent.cs @@ -57,6 +57,11 @@ public sealed partial class ReflectComponent : Component /// [DataField] public float MinReflectProb = 0.1f; + + // WD START + [DataField, AutoNetworkedField] + public float DamageOnReflectModifier; + // WD END } [Flags] diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 36dbedb4cb..af57f36a2f 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Audio; +using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Database; using Content.Shared.Gravity; @@ -41,6 +42,7 @@ public sealed class ReflectSystem : EntitySystem [Dependency] private readonly SharedGravitySystem _gravity = default!; [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; // WD EDIT public override void Initialize() { @@ -65,7 +67,7 @@ private void OnReflectUserHitscan(EntityUid uid, ReflectUserComponent component, foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.WITHOUT_POCKET)) { - if (!TryReflectHitscan(uid, ent, args.Shooter, args.SourceItem, args.Direction, out var dir)) + if (!TryReflectHitscan(uid, ent, args.Shooter, args.SourceItem, args.Direction, args.Damage, out var dir)) continue; args.Direction = dir.Value; @@ -134,6 +136,14 @@ private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid if (Resolve(projectile, ref projectileComp, false)) { + // WD EDIT START + if (reflect.DamageOnReflectModifier != 0) + { + _damageable.TryChangeDamage(reflector, projectileComp.Damage * reflect.DamageOnReflectModifier, + projectileComp.IgnoreResistances, origin: projectileComp.Shooter); + } + // WD EDIT END + _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)} from {ToPrettyString(projectileComp.Weapon)} shot by {projectileComp.Shooter}"); projectileComp.Shooter = user; @@ -184,7 +194,7 @@ private void OnReflectHitscan(EntityUid uid, ReflectComponent component, ref Hit return; } - if (TryReflectHitscan(uid, uid, args.Shooter, args.SourceItem, args.Direction, out var dir)) + if (TryReflectHitscan(uid, uid, args.Shooter, args.SourceItem, args.Direction, args.Damage, out var dir)) // WD EDIT { args.Direction = dir.Value; args.Reflected = true; @@ -197,6 +207,7 @@ private bool TryReflectHitscan( EntityUid? shooter, EntityUid shotSource, Vector2 direction, + DamageSpecifier? damage, // WD EDIT [NotNullWhen(true)] out Vector2? newDirection) { if (!TryComp(reflector, out var reflect) || @@ -220,6 +231,11 @@ private bool TryReflectHitscan( _audio.PlayPvs(reflect.SoundOnReflect, user, AudioHelpers.WithVariation(0.05f, _random)); } + // WD EDIT START + if (reflect.DamageOnReflectModifier != 0 && damage != null) + _damageable.TryChangeDamage(reflector, damage * reflect.DamageOnReflectModifier, origin: shooter); + // WD EDIT END + var spread = _random.NextAngle(-reflect.Spread / 2, reflect.Spread / 2); newDirection = -spread.RotateVec(direction); diff --git a/Resources/Locale/en-US/_white/rechargeable-blocking/rechargeable-blocking.ftl b/Resources/Locale/en-US/_white/rechargeable-blocking/rechargeable-blocking.ftl new file mode 100644 index 0000000000..607f65ab5c --- /dev/null +++ b/Resources/Locale/en-US/_white/rechargeable-blocking/rechargeable-blocking.ftl @@ -0,0 +1,2 @@ +rechargeable-blocking-discharged = It is [color=red]discharged[/color]. +rechargeable-blocking-remaining-time = Time left to charge: [color=green]{ $remainingTime }[/color] seconds. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_white/rechargeable-blocking/rechargeable-blocking.ftl b/Resources/Locale/ru-RU/_white/rechargeable-blocking/rechargeable-blocking.ftl new file mode 100644 index 0000000000..5df45a4784 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/rechargeable-blocking/rechargeable-blocking.ftl @@ -0,0 +1,2 @@ +rechargeable-blocking-discharged = Он [color=red]разряжен[/color]. +rechargeable-blocking-remaining-time = Осталось времени для зарядки: [color=green]{ $remainingTime }[/color] секунд. \ No newline at end of file diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 03e0743509..79b0d25139 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -1556,7 +1556,7 @@ icon: { sprite: /Textures/Objects/Weapons/Melee/e_shield.rsi, state: eshield-on } productEntity: EnergyShield cost: - Telecrystal: 8 + Telecrystal: 6 # WD EDIT categories: - UplinkMisc conditions: diff --git a/Resources/Prototypes/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Entities/Objects/Shields/shields.yml index 34f517b1a7..ab25a003a2 100644 --- a/Resources/Prototypes/Entities/Objects/Shields/shields.yml +++ b/Resources/Prototypes/Entities/Objects/Shields/shields.yml @@ -366,10 +366,17 @@ description: Exotic energy shield, when folded, can even fit in your pocket. components: - type: ItemToggle + predictable: false # WD EDIT soundActivate: path: /Audio/Weapons/ebladeon.ogg soundDeactivate: path: /Audio/Weapons/ebladeoff.ogg + # WD EDIT START + soundFailToActivate: + path: /Audio/Machines/button.ogg + params: + variation: 0.250 + # WD EDIT END - type: ItemToggleActiveSound activeSound: path: /Audio/Weapons/ebladehum.ogg @@ -405,12 +412,12 @@ netsync: false enabled: false radius: 1.5 - energy: 2 - color: blue + energy: 0.7 # WD EDIT + color: "#678AD9" # WD EDIT - type: Reflect enabled: false - reflectProb: 0.95 - innate: true + reflectProb: 1 # WD EDIT + damageOnReflectModifier: 0.5 # WD EDIT reflects: - Energy - type: Blocking @@ -432,31 +439,18 @@ - type: Appearance - type: Damageable damageContainer: Shield - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 180 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - BrokenEnergyShield: - min: 1 - max: 1 - type: StaticPrice price: 350 - - type: MeleeBlock # WD EDIT + # WD EDIT START + - type: MeleeBlock + - type: Battery + maxCharge: 40 + startingCharge: 40 + - type: BatterySelfRecharger + autoRecharge: true + autoRechargeRate: 1 + - type: RechargeableBlocking + # WD EDIT END - type: entity name: broken energy shield