Skip to content
This repository has been archived by the owner on Nov 1, 2024. It is now read-only.

Commit

Permalink
Merge branch 'new-frontiers-14:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Vonsant authored Apr 4, 2024
2 parents fde4df4 + 072a54d commit c20cd5a
Show file tree
Hide file tree
Showing 821 changed files with 71,246 additions and 33,863 deletions.
5 changes: 5 additions & 0 deletions Content.Client/DeltaV/Harpy/HarpyVisualsComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Content.Client.DeltaV.Harpy;

[RegisterComponent]
public sealed partial class HarpyVisualsComponent : Component
{ }
44 changes: 44 additions & 0 deletions Content.Client/DeltaV/Overlays/UltraVisionOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Content.Shared.DeltaV.Abilities;

namespace Content.Client.DeltaV.Overlays;

public sealed partial class UltraVisionOverlay : Overlay
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] IEntityManager _entityManager = default!;


public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly ShaderInstance _ultraVisionShader;

public UltraVisionOverlay()
{
IoCManager.InjectDependencies(this);
_ultraVisionShader = _prototypeManager.Index<ShaderPrototype>("UltraVision").Instance().Duplicate();
}

protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;
if (_playerManager.LocalPlayer?.ControlledEntity is not {Valid: true} player)
return;
if (!_entityManager.HasComponent<UltraVisionComponent>(player))
return;

_ultraVisionShader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);


var worldHandle = args.WorldHandle;
var viewport = args.WorldBounds;
worldHandle.SetTransform(Matrix3.Identity);
worldHandle.UseShader(_ultraVisionShader);
worldHandle.DrawRect(viewport, Color.White);
}
}
31 changes: 31 additions & 0 deletions Content.Client/DeltaV/Overlays/UltraVisionSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Content.Shared.DeltaV.Abilities;
using Robust.Client.Graphics;

namespace Content.Client.DeltaV.Overlays;

public sealed partial class UltraVisionSystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;

private UltraVisionOverlay _overlay = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<UltraVisionComponent, ComponentInit>(OnUltraVisionInit);
SubscribeLocalEvent<UltraVisionComponent, ComponentShutdown>(OnUltraVisionShutdown);

_overlay = new();
}

private void OnUltraVisionInit(EntityUid uid, UltraVisionComponent component, ComponentInit args)
{
_overlayMan.AddOverlay(_overlay);
}

private void OnUltraVisionShutdown(EntityUid uid, UltraVisionComponent component, ComponentShutdown args)
{
_overlayMan.RemoveOverlay(_overlay);
}
}
36 changes: 36 additions & 0 deletions Content.Server/Chemistry/ReagentEffects/DisintegrateArtifact.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Content.Server.Xenoarchaeology.XenoArtifacts;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;

namespace Content.Server.Chemistry.ReagentEffects;

public sealed partial class DisintegrateArtifact : ReagentEffect
{

/// <summary>
/// Disintegrate chance
/// </summary>
[DataField("probabilityMin"), ViewVariables(VVAccess.ReadWrite)]
public float ProbabilityMax = 0.05f;

/// <summary>
/// Disintegrate chance
/// </summary>
[DataField("probabilityMax"), ViewVariables(VVAccess.ReadWrite)]
public float ProbabilityMin = 0.15f;

/// <summary>
/// The range around the artifact that it will spawn the entity
/// </summary>
[DataField("range")]
public float Range = 0.5f;

public override void Effect(ReagentEffectArgs args)
{
var artifact = args.EntityManager.EntitySysManager.GetEntitySystem<ArtifactSystem>();
artifact.DisintegrateArtifact(args.SolutionEntity, ProbabilityMin, ProbabilityMax, Range);
}

protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
null;
}
180 changes: 180 additions & 0 deletions Content.Server/DeltaV/Harpy/HarpySingerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
using Content.Server.Instruments;
using Content.Server.Speech.Components;
using Content.Server.UserInterface;
using Content.Shared.Instruments;
using Content.Shared.ActionBlocker;
using Content.Shared.Damage;
using Content.Shared.Damage.ForceSay;
using Content.Shared.DeltaV.Harpy;
using Content.Shared.FixedPoint;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Mobs;
using Content.Shared.Popups;
using Content.Shared.StatusEffect;
using Content.Shared.Stunnable;
using Content.Shared.UserInterface;
using Content.Shared.Zombies;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Content.Shared.DeltaV.Harpy.Components;

