Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Robust.Shared.GameStates;

namespace Content.Shared._Omu.RadiusBuff.Components;

/// <summary>
/// Activates a <see cref="RadiusBuffComponent"/> when this entity is wielded
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class ActivateBuffOnWieldComponent : Component
{
/// <summary>
/// Deactivate on wield instead
/// </summary>
[DataField]
public bool Invert = false;
}
64 changes: 64 additions & 0 deletions Content.Shared/_Omu/RadiusBuff/Components/RadiusBuffComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Content.Shared.Whitelist;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;

namespace Content.Shared._Omu.RadiusBuff.Components;

/// <summary>
/// Defines a status effect that an entity should give in a radius under some condition.
/// </summary>
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState, AutoGenerateComponentPause]
public sealed partial class RadiusBuffComponent : Component
{
[DataField]
public bool Active = false;

[DataField]
public float Range = 4f;

/// <summary>
/// Seconds between application of the buff.
/// </summary>
[DataField]
public TimeSpan Delay = TimeSpan.FromSeconds(3);

/// <summary>
/// Duration of the buff. Buff will be continuous in range as long as this is more than delay.
/// </summary>
[DataField]
public TimeSpan Duration = TimeSpan.FromSeconds(5);

/// <summary>
/// When this comp will next attempt to apply a buff
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoNetworkedField, AutoPausedField]
public TimeSpan NextCheck = TimeSpan.Zero;

[DataField]
public EntityWhitelist? Whitelist;

[DataField]
public EntityWhitelist? Blacklist;

/// <summary>
/// If this buff should apply to the entity that is providing this buff, regardless of the whitelist.
/// </summary>
[DataField]
public bool BuffSelf = false;

/// <summary>
/// If this buff should apply to the entity that is holding the item providing this buff, regardless of the whitelist.
/// Assumes that the entity with this component is an item.
/// </summary>
[DataField]
public bool BuffHolder = true;

/// <summary>
/// Proto of the status effect to apply.
/// </summary>
[DataField(required: true)]
public EntProtoId Status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Omu.RadiusBuff.Components;

/// <summary>
/// Marks entities that should receive a buff from <see cref="RadiusBuffComponent"/>
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class RadiusBuffReceiverComponent : Component;
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Content.Shared.Damage;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;

namespace Content.Shared._Omu.RadiusBuff.Components;

/// <summary>
/// Component for the shredder buff status effect.
/// </summary>
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState, AutoGenerateComponentPause]
public sealed partial class ShredderStatusEffectComponent : Component
{
/// <summary>
/// Healing provided, in intervals specified by Delay
/// </summary>
[DataField]
public DamageSpecifier HealBuff = new()
{
DamageDict =
{
{"Blunt", -0.8f},
{"Slash", -0.8f},
{"Piercing", -0.8f},
{"Heat", -0.5},
{"Cold", -0.5},
{"Shock", -0.5},
{"Asphyxiation", -0.8},
{"Bloodloss", -0.2},
},
};

/// <summary>
/// When the status effect will next.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoNetworkedField, AutoPausedField]
public TimeSpan NextCheck = TimeSpan.Zero;

/// <summary>
/// Seconds between healing and visual effects.
/// </summary>
[DataField]
public TimeSpan Delay = TimeSpan.FromSeconds(3);

/// <summary>
/// Effect entity to use for the buff visual.
/// </summary>
[DataField]
public EntProtoId? Visual = "EffectShredder";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Content.Shared._Omu.RadiusBuff.Components;
using Content.Shared.Wieldable;
using Content.Shared.Wieldable.Components;

namespace Content.Shared._Omu.RadiusBuff.Systems;

public sealed class ActivateBuffOnWieldSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<ActivateBuffOnWieldComponent, ItemWieldedEvent>(OnWield);
SubscribeLocalEvent<ActivateBuffOnWieldComponent, ItemUnwieldedEvent>(OnUnwield);
}

private void OnWield(EntityUid ent, ActivateBuffOnWieldComponent comp, ItemWieldedEvent args)
{
if (!TryComp<RadiusBuffComponent>(ent, out var buffComp))
return;

// Just in case...
if (!TryComp<WieldableComponent>(ent, out var wield) || !wield.Wielded)
return;

// True if inverted, false if not
buffComp.Active = !comp.Invert;
Dirty(ent, comp);
}

private void OnUnwield(EntityUid ent, ActivateBuffOnWieldComponent comp, ItemUnwieldedEvent args)
{
if (!TryComp<RadiusBuffComponent>(ent, out var buffComp))
return;

if (!TryComp<WieldableComponent>(ent, out var wield) || wield.Wielded)
return;

// False if inverted, true if not
buffComp.Active = comp.Invert;
Dirty(ent, comp);
}
}
57 changes: 57 additions & 0 deletions Content.Shared/_Omu/RadiusBuff/Systems/RadiusBuffSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Content.Shared._Omu.RadiusBuff.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.StatusEffectNew;
using Content.Shared.Whitelist;
using Robust.Shared.Timing;

namespace Content.Shared._Omu.RadiusBuff.Systems;

public sealed class RadiusBuffSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly StatusEffectsSystem _status = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;

public override void Update(float frameTime)
{
if (!_timing.IsFirstTimePredicted)
return;

var query = EntityQueryEnumerator<RadiusBuffComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var comp, out var xform))
{
if (!comp.Active)
continue;

if (comp.NextCheck >= _timing.CurTime)
continue;

var lookup = _lookup.GetEntitiesInRange(xform.Coordinates, comp.Range);
foreach (var target in lookup)
{
if (Allowed(uid, target, comp))
_status.TrySetStatusEffectDuration(target, comp.Status, comp.Duration);
}

comp.NextCheck = _timing.CurTime + comp.Delay;

Dirty(uid, comp);
}
}

private bool Allowed(EntityUid uid, EntityUid target, RadiusBuffComponent comp)
{
if (_whitelist.IsWhitelistPass(comp.Whitelist, target) && !_whitelist.IsWhitelistPass(comp.Blacklist, target))
return true;

if (comp.BuffSelf && uid == target)
return true;

if (comp.BuffHolder && _hands.IsHolding(target, uid))
return true;

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Content.Shared._Omu.RadiusBuff.Components;
using Content.Shared._Shitmed.Damage;
using Content.Shared._Shitmed.Targeting;
using Content.Shared.Damage;
using Content.Shared.Movement.Systems;
using Content.Shared.StatusEffectNew.Components;
using Robust.Shared.Network;
using Robust.Shared.Timing;

namespace Content.Shared._Omu.RadiusBuff.Systems;

public sealed class ShredderStatusEffectSystem : EntitySystem
{
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly DamageableSystem _dmg = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;

public override void Update(float frameTime)
{
base.Update(frameTime);

if (!_timing.IsFirstTimePredicted)
return;

var query = EntityQueryEnumerator<ShredderStatusEffectComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (comp.NextCheck >= _timing.CurTime)
continue;

if (!TryComp<StatusEffectComponent>(uid, out var statusComp) || statusComp.AppliedTo == null)
continue;

var target = statusComp.AppliedTo.Value;

TryApplyHealing(target, comp);
_movement.RefreshMovementSpeedModifiers(target);

if (!_net.IsClient && comp.Visual != null)
{
var effect = Spawn(comp.Visual, Transform(target).Coordinates);
_transform.SetParent(effect, Transform(effect), target);
}

comp.NextCheck = _timing.CurTime + comp.Delay;

Dirty(uid, comp);
}
}

private bool TryApplyHealing(EntityUid target, ShredderStatusEffectComponent comp)
{
if (comp.HealBuff == null)
return false;

if (!TryComp<DamageableComponent>(target, out var damageable))
return false;

_dmg.TryChangeDamage(target,
comp.HealBuff,
true,
false,
damageable,
targetPart: TargetBodyPart.All,
splitDamage: SplitDamageBehavior.SplitEnsureAll);

return true;
}
}
4 changes: 4 additions & 0 deletions Resources/Audio/_Omu/Weapons/attributions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- files: ["shredderhit.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken from debudding on freesound.org"
source: "https://freesound.org/people/debudding/sounds/44532/"
Binary file added Resources/Audio/_Omu/Weapons/shredderhit.ogg
Binary file not shown.
8 changes: 8 additions & 0 deletions Resources/Locale/en-US/_Omu/store/uplink-catalog.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ uplink-hardsuit-cybersunsciencesuit-desc = A scientific reseach hardsuit meant t
uplink-dislippler-name = Dislippler
uplink-dislippler-desc = Standard-issue security disabler on Honk! Co. stations. Ultimate proof that God has abandoned us.

uplink-shredder-name = Shredder
uplink-shredder-desc = A razor-sharp rock guitar which delivers deadly shocks while providing you with increased speed and healing while wielded. Anyone wearing blood-red headphones nearby will also gain the same boost!

uplink-shredder-bundle-name = Shredder Bundle
uplink-shredder-bundle-desc = Contains a Shredder, and 6 blood-red headphones so everyone can enjoy the benefits of the music.

uplink-bloodred-headphones-name = Blood-red Headphones
uplink-bloodred-headphones-desc = Looks stylish, and synergizes with the Shredder.
11 changes: 11 additions & 0 deletions Resources/Prototypes/_Omu/Catalog/Fills/Backpacks/dufflebag.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- type: entity
parent: ClothingBackpackDuffelSyndicateBundle
id: ClothingBackpackDuffelSyndicateFilledShredder
name: Shredder bundle
description: Contains a Shredder, and headphones for the whole squad.
components:
- type: StorageFill
contents:
- id: WeaponShredder
- id: ClothingMultipleBloodredHeadphones
amount: 6
45 changes: 45 additions & 0 deletions Resources/Prototypes/_Omu/Catalog/uplink_catalog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,48 @@
whitelist:
- Clown

- type: listing
id: UplinkShredder
name: uplink-shredder-name
description: uplink-shredder-desc
icon: { sprite: _Omu/Objects/Weapons/Melee/shredder.rsi, state: icon }
productEntity: WeaponShredder
cost:
Telecrystal: 45
categories:
- UplinkJob
conditions:
- !type:StoreWhitelistCondition
blacklist:
tags:
- NukeOpsUplink
- !type:BuyerJobCondition
whitelist:
- Musician

- type: listing
id: UplinkShredderBundle
name: uplink-shredder-bundle-name
description: uplink-shredder-bundle-desc
icon: { sprite: _Omu/Objects/Weapons/Melee/shredder.rsi, state: icon }
productEntity: ClothingBackpackDuffelSyndicateFilledShredder
cost:
Telecrystal: 75
categories:
- UplinkWeaponry
conditions:
- !type:StoreWhitelistCondition
whitelist:
tags:
- NukeOpsUplink

- type: listing
id: UplinkBloodredHeadphones
name: uplink-bloodred-headphones-name
description: uplink-bloodred-headphones-desc
icon: { sprite: /Textures/_Omu/Clothing/Multiple/bloodred-headphones.rsi, state: icon }
productEntity: ClothingMultipleBloodredHeadphones
cost:
Telecrystal: 3
categories:
- UplinkWearables
Loading
Loading