Skip to content

Commit

Permalink
Ton of work refactoring Holograms, generally cleaning it up, and addi…
Browse files Browse the repository at this point in the history
…ng new functionality.

The AIEye can now function as a camera-bound Hologram (commented out), work still needs to be done on making Hologram disks and servers cool to use, and I need to decide how to determine what a Hologram collides with.
  • Loading branch information
Pspritechologist committed Feb 6, 2024
1 parent 15995ca commit 4718be2
Show file tree
Hide file tree
Showing 26 changed files with 628 additions and 412 deletions.
44 changes: 44 additions & 0 deletions Content.Client/SimpleStation14/Holograms/AcceptHologramEui.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Content.Client.Eui;
using Content.Client.Holograms;
using Content.Shared.SimpleStation14.Holograms;
using JetBrains.Annotations;
using Robust.Client.Graphics;

namespace Content.Client.SimpleStation14.Holograms;

[UsedImplicitly]
public sealed class AcceptHologramEui : BaseEui
{
private readonly AcceptHologramWindow _window;

public AcceptHologramEui()
{
_window = new AcceptHologramWindow();

_window.DenyButton.OnPressed += _ =>
{
SendMessage(new AcceptHologramChoiceMessage(AcceptHologramUiButton.Deny));
_window.Close();
};

_window.OnClose += () => SendMessage(new AcceptHologramChoiceMessage(AcceptHologramUiButton.Deny));

_window.AcceptButton.OnPressed += _ =>
{
SendMessage(new AcceptHologramChoiceMessage(AcceptHologramUiButton.Accept));
_window.Close();
};
}

public override void Opened()
{
IoCManager.Resolve<IClyde>().RequestWindowAttention();
_window.OpenCentered();
}

public override void Closed()
{
_window.Close();
}

}
60 changes: 60 additions & 0 deletions Content.Client/SimpleStation14/Holograms/AcceptHologramWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Numerics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using static Robust.Client.UserInterface.Controls.BoxContainer;

namespace Content.Client.Holograms;

public sealed class AcceptHologramWindow : DefaultWindow
{
public readonly Button DenyButton;
public readonly Button AcceptButton;

public AcceptHologramWindow()
{

Title = Loc.GetString("accept-hologram-window-title");

Contents.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =
{
new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =
{
new Label()
{
Text = Loc.GetString("accept-hologram-window-prompt-text-part")
},
new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Align = AlignMode.Center,
Children =
{
(AcceptButton = new Button
{
Text = Loc.GetString("accept-hologram-window-accept-button"),
}),

new Control()
{
MinSize = new Vector2(20, 0)
},

(DenyButton = new Button
{
Text = Loc.GetString("accept-hologram-window-deny-button"),
})
}
},
}
},
}
});
}
}
60 changes: 44 additions & 16 deletions Content.Client/SimpleStation14/Holograms/HologramSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,43 @@ namespace Content.Client.SimpleStation14.Holograms;
public sealed class HologramSystem : SharedHologramSystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly TransformSystem _transform = default!;

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

SubscribeLocalEvent<HologramProjectedComponent, ComponentShutdown>(OnHoloProjectedShutdown);
SubscribeLocalEvent<HologramProjectedComponent, ComponentShutdown>(OnProjectedShutdown);
}

public override void Update(float frameTime)
{
var player = _player.LocalPlayer?.ControlledEntity; // This makes it so only the currently controlled entity is predicted, assuming they're a hologram.
var player = _player.LocalPlayer?.ControlledEntity;
if (TryComp<HologramProjectedComponent>(player, out var holoProjComp))
ProjectedUpdate(player.Value, holoProjComp, frameTime);
{
ProjectedUpdate(player.Value, holoProjComp, frameTime); // This makes it so only the currently controlled entity is predicted, assuming they're a hologram.

// Check if we should be setting the eye target of the hologram.
if (holoProjComp.SetEyeTarget && TryComp<EyeComponent>(player.Value, out var eyeComp))
eyeComp.Target = holoProjComp.CurProjector;
}

HandleProjectedEffects(EntityQueryEnumerator<HologramProjectedComponent>());
}

private void OnProjectedShutdown(EntityUid hologram, HologramProjectedComponent component, ComponentShutdown args)
{
DeleteEffect(component);

if (component.SetEyeTarget && TryComp<EyeComponent>(hologram, out var eyeComp))
eyeComp.Target = null; // This should be fine? I guess if you're a hologram riding a vehicle when this happens it'd be a bit weird.
}

private void HandleProjectedEffects(EntityQueryEnumerator<HologramProjectedComponent> query)
{
while (query.MoveNext(out var hologram, out var holoProjectedComp))
{
if (!holoProjectedComp.DoProjectionEffect)
if (holoProjectedComp.EffectPrototype == null)
{
DeleteEffect(holoProjectedComp);
continue;
Expand All @@ -58,12 +72,30 @@ private void HandleProjectedEffects(EntityQueryEnumerator<HologramProjectedCompo
continue;
}

var originPos = projCoords.Position;

// Add the effect's offset, if applicable.
if (TryComp<HologramProjectorComponent>(projector, out var projComp))
{
var direction = projXformComp.LocalRotation.GetCardinalDir();

var offset = direction switch
{
Direction.North => projComp.EffectOffsets[Direction.South],
Direction.South => projComp.EffectOffsets[Direction.North],
Direction.East => projComp.EffectOffsets[Direction.West],
Direction.West => projComp.EffectOffsets[Direction.East],
_ => Vector2.Zero
};

originPos += offset;
}

// Determine a middle point between the hologram and the projector.
var effectPos = (holoCoords.Position + projCoords.Position) / 2;
// Offset the position a quarter tile towards the projector.
effectPos += (projCoords.Position - holoCoords.Position).Normalized() * 0.25f;
var effectPos = (holoCoords.Position + originPos) / 2;

// Determine a rotation that points from the projector to the hologram.
var effectRot = (holoCoords.Position - projCoords.Position).ToAngle() + -MathHelper.PiOver2;
var effectRot = (holoCoords.Position - originPos).ToAngle() - MathHelper.PiOver2;

var effectCoords = new EntityCoordinates(holoCoords.EntityId, effectPos);
if (!effectCoords.IsValid(EntityManager))
Expand All @@ -81,16 +113,12 @@ private void HandleProjectedEffects(EntityQueryEnumerator<HologramProjectedCompo
_transform.SetLocalRotation(holoProjectedComp.EffectEntity.Value, effectRot);

// Determine the scaling factor to make it fit between the hologram and the projector.
var effectScale = new Vector2(1, (holoCoords.Position - projCoords.Position).Length());
Comp<SpriteComponent>(holoProjectedComp.EffectEntity.Value).Scale = effectScale.Y > 0.1f ? effectScale : Vector2.One;
var yScale = (holoCoords.Position - originPos).Length();
var effectScale = new Vector2(1, Math.Max(0.1f, yScale)); // No smaller than 0.1.
Comp<SpriteComponent>(holoProjectedComp.EffectEntity.Value).Scale = effectScale;
}
}

private void OnHoloProjectedShutdown(EntityUid uid, HologramProjectedComponent component, ComponentShutdown args)
{
DeleteEffect(component);
}

private void DeleteEffect(HologramProjectedComponent component)
{
if (component.EffectEntity != null && Exists(component.EffectEntity.Value))
Expand Down
32 changes: 32 additions & 0 deletions Content.Server/SimpleStation14/Holograms/AcceptHologramEui.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Content.Server.EUI;
using Content.Shared.Eui;
using Content.Shared.SimpleStation14.Holograms;

namespace Content.Server.SimpleStation14.Holograms;

public sealed class AcceptHologramEui : BaseEui
{
private readonly HologramSystem _hologramSystem;
private readonly Mind.Mind _mind;

public AcceptHologramEui(Mind.Mind mind, HologramSystem hologramSys)
{
_mind = mind;
_hologramSystem = hologramSys;
}

public override void HandleMessage(EuiMessageBase msg)
{
base.HandleMessage(msg);

if (msg is not AcceptHologramChoiceMessage choice ||
choice.Button == AcceptHologramUiButton.Deny)
{
Close();
return;
}

_hologramSystem.TransferMindToHologram(_mind);
Close();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// using static Content.Server.SimpleStation14.Hologram.HologramSystem;

namespace Content.Server.SimpleStation14.Holograms;

/// <summary>
/// Marks this entity as storing a hologram's data in it, for use in a <see cref="HologramServerComponent"/>.
/// </summary>
[RegisterComponent]
public sealed class HologramDiskComponent : Component
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

namespace Content.Server.SimpleStation14.Holograms.Components;

/// <summary>
/// For any items that should generate a 'dummy' hologram when inserted as a holo disk.
/// Mostly intended for jokes and gaffs, but could be used for useful AI entities as well.
/// </summary>
[RegisterComponent]
public sealed class HologramDiskDummyComponent : Component
{
/// <summary>
/// The prototype to spawn when this disk is inserted into a server.
/// </summary>
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>)), ViewVariables(VVAccess.ReadWrite)]
public string HoloPrototype = default!;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.SurveillanceCamera;
using Content.Shared.SimpleStation14.Holograms;
using Content.Shared.SimpleStation14.Holograms.Components;

namespace Content.Server.SimpleStation14.Holograms;

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

SubscribeLocalEvent((EntityUid ent, HologramProjectorComponent comp, ref PowerChangedEvent _) => CheckState(ent, comp));
SubscribeLocalEvent<HologramProjectorComponent, SurveillanceCameraChangeStateEvent>((ent, comp, args) => CheckState(ent, comp));
SubscribeLocalEvent<HologramProjectorComponent, MapInitEvent>((ent, comp, args) => CheckState(ent, comp));
}

public void CheckState(EntityUid projector, HologramProjectorComponent? projComp = null)
{
if (!Resolve(projector, ref projComp))
return;

if (TryComp<ApcPowerReceiverComponent>(projector, out var powerComp) && !powerComp.Powered ||
TryComp<SurveillanceCameraComponent>(projector, out var cameraComp) && !cameraComp.Active)
{
RemComp<HologramProjectorActiveComponent>(projector);
return;
}

EnsureComp<HologramProjectorActiveComponent>(projector);
}
}
Loading

0 comments on commit 4718be2

Please sign in to comment.