namespace Content.Server.DeltaV.Harpy
{
public sealed class HarpySingerSystem : EntitySystem
{
[Dependency] private readonly InstrumentSystem _instrument = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<InstrumentComponent, MobStateChangedEvent>(OnMobStateChangedEvent);
SubscribeLocalEvent<GotEquippedEvent>(OnEquip);
SubscribeLocalEvent<EntityZombifiedEvent>(OnZombified);
SubscribeLocalEvent<InstrumentComponent, KnockedDownEvent>(OnKnockedDown);
SubscribeLocalEvent<InstrumentComponent, StunnedEvent>(OnStunned);
SubscribeLocalEvent<InstrumentComponent, SleepStateChangedEvent>(OnSleep);
SubscribeLocalEvent<InstrumentComponent, StatusEffectAddedEvent>(OnStatusEffect);
SubscribeLocalEvent<InstrumentComponent, DamageChangedEvent>(OnDamageChanged);
SubscribeLocalEvent<HarpySingerComponent, BoundUIClosedEvent>(OnBoundUIClosed);
SubscribeLocalEvent<HarpySingerComponent, BoundUIOpenedEvent>(OnBoundUIOpened);

// This is intended to intercept the UI event and stop the MIDI UI from opening if the
// singer is unable to sing. Thus it needs to run before the ActivatableUISystem.
SubscribeLocalEvent<HarpySingerComponent, OpenUiActionEvent>(OnInstrumentOpen, before: new[] { typeof(ActivatableUISystem) });
}

private void OnEquip(GotEquippedEvent args)
{
// Check if an item that makes the singer mumble is equipped to their face
// (not their pockets!). As of writing, this should just be the muzzle.
if (TryComp<AddAccentClothingComponent>(args.Equipment, out var accent) &&
accent.ReplacementPrototype == "mumble" &&
args.Slot == "mask")
{
CloseMidiUi(args.Equipee);
}
}

private void OnMobStateChangedEvent(EntityUid uid, InstrumentComponent component, MobStateChangedEvent args)
{
if (args.NewMobState is MobState.Critical or MobState.Dead)
CloseMidiUi(args.Target);
}

private void OnZombified(ref EntityZombifiedEvent args)
{
CloseMidiUi(args.Target);
}

private void OnKnockedDown(EntityUid uid, InstrumentComponent component, ref KnockedDownEvent args)
{
CloseMidiUi(uid);
}

private void OnStunned(EntityUid uid, InstrumentComponent component, ref StunnedEvent args)
{
CloseMidiUi(uid);
}

private void OnSleep(EntityUid uid, InstrumentComponent component, ref SleepStateChangedEvent args)
{
if (args.FellAsleep)
CloseMidiUi(uid);
}

private void OnStatusEffect(EntityUid uid, InstrumentComponent component, StatusEffectAddedEvent args)
{
if (args.Key == "Muted")
CloseMidiUi(uid);
}

/// <summary>
/// Almost a copy of Content.Server.Damage.ForceSay.DamageForceSaySystem.OnDamageChanged.
/// Done so because DamageForceSaySystem doesn't output an event, and my understanding is
/// that we don't want to change upstream code more than necessary to avoid merge conflicts
/// and maintenance overhead. It still reuses the values from DamageForceSayComponent, so
/// any tweaks to that will keep ForceSay consistent with singing interruptions.
/// </summary>
private void OnDamageChanged(EntityUid uid, InstrumentComponent instrumentComponent, DamageChangedEvent args)
{
if (!TryComp<DamageForceSayComponent>(uid, out var component) ||
args.DamageDelta == null ||
!args.DamageIncreased ||
args.DamageDelta.GetTotal() < component.DamageThreshold ||
component.ValidDamageGroups == null)
return;

var totalApplicableDamage = FixedPoint2.Zero;
foreach (var (group, value) in args.DamageDelta.GetDamagePerGroup(_prototype))
{
if (!component.ValidDamageGroups.Contains(group))
continue;

totalApplicableDamage += value;
}

if (totalApplicableDamage >= component.DamageThreshold)
CloseMidiUi(uid);
}

/// <summary>
/// Closes the MIDI UI if it is open.
/// </summary>
private void CloseMidiUi(EntityUid uid)
{
if (HasComp<ActiveInstrumentComponent>(uid) &&
TryComp<ActorComponent>(uid, out var actor))
{
_instrument.ToggleInstrumentUi(uid, actor.PlayerSession);
}
}

/// <summary>
/// Prevent the player from opening the MIDI UI under some circumstances.
/// </summary>
private void OnInstrumentOpen(EntityUid uid, HarpySingerComponent component, OpenUiActionEvent args)
{
// CanSpeak covers all reasons you can't talk, including being incapacitated
// (crit/dead), asleep, or for any reason mute inclding glimmer or a mime's vow.
var canNotSpeak = !_blocker.CanSpeak(uid);
var zombified = TryComp<ZombieComponent>(uid, out var _);
var muzzled = _inventorySystem.TryGetSlotEntity(uid, "mask", out var maskUid) &&
TryComp<AddAccentClothingComponent>(maskUid, out var accent) &&
accent.ReplacementPrototype == "mumble";

// Set this event as handled when the singer should be incapable of singing in order
// to stop the ActivatableUISystem event from opening the MIDI UI.
args.Handled = canNotSpeak || muzzled || zombified;

// Tell the user that they can not sing.
if (args.Handled)
_popupSystem.PopupEntity(Loc.GetString("no-sing-while-no-speak"), uid, uid, PopupType.Medium);
}

private void OnBoundUIClosed(EntityUid uid, HarpySingerComponent component, BoundUIClosedEvent args)
{
if (args.UiKey is not InstrumentUiKey)
return;

TryComp(uid, out AppearanceComponent? appearance);
_appearance.SetData(uid, HarpyVisualLayers.Singing, SingingVisualLayer.False, appearance);
}

private void OnBoundUIOpened(EntityUid uid, HarpySingerComponent component, BoundUIOpenedEvent args)
{
if (args.UiKey is not InstrumentUiKey)
return;

TryComp(uid, out AppearanceComponent? appearance);
_appearance.SetData(uid, HarpyVisualLayers.Singing, SingingVisualLayer.True, appearance);

}
}
}
Loading

0 comments on commit c20cd5a

Please sign in to comment.