Added medical scanner (#338)

* Added medical scanner

* RegisterIgnore
This commit is contained in:
DamianX
2019-09-18 20:24:55 +02:00
committed by Pieter-Jan Briers
parent 415ac8fa46
commit 364279e0f7
21 changed files with 433 additions and 1 deletions

View File

@@ -108,6 +108,7 @@ namespace Content.Client
"AccessReader", "AccessReader",
"IdCardConsole", "IdCardConsole",
"Airlock", "Airlock",
"MedicalScanner",
}; };
foreach (var ignoreName in registerIgnore) foreach (var ignoreName in registerIgnore)

View File

@@ -0,0 +1,33 @@
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Shared.GameObjects.Components.UserInterface;
using static Content.Shared.GameObjects.Components.Medical.SharedMedicalScannerComponent;
namespace Content.Client.GameObjects.Components.MedicalScanner
{
public class MedicalScannerBoundUserInterface : BoundUserInterface
{
public MedicalScannerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
private MedicalScannerWindow _window;
protected override void Open()
{
base.Open();
_window = new MedicalScannerWindow
{
Title = Owner.Owner.Name,
Size = (485, 90),
};
_window.OnClose += Close;
_window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
_window.Populate((MedicalScannerBoundUserInterfaceState) state);
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using static Content.Shared.GameObjects.Components.Medical.SharedMedicalScannerComponent;
using static Content.Shared.GameObjects.Components.Medical.SharedMedicalScannerComponent.MedicalScannerStatus;
namespace Content.Client.GameObjects.Components.MedicalScanner
{
public class MedicalScannerVisualizer2D : AppearanceVisualizer
{
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (!component.TryGetData(MedicalScannerVisuals.Status, out MedicalScannerStatus status)) return;
sprite.LayerSetState(MedicalScannerVisualLayers.Machine, StatusToMachineStateId(status));
sprite.LayerSetState(MedicalScannerVisualLayers.Terminal, StatusToTerminalStateId(status));
}
private string StatusToMachineStateId(MedicalScannerStatus status)
{
switch (status)
{
case Off: return "scanner_off";
case Open: return "scanner_open";
case Red: return "scanner_red";
case Death: return "scanner_death";
case Green: return "scanner_green";
case Yellow: return "scanner_yellow";
default:
throw new ArgumentOutOfRangeException(nameof(status), status, "unknown MedicalScannerStatus");
}
}
private string StatusToTerminalStateId(MedicalScannerStatus status)
{
switch (status)
{
case Off: return "scanner_terminal_off";
case Open: return "scanner_terminal_blue";
case Red: return "scanner_terminal_red";
case Death: return "scanner_terminal_dead";
case Green: return "scanner_terminal_green";
case Yellow: return "scanner_terminal_blue";
default:
throw new ArgumentOutOfRangeException(nameof(status), status, "unknown MedicalScannerStatus");
}
}
public enum MedicalScannerVisualLayers
{
Machine,
Terminal,
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Text;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Utility;
using static Content.Shared.GameObjects.Components.Medical.SharedMedicalScannerComponent;
namespace Content.Client.GameObjects.Components.MedicalScanner
{
public class MedicalScannerWindow : SS14Window
{
public MedicalScannerWindow()
{
}
public void Populate(MedicalScannerBoundUserInterfaceState state)
{
Contents.RemoveAllChildren();
var text = new StringBuilder();
if (state.MaxHealth == 0)
{
text.Append("No patient data.");
} else
{
text.Append($"Patient's health: {state.CurrentHealth}/{state.MaxHealth}\n");
if (state.DamageDictionary != null)
{
foreach (var (dmgType, amount) in state.DamageDictionary)
{
text.Append($"\n{dmgType}: {amount}");
}
}
}
Contents.AddChild(new Label(){Text = text.ToString()});
}
}
}

View File

@@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Medical;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Medical
{
[RegisterComponent]
[ComponentReference(typeof(IActivate))]
public class MedicalScannerComponent : SharedMedicalScannerComponent, IActivate
{
private AppearanceComponent _appearance;
private BoundUserInterface _userInterface;
private ContainerSlot _bodyContainer;
public bool IsOccupied => _bodyContainer.ContainedEntity != null;
public override void Initialize()
{
base.Initialize();
_appearance = Owner.GetComponent<AppearanceComponent>();
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
.GetBoundUserInterface(MedicalScannerUiKey.Key);
_bodyContainer = ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-bodyContainer", Owner);
UpdateUserInterface();
}
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
new MedicalScannerBoundUserInterfaceState(
0,
0,
null);
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
{
var body = _bodyContainer.ContainedEntity;
if (body == null)
{
_appearance.SetData(MedicalScannerVisuals.Status, MedicalScannerStatus.Open);
return EmptyUIState;
}
var damageable = body.GetComponent<DamageableComponent>();
var species = body.GetComponent<SpeciesComponent>();
var deathThreshold =
species.DamageTemplate.DamageThresholds.FirstOrNull(x => x.ThresholdType == ThresholdType.Death);
if (!deathThreshold.HasValue)
{
return EmptyUIState;
}
var deathThresholdValue = deathThreshold.Value.Value;
var currentHealth = damageable.CurrentDamage[DamageType.Total];
var dmgDict = new Dictionary<string, int>();
foreach (var dmgType in (DamageType[])Enum.GetValues(typeof(DamageType)))
{
if (damageable.CurrentDamage.TryGetValue(dmgType, out var amount))
{
dmgDict[dmgType.ToString()] = amount;
}
}
return new MedicalScannerBoundUserInterfaceState(
deathThresholdValue-currentHealth,
deathThresholdValue,
dmgDict);
}
private void UpdateUserInterface()
{
var newState = GetUserInterfaceState();
_userInterface.SetState(newState);
}
private MedicalScannerStatus GetStatusFromDamageState(DamageState damageState)
{
switch (damageState)
{
case NormalState _: return MedicalScannerStatus.Green;
case CriticalState _: return MedicalScannerStatus.Red;
case DeadState _: return MedicalScannerStatus.Death;
default: throw new ArgumentException(nameof(damageState));
}
}
private MedicalScannerStatus GetStatus()
{
var body = _bodyContainer.ContainedEntity;
return body == null
? MedicalScannerStatus.Open
: GetStatusFromDamageState(body.GetComponent<SpeciesComponent>().CurrentDamageState);
}
private void UpdateAppearance()
{
_appearance.SetData(MedicalScannerVisuals.Status, GetStatus());
}
public void Activate(ActivateEventArgs args)
{
if (!args.User.TryGetComponent(out IActorComponent actor))
{
return;
}
_userInterface.Open(actor.playerSession);
}
[Verb]
public sealed class EnterVerb : Verb<MedicalScannerComponent>
{
protected override string GetText(IEntity user, MedicalScannerComponent component)
{
return "Enter";
}
protected override VerbVisibility GetVisibility(IEntity user, MedicalScannerComponent component)
{
return component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible;
}
protected override void Activate(IEntity user, MedicalScannerComponent component)
{
component.InsertBody(user);
}
}
[Verb]
public sealed class EjectVerb : Verb<MedicalScannerComponent>
{
protected override string GetText(IEntity user, MedicalScannerComponent component)
{
return "Eject";
}
protected override VerbVisibility GetVisibility(IEntity user, MedicalScannerComponent component)
{
return component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
}
protected override void Activate(IEntity user, MedicalScannerComponent component)
{
component.EjectBody();
}
}
public void InsertBody(IEntity user)
{
_bodyContainer.Insert(user);
UpdateUserInterface();
UpdateAppearance();
}
public void EjectBody()
{
_bodyContainer.Remove(_bodyContainer.ContainedEntity);
UpdateUserInterface();
UpdateAppearance();
}
public void Update(float frameTime)
{
if (_bodyContainer.ContainedEntity == null)
{
// There's no need to update if there's no one inside
return;
}
UpdateUserInterface();
UpdateAppearance();
}
}
}

View File

@@ -28,7 +28,7 @@ namespace Content.Server.GameObjects
/// <summary> /// <summary>
/// Holds the damage template which controls the threshold and resistance settings for this species type /// Holds the damage template which controls the threshold and resistance settings for this species type
/// </summary> /// </summary>
private DamageTemplates DamageTemplate; public DamageTemplates DamageTemplate { get; private set; }
/// <summary> /// <summary>
/// Variable for serialization /// Variable for serialization

View File

@@ -0,0 +1,23 @@
using Content.Server.GameObjects.Components.Medical;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
public class MedicalScannerSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(MedicalScannerComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
{
var comp = entity.GetComponent<MedicalScannerComponent>();
comp.Update(frameTime);
}
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Medical
{
public class SharedMedicalScannerComponent : Component
{
public override string Name => "MedicalScanner";
[Serializable, NetSerializable]
public class MedicalScannerBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly int CurrentHealth;
public readonly int MaxHealth;
public readonly Dictionary<string, int> DamageDictionary;
public MedicalScannerBoundUserInterfaceState(
int currentHealth,
int maxHealth,
Dictionary<string, int> damageDictionary)
{
CurrentHealth = currentHealth;
MaxHealth = maxHealth;
DamageDictionary = damageDictionary;
}
}
[Serializable, NetSerializable]
public enum MedicalScannerUiKey
{
Key
}
[Serializable, NetSerializable]
public enum MedicalScannerVisuals
{
Status
}
[Serializable, NetSerializable]
public enum MedicalScannerStatus
{
Off,
Open,
Red,
Death,
Green,
Yellow,
}
}
}

View File

@@ -0,0 +1,43 @@
- type: entity
id: medical_scanner
name: Medical Scanner
description: A bulky medical scanner.
components:
- type: Sprite
netsync: false
sprite: Buildings/medical_scanner.rsi
layers:
- state: scanner_open
map: ["enum.MedicalScannerVisualLayers.Machine"]
- state: scanner_terminal_blue
map: ["enum.MedicalScannerVisualLayers.Terminal"]
- type: Icon
sprite: Buildings/medical_scanner.rsi
state: scanner_open
- type: Clickable
- type: Collidable
shapes:
- !type:PhysShapeAabb
bounds: "-0.5,-0.25,0.5,0.25"
mask: 19
layer: 16
IsScrapingFloor: true
- type: Physics
mass: 25
Anchored: true
- type: SnapGrid
offset: Center
- type: MedicalScanner
- type: Damageable
- type: Destructible
thresholdvalue: 100
- type: Appearance
visuals:
- type: MedicalScannerVisualizer2D
- type: PowerDevice
- type: UserInterface
interfaces:
- key: enum.MedicalScannerUiKey.Key
type: MedicalScannerBoundUserInterface

View File

@@ -0,0 +1 @@
{"version": 1, "size": {"x": 64, "y": 32}, "states": [{"name": "scanner_death", "directions": 1, "delays": [[0.3, 0.3, 0.3, 0.3]]}, {"name": "scanner_green", "directions": 1, "delays": [[0.3, 0.3, 0.3, 0.3, 0.3, 0.3]]}, {"name": "scanner_off", "directions": 1, "delays": [[1.0]]}, {"name": "scanner_open", "directions": 1, "delays": [[1.0]]}, {"name": "scanner_red", "directions": 1, "delays": [[0.3, 0.3, 0.3, 0.3, 0.3, 0.3]]}, {"name": "scanner_terminal_blue", "directions": 1, "delays": [[0.3, 0.3, 0.3, 0.3]]}, {"name": "scanner_terminal_dead", "directions": 1, "delays": [[0.2, 0.2, 0.2, 0.2]]}, {"name": "scanner_terminal_green", "directions": 1, "delays": [[0.3, 0.3, 0.3, 0.3]]}, {"name": "scanner_terminal_off", "directions": 1, "delays": [[1.0]]}, {"name": "scanner_terminal_red", "directions": 1, "delays": [[0.3, 0.3, 0.3, 0.3]]}, {"name": "scanner_yellow", "directions": 1, "delays": [[0.3, 0.3, 0.3, 0.3, 0.3, 0.3]]}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB