Skip to content

Commit

Permalink
Added Penlights (#567)
Browse files Browse the repository at this point in the history
# Description

Added in penlights that spawn in Medical Staff PDAs.

---

# TODO

- [x] EyeCheck system
- [x] Add in the bloody pens.

---


<details><summary><h1>Media</h1></summary>
<p>


https://github.com/user-attachments/assets/dc746aa2-782e-4d86-b9ef-9e012343fb87


</p>
</details>

---

# Changelog

:cl: Tilkku
- add: Added Pen Lights
- add: Eye Examination

---------

Signed-off-by: SleepyScarecrow <[email protected]>
Co-authored-by: VMSolidus <[email protected]>
  • Loading branch information
SleepyScarecrow and VMSolidus committed Aug 6, 2024
1 parent 48c0770 commit 04d01f6
Show file tree
Hide file tree
Showing 17 changed files with 510 additions and 1 deletion.
47 changes: 47 additions & 0 deletions Content.Client/Eye/PenLight/UI/PenLightBoundUserInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Content.Shared.Medical;
using JetBrains.Annotations;
using Robust.Client.GameObjects;

namespace Content.Client.Eye.PenLight.UI
{
[UsedImplicitly]
public sealed class PenLightBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private PenLightWindow? _window;

public PenLightBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }

protected override void Open()
{
base.Open();
_window = new PenLightWindow
{
Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName,
};
_window.OnClose += Close;
_window.OpenCentered();
}

protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
if (_window == null
|| message is not PenLightUserMessage cast)
return;

_window.Diagnose(cast);
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;

if (_window != null)
_window.OnClose -= Close;

_window?.Dispose();
}
}
}
11 changes: 11 additions & 0 deletions Content.Client/Eye/PenLight/UI/PenLightWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'pen-light-exam-title'}"
SetSize="0 -300">
<ScrollContainer VerticalExpand="True">
<BoxContainer Name="RootContainer" Orientation="Vertical" HorizontalExpand="True">
<Label Name="NoPatientDataText" Text="{Loc 'pen-light-window-no-patient-data-text'}" Visible="False"/>
<Label Name="ExamDataLabel"/>
</BoxContainer>
</ScrollContainer>
</controls:FancyWindow>
78 changes: 78 additions & 0 deletions Content.Client/Eye/PenLight/UI/PenLightWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Damage;
using Content.Shared.IdentityManagement;
using Content.Shared.Medical;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using System.Text;


namespace Content.Client.Eye.PenLight.UI
{
[GenerateTypedNameReferences]
public sealed partial class PenLightWindow : FancyWindow
{
private readonly IEntityManager _entityManager;
private const int LightHeight = 150;
private const int LightWidth = 900;

public PenLightWindow()
{
RobustXamlLoader.Load(this);

var dependencies = IoCManager.Instance!;
_entityManager = dependencies.Resolve<IEntityManager>();
}
public void Diagnose(PenLightUserMessage msg)
{
var target = _entityManager.GetEntity(msg.TargetEntity);

if (target == null || !_entityManager.TryGetComponent<DamageableComponent>(target, out var damageable))
{
NoPatientDataText.Visible = true;
ExamDataLabel.Text = string.Empty;
return;
}

NoPatientDataText.Visible = false;


string entityName = Loc.GetString("pen-light-window-entity-unknown-text");
if (_entityManager.HasComponent<MetaDataComponent>(target.Value))
entityName = Identity.Name(target.Value, _entityManager);

var sb = new StringBuilder();
sb.AppendLine(Loc.GetString("pen-light-window-entity-eyes-text", ("entityName", entityName)));

// Check if Blind and return early if true
if (msg.Blind == true)
{
sb.AppendLine(Loc.GetString("pen-light-exam-blind-text"));
ExamDataLabel.Text = sb.ToString();
SetHeight = LightHeight;
SetWidth = LightWidth;
return;
}
// EyeDamage
if (msg.EyeDamage == true)
sb.AppendLine(Loc.GetString("pen-light-exam-eyedamage-text"));

// Drunk
if (msg.Drunk == true)
sb.AppendLine(Loc.GetString("pen-light-exam-drunk-text"));

// Hallucinating
if (msg.SeeingRainbows == true)
sb.AppendLine(Loc.GetString("pen-light-exam-hallucinating-text"));

// Healthy
if (msg.Healthy == true)
sb.AppendLine(Loc.GetString("pen-light-exam-healthy-text"));

ExamDataLabel.Text = sb.ToString();

SetHeight = LightHeight;
SetWidth = LightWidth;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public sealed class EyeProtectionSystem : EntitySystem
{
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
[Dependency] private readonly BlindableSystem _blindingSystem = default!;

public override void Initialize()
{
base.Initialize();
Expand Down
118 changes: 118 additions & 0 deletions Content.Server/Medical/PenLightSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using Content.Server.DoAfter;
using Content.Server.PowerCell;
using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Content.Shared.Drugs;
using Content.Shared.Drunk;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Interaction;
using Content.Shared.Medical;
using Content.Shared.Mobs.Systems;
using Content.Shared.Traits.Assorted.Components;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
using Robust.Shared.Timing;

namespace Content.Server.Medical;
/// <summary>
/// This stores the eye exam system for <see cref="PenLightComponent"/>
/// </summary>
public sealed class PenLightSystem : EntitySystem
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<PenLightComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<PenLightComponent, PenLightDoAfterEvent>(OnDoAfter);
}

private void OnAfterInteract(EntityUid uid, PenLightComponent component, AfterInteractEvent args)
{
if (args.Handled
|| args.Target is not { } target)
return;

args.Handled = TryStartExam(uid, target, args.User, component);
}

private void OnDoAfter(Entity<PenLightComponent> uid, ref PenLightDoAfterEvent args)
{
if (args.Handled
|| args.Cancelled
|| args.Target == null
|| !_powerCell.HasDrawCharge(uid, user: args.User))
return;

OpenUserInterface(args.User, uid);
Diagnose(uid, args.Target.Value);
args.Handled = true;
}


/// <summary>
/// Actually handles the exam interaction.
/// </summary>
public bool TryStartExam(EntityUid uid, EntityUid target, EntityUid user, PenLightComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;

return _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.ExamSpeed, new PenLightDoAfterEvent(),
uid, target, uid)
{
BlockDuplicate = true,
BreakOnUserMove = true,
BreakOnTargetMove = true,
BreakOnHandChange = true,
NeedHand = true
});
}
private void OpenUserInterface(EntityUid user, EntityUid penlight)
{
if (!TryComp<ActorComponent>(user, out var actor)
|| !_uiSystem.TryGetUi(penlight, PenLightUiKey.Key, out var ui))
return;

_uiSystem.OpenUi(ui, actor.PlayerSession);
}

/// <summary>
/// Runs the checks for the different types of eye damage
/// </summary>
private void Diagnose(EntityUid penlight, EntityUid target)
{
if (!_uiSystem.TryGetUi(penlight, PenLightUiKey.Key, out var ui)
|| !HasComp<EyeComponent>(target))
return;
// Blind
var blind = _entityManager.HasComponent<PermanentBlindnessComponent>(target);

// Drunk
var drunk = _entityManager.HasComponent<DrunkComponent>(target);

// EyeDamage
var eyeDamage = false;
if (TryComp<BlindableComponent>(target, out var eyeDam))
{
eyeDamage = eyeDam.EyeDamage > 0 && eyeDam.EyeDamage < 6; //6 means perma-blind
}

// Hallucinating
var seeingRainbows = _entityManager.HasComponent<SeeingRainbowsComponent>(target);

// Healthy
var healthy = !(blind || drunk || eyeDamage || seeingRainbows);

_uiSystem.SendUiMessage(ui, new PenLightUserMessage(GetNetEntity(target),
blind,
drunk,
eyeDamage,
healthy,
seeingRainbows
));
}
}
33 changes: 33 additions & 0 deletions Content.Shared/Medical/PenLightComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Content.Shared.DoAfter;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Medical;

/// <summary>
/// This for penlights; a tool used to check for eye damage.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
public sealed partial class PenLightComponent : Component
{
/// <summary>
/// Cooldown Time, exams take a bit
/// </summary>
[AutoPausedField]
public TimeSpan? NextExamTime;

/// <summary>
/// The min time between exams
/// </summary>
[DataField]
public TimeSpan ExamDelay = TimeSpan.FromSeconds(3);

/// <summary>
/// How long the doafter for the exam takes
/// </summary>
[DataField(required: true)]
public float ExamSpeed { get; set; }

}

[Serializable, NetSerializable]
public sealed partial class PenLightDoAfterEvent : SimpleDoAfterEvent { }
9 changes: 9 additions & 0 deletions Content.Shared/Medical/PenLightUiKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Robust.Shared.Serialization;

namespace Content.Shared.Medical;

[Serializable, NetSerializable]
public enum PenLightUiKey : byte
{
Key
}
24 changes: 24 additions & 0 deletions Content.Shared/Medical/PenLightUserMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Robust.Shared.Serialization;

namespace Content.Shared.Medical;
[Serializable, NetSerializable]
public sealed class PenLightUserMessage : BoundUserInterfaceMessage
{
public readonly NetEntity? TargetEntity;
public bool? Blind;
public bool? Drunk;
public bool? EyeDamage;
public bool? Healthy;
public bool? SeeingRainbows;

public PenLightUserMessage(NetEntity? targetEntity, bool? blind, bool? drunk, bool? eyeDamage, bool? healthy, bool? seeingRainbows)
{
TargetEntity = targetEntity;
Blind = blind;
Drunk = drunk;
EyeDamage = eyeDamage;
Healthy = healthy;
SeeingRainbows = seeingRainbows;
}
}

11 changes: 11 additions & 0 deletions Resources/Locale/en-US/medical/components/penlight.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
penlight-off = The pen light is off.
pen-light-exam-title = Pen Light
pen-light-window-entity-eyes-text = {$entityName}'s conditions:
pen-light-window-no-patient-data-text = No patient data.
pen-light-window-entity-unknown-text = unknown
pen-light-exam-blind-text = The patient's eyes are glassy and unfocused. They can't follow the light at all.
pen-light-exam-drunk-text = The patient's eyes are slow to follow the light, droopy.
pen-light-exam-eyedamage-text = The patient's eyes are partially focused, though they struggle to look at the light for too long.
pen-light-exam-hallucinating-text = The patient's eyes are wandering around, with dilated pupils. They don't focus on the light.
pen-light-exam-healthy-text = The patient follows the light perfectly with no stuttering.
Loading

0 comments on commit 04d01f6

Please sign in to comment.