Merge branch 'master' into DecimalReagents

This commit is contained in:
PrPleGoo
2020-04-09 17:24:22 +02:00
23 changed files with 415 additions and 24 deletions

View File

@@ -288,7 +288,7 @@ namespace Content.Client.Chat
WriteChatMessage(storedMessage);
// Local messages that have an entity attached get a speech bubble.
if (msg.Channel == ChatChannel.Local && msg.SenderEntity != default)
if ((msg.Channel == ChatChannel.Local || msg.Channel == ChatChannel.Dead) && msg.SenderEntity != default)
{
AddSpeechBubble(msg);
}

View File

@@ -0,0 +1,99 @@
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Observer;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Observer
{
[RegisterComponent]
public class GhostComponent : SharedGhostComponent
{
private GhostGui _gui;
[ViewVariables(VVAccess.ReadOnly)]
public bool CanReturnToBody { get; private set; } = true;
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private IComponentManager _componentManager;
#pragma warning restore 649
public override void OnRemove()
{
base.OnRemove();
_gui?.Dispose();
}
private void SetGhostVisibility(bool visibility)
{
// So, for now this is a client-side hack... Please, PLEASE someone make this work server-side.
foreach (var ghost in _componentManager.GetAllComponents(typeof(GhostComponent)))
{
if (ghost.Owner.TryGetComponent(out SpriteComponent component))
component.Visible = visibility;
}
}
public override void Initialize()
{
base.Initialize();
if (Owner.TryGetComponent(out SpriteComponent component))
component.Visible = _playerManager.LocalPlayer.ControlledEntity?.HasComponent<GhostComponent>() ?? false;
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case PlayerAttachedMsg _:
if (_gui == null)
{
_gui = new GhostGui(this);
}
else
{
_gui.Orphan();
}
_gameHud.HandsContainer.AddChild(_gui);
SetGhostVisibility(true);
break;
case PlayerDetachedMsg _:
_gui.Parent?.RemoveChild(_gui);
SetGhostVisibility(false);
break;
}
}
public void SendReturnToBodyMessage() => SendNetworkMessage(new ReturnToBodyComponentMessage());
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is GhostComponentState state)) return;
CanReturnToBody = state.CanReturnToBody;
if (Owner == _playerManager.LocalPlayer.ControlledEntity)
{
_gui?.Update();
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Data;
using Content.Client.GameObjects.Components.Observer;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.IoC;
namespace Content.Client.UserInterface
{
public class GhostGui : Control
{
public Button ReturnToBody = new Button(){Text = "Return to body"};
private GhostComponent _owner;
public GhostGui(GhostComponent owner)
{
IoCManager.InjectDependencies(this);
_owner = owner;
MouseFilter = MouseFilterMode.Ignore;
ReturnToBody.OnPressed += (args) => { owner.SendReturnToBodyMessage(); };
AddChild(ReturnToBody);
Update();
}
public void Update()
{
ReturnToBody.Disabled = !_owner.CanReturnToBody;
}
}
}

View File

@@ -4,6 +4,7 @@ using Content.Server.GameTicking;
using Content.Server.Interfaces.GameTicking;
using Content.Shared;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Map;
using Robust.Shared.Timing;
namespace Content.IntegrationTests
@@ -54,6 +55,10 @@ namespace Content.IntegrationTests
{
}
public GridCoordinates GetLateJoinSpawnPoint() => GridCoordinates.InvalidGrid;
public GridCoordinates GetJobSpawnPoint(string jobId) => GridCoordinates.InvalidGrid;
public GridCoordinates GetObserverSpawnPoint() => GridCoordinates.InvalidGrid;
public T AddGameRule<T>() where T : GameRule, new()
{
return new T();

View File

@@ -1,4 +1,5 @@
using Content.Server.Players;
using Content.Server.GameObjects.Components.Observer;
using Content.Server.Players;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.GameObjects;
@@ -30,10 +31,14 @@ namespace Content.Server.Administration
}
else
{
var canReturn = mind.CurrentEntity != null && !mind.CurrentEntity.HasComponent<GhostComponent>();
var entityManager = IoCManager.Resolve<IEntityManager>();
var ghost = entityManager.SpawnEntity("AdminObserver", player.AttachedEntity.Transform.GridPosition);
mind.Visit(ghost);
if(canReturn)
mind.Visit(ghost);
else
mind.TransferTo(ghost);
ghost.GetComponent<GhostComponent>().CanReturnToBody = canReturn;
}
}
}

View File

@@ -1,4 +1,6 @@
using Content.Server.Interfaces.Chat;
using Content.Server.GameObjects.Components.Observer;
using Content.Server.Interfaces.Chat;
using Content.Server.Observer;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Enums;
@@ -24,7 +26,10 @@ namespace Content.Server.Chat
var message = string.Join(" ", args);
chat.EntitySay(player.AttachedEntity, message);
if (player.AttachedEntity.HasComponent<GhostComponent>())
chat.SendDeadChat(player, message);
else
chat.EntitySay(player.AttachedEntity, message);
}
}

View File

@@ -1,12 +1,17 @@
using System.Linq;
using Content.Server.GameObjects.Components.Observer;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Interfaces.Chat;
using Content.Server.Observer;
using Content.Server.Players;
using Content.Shared.Chat;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
namespace Content.Server.Chat
{
@@ -20,6 +25,7 @@ namespace Content.Server.Chat
#pragma warning disable 649
[Dependency] private readonly IServerNetManager _netManager;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly ILocalizationManager _localizationManager;
[Dependency] private readonly IMoMMILink _mommiLink;
#pragma warning restore 649
@@ -93,6 +99,18 @@ namespace Content.Server.Chat
_mommiLink.SendOOCMessage(player.SessionId.ToString(), message);
}
public void SendDeadChat(IPlayerSession player, string message)
{
var clients = _playerManager.GetPlayersBy(x => x.AttachedEntity != null && x.AttachedEntity.HasComponent<GhostComponent>()).Select(p => p.ConnectedClient);;
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
msg.Channel = ChatChannel.Dead;
msg.Message = message;
msg.MessageWrap = $"{_localizationManager.GetString("DEAD")}: {player.AttachedEntity.Name}: {{0}}";
msg.SenderEntity = player.AttachedEntityUid.GetValueOrDefault();
_netManager.ServerSendToMany(msg, clients.ToList());
}
public void SendHookOOC(string sender, string message)
{
var msg = _netManager.CreateNetMessage<MsgChatMessage>();

View File

@@ -75,7 +75,13 @@ namespace Content.Server.GameObjects
{
if (damageType == DamageType.Total)
{
throw new ArgumentException("Cannot take damage for DamageType.Total");
foreach (DamageType e in Enum.GetValues(typeof(DamageType)))
{
if (e == damageType) continue;
TakeDamage(e, amount, source, sourceMob);
}
return;
}
InitializeDamageType(damageType);

View File

@@ -38,5 +38,6 @@ namespace Content.Server.GameObjects.Components.Markers
Unset = 0,
LateJoin,
Job,
Observer,
}
}

View File

@@ -0,0 +1,66 @@
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Players;
using Content.Shared.GameObjects.Components.Observer;
using Robust.Server.GameObjects;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.ViewVariables;
using Timer = Robust.Shared.Timers.Timer;
namespace Content.Server.GameObjects.Components.Observer
{
[RegisterComponent]
public class GhostComponent : SharedGhostComponent, IActionBlocker
{
private bool _canReturnToBody = true;
[ViewVariables(VVAccess.ReadWrite)]
public bool CanReturnToBody
{
get => _canReturnToBody;
set
{
_canReturnToBody = value;
Dirty();
}
}
public override ComponentState GetComponentState() => new GhostComponentState(CanReturnToBody);
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case ReturnToBodyComponentMessage reenter:
if (!Owner.TryGetComponent(out IActorComponent actor) || !CanReturnToBody) break;
if (netChannel == null || netChannel == actor.playerSession.ConnectedClient)
{
actor.playerSession.ContentData().Mind.UnVisit();
}
break;
case PlayerAttachedMsg _:
Dirty();
break;
case PlayerDetachedMsg _:
Timer.Spawn(100, Owner.Delete);
break;
default:
break;
}
}
public bool CanInteract() => false;
public bool CanUse() => false;
public bool CanThrow() => false;
public bool CanDrop() => false;
public bool CanPickup() => false;
public bool CanEmote() => false;
public bool CanAttack() => false;
}
}

View File

@@ -5,23 +5,23 @@ namespace Content.Server.GameObjects.EntitySystems
{
public interface IActionBlocker
{
bool CanMove();
bool CanMove() => true;
bool CanInteract();
bool CanInteract() => true;
bool CanUse();
bool CanUse() => true;
bool CanThrow();
bool CanThrow() => true;
bool CanSpeak();
bool CanSpeak() => true;
bool CanDrop();
bool CanDrop() => true;
bool CanPickup();
bool CanPickup() => true;
bool CanEmote();
bool CanEmote() => true;
bool CanAttack();
bool CanAttack() => true;
}
public class ActionBlockerSystem : EntitySystem

View File

@@ -3,12 +3,14 @@ using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Movement;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.Interfaces.GameObjects.Components.Movement;
using Content.Server.Observer;
using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.Maps;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Server.Interfaces.Timing;
using Robust.Shared.Configuration;
@@ -25,6 +27,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Players;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
@@ -138,6 +141,7 @@ namespace Content.Server.GameObjects.EntitySystems
{
if (physics.LinearVelocity != Vector2.Zero)
physics.LinearVelocity = Vector2.Zero;
}
else
{
@@ -185,6 +189,11 @@ namespace Content.Server.GameObjects.EntitySystems
if (!TryGetAttachedComponent(session as IPlayerSession, out IMoverComponent moverComp))
return;
var owner = (session as IPlayerSession)?.AttachedEntity;
if (owner != null && owner.TryGetComponent(out SpeciesComponent species) && species.CurrentDamageState is DeadState)
new Ghost().Execute(null, (IPlayerSession)session, null);
moverComp.SetVelocityDirection(dir, state);
}

View File

@@ -273,7 +273,7 @@ namespace Content.Server.GameTicking
private IEntity _spawnPlayerMob(Job job, bool lateJoin = true)
{
GridCoordinates coordinates = lateJoin ? _getLateJoinSpawnPoint() : _getJobSpawnPoint(job.Prototype.ID);
GridCoordinates coordinates = lateJoin ? GetLateJoinSpawnPoint() : GetJobSpawnPoint(job.Prototype.ID);
var entity = _entityManager.SpawnEntity(PlayerPrototypeName, coordinates);
if (entity.TryGetComponent(out InventoryComponent inventory))
{
@@ -299,11 +299,11 @@ namespace Content.Server.GameTicking
private IEntity _spawnObserverMob()
{
GridCoordinates coordinates = _getLateJoinSpawnPoint();
var coordinates = GetObserverSpawnPoint();
return _entityManager.SpawnEntity(ObserverPrototypeName, coordinates);
}
private GridCoordinates _getLateJoinSpawnPoint()
public GridCoordinates GetLateJoinSpawnPoint()
{
var location = _spawnPoint;
@@ -319,7 +319,7 @@ namespace Content.Server.GameTicking
return location;
}
private GridCoordinates _getJobSpawnPoint(string jobId)
public GridCoordinates GetJobSpawnPoint(string jobId)
{
var location = _spawnPoint;
@@ -336,6 +336,23 @@ namespace Content.Server.GameTicking
return location;
}
public GridCoordinates GetObserverSpawnPoint()
{
var location = _spawnPoint;
var possiblePoints = new List<GridCoordinates>();
foreach (var entity in _entityManager.GetEntities(new TypeEntityQuery(typeof(SpawnPointComponent))))
{
var point = entity.GetComponent<SpawnPointComponent>();
if (point.SpawnType == SpawnPointType.Observer)
possiblePoints.Add(entity.Transform.GridPosition);
}
if (possiblePoints.Count != 0) location = _robustRandom.Pick(possiblePoints);
return location;
}
/// <summary>
/// Cleanup that has to run to clear up anything from the previous round.
/// Stuff like wiping the previous map clean.

View File

@@ -18,6 +18,7 @@ namespace Content.Server.Interfaces.Chat
void EntityMe(IEntity source, string action);
void SendOOC(IPlayerSession player, string message);
void SendDeadChat(IPlayerSession player, string message);
void SendHookOOC(string sender, string message);
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Content.Server.GameTicking;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Map;
using Robust.Shared.Timing;
namespace Content.Server.Interfaces.GameTicking
@@ -27,6 +28,10 @@ namespace Content.Server.Interfaces.GameTicking
void MakeJoinGame(IPlayerSession player);
void ToggleReady(IPlayerSession player, bool ready);
GridCoordinates GetLateJoinSpawnPoint();
GridCoordinates GetJobSpawnPoint(string jobId);
GridCoordinates GetObserverSpawnPoint();
// GameRule system.
T AddGameRule<T>() where T : GameRule, new();
void RemoveGameRule(GameRule rule);

View File

@@ -0,0 +1,74 @@
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.Observer;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.GameTicking;
using Content.Server.Players;
using Content.Shared.GameObjects;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
namespace Content.Server.Observer
{
public class Ghost : IClientCommand
{
public string Command => "ghost";
public string Description => "Give up on life and become a ghost.";
public string Help => "ghost";
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
{
if (player == null)
{
shell.SendText((IPlayerSession) null, "Nah");
return;
}
var mind = player.ContentData().Mind;
var canReturn = player.AttachedEntity != null;
var name = player.AttachedEntity?.Name ?? player.Name;
if (player.AttachedEntity != null && player.AttachedEntity.HasComponent<GhostComponent>())
return;
if (mind.VisitingEntity != null)
{
mind.UnVisit();
}
var position = player.AttachedEntity?.Transform.GridPosition ?? IoCManager.Resolve<IGameTicker>().GetObserverSpawnPoint();
if (canReturn && player.AttachedEntity.TryGetComponent(out SpeciesComponent species))
{
switch (species.CurrentDamageState)
{
case DeadState _:
canReturn = true;
break;
case CriticalState _:
canReturn = true;
if (!player.AttachedEntity.TryGetComponent(out DamageableComponent damageable)) break;
damageable.TakeDamage(DamageType.Total, 100); // TODO: Use airloss/oxyloss instead
break;
default:
canReturn = false;
break;
}
}
var entityManager = IoCManager.Resolve<IEntityManager>();
var ghost = entityManager.SpawnEntity("MobObserver", position);
ghost.Name = name;
var ghostComponent = ghost.GetComponent<GhostComponent>();
ghostComponent.CanReturnToBody = canReturn;
if(canReturn)
mind.Visit(ghost);
else
mind.TransferTo(ghost);
}
}
}

View File

@@ -6,7 +6,7 @@ namespace Content.Shared.Chat
/// Represents chat channels that the player can filter chat tabs by.
/// </summary>
[Flags]
public enum ChatChannel : byte
public enum ChatChannel : short
{
None = 0,
@@ -46,9 +46,14 @@ namespace Content.Shared.Chat
/// </summary>
Emotes = 64,
/// <summary>
/// Deadchat
/// </summary>
Dead = 128,
/// <summary>
/// Unspecified.
/// </summary>
Unspecified = 128,
Unspecified = 256,
}
}

View File

@@ -35,7 +35,7 @@ namespace Content.Shared.Chat
/// <summary>
/// The sending entity.
/// Only applies to <see cref="ChatChannel.Local"/> and <see cref="ChatChannel.Emotes"/>.
/// Only applies to <see cref="ChatChannel.Local"/>, <see cref="ChatChannel.Dead"/> and <see cref="ChatChannel.Emotes"/>.
/// </summary>
public EntityUid SenderEntity { get; set; }
@@ -48,6 +48,7 @@ namespace Content.Shared.Chat
switch (Channel)
{
case ChatChannel.Local:
case ChatChannel.Dead:
case ChatChannel.Emotes:
SenderEntity = buffer.ReadEntityUid();
break;
@@ -63,6 +64,7 @@ namespace Content.Shared.Chat
switch (Channel)
{
case ChatChannel.Local:
case ChatChannel.Dead:
case ChatChannel.Emotes:
buffer.Write(SenderEntity);
break;

View File

@@ -0,0 +1,29 @@
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Observer
{
public class SharedGhostComponent : Component
{
public override string Name => "Ghost";
public override uint? NetID => ContentNetIDs.GHOST;
}
[Serializable, NetSerializable]
public class GhostComponentState : ComponentState
{
public bool CanReturnToBody { get; }
public GhostComponentState(bool canReturnToBody) : base(ContentNetIDs.GHOST)
{
CanReturnToBody = canReturnToBody;
}
}
[Serializable, NetSerializable]
public class ReturnToBodyComponentMessage : ComponentMessage
{
public ReturnToBodyComponentMessage() => Directed = true;
}
}

View File

@@ -41,5 +41,6 @@
public const uint HANDHELD_LIGHT = 1036;
public const uint PAPER = 1037;
public const uint REAGENT_INJECTOR = 1038;
public const uint GHOST = 1039;
}
}

View File

@@ -11,6 +11,7 @@
- ooc
- observe
- toggleready
- ghost
- Index: 50
Name: Moderator
@@ -26,6 +27,7 @@
- showtime
- observe
- toggleready
- ghost
- kick
- listplayers
- loc
@@ -44,6 +46,7 @@
- aghost
- observe
- toggleready
- ghost
- spawn
- delete
- tp
@@ -84,6 +87,7 @@
- aghost
- observe
- toggleready
- ghost
- spawn
- delete
- tp

View File

@@ -15,3 +15,8 @@
- type: Examiner
DoRangeCheck: false
- type: IgnorePause
- type: Ghost
- type: Sprite
netsync: false
drawdepth: Mobs
texture: Mob/observer.png