Player post-spawn hook and player ghosting controlled by preset (#2734)

* GamePreset: Add a hook to catch all player spawns for modification.

Unless someone makes the entire job system unpluggable, this is what you get...

* GamePreset: Is now in control of voluntary ghosting

* Clean up ghost code to only rely on a player's Mind
This commit is contained in:
20kdc
2020-12-11 01:10:55 +00:00
committed by GitHub
parent adc972f9d3
commit 31b39823ad
5 changed files with 107 additions and 56 deletions

View File

@@ -3,7 +3,9 @@ using System.Collections.Generic;
using Content.Server.GameTicking;
using Content.Server.Interfaces.GameTicking;
using Content.Shared.Roles;
using Content.Server.Mobs;
using Robust.Server.Interfaces.Player;
using Robust.Server.Interfaces.Console;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Timing;
@@ -49,6 +51,11 @@ namespace Content.IntegrationTests
{
}
public bool OnGhostAttempt(Mind mind, bool canReturnGlobal)
{
return false;
}
public void MakeObserve(IPlayerSession player)
{
}

View File

@@ -27,71 +27,22 @@ namespace Content.Server.Commands.Observer
{
if (player == null)
{
shell.SendText(player, "Nah");
shell?.SendText(player, "You have no session, you can't ghost.");
return;
}
var mind = player.ContentData()?.Mind;
var mind = player!.ContentData()?.Mind;
if (mind == null)
{
shell.SendText(player, "You can't ghost here!");
shell?.SendText(player, "You have no Mind, you can't ghost.");
return;
}
var playerEntity = player.AttachedEntity;
if (playerEntity != null && playerEntity.HasComponent<GhostComponent>())
if (!IoCManager.Resolve<IGameTicker>().OnGhostAttempt(mind, CanReturn))
{
shell?.SendText(player, "You can't ghost right now.");
return;
if (mind.VisitingEntity != null)
{
mind.UnVisit();
mind.VisitingEntity.Delete();
}
var position = playerEntity?.Transform.Coordinates ?? IoCManager.Resolve<IGameTicker>().GetObserverSpawnPoint();
var canReturn = false;
if (playerEntity != null && CanReturn && playerEntity.TryGetComponent(out IMobStateComponent? mobState))
{
if (mobState.IsDead())
{
canReturn = true;
}
else if (mobState.IsCritical())
{
canReturn = true;
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
{
//todo: what if they dont breathe lol
damageable.ChangeDamage(DamageType.Asphyxiation, 100, true);
}
}
else
{
canReturn = false;
}
}
var entityManager = IoCManager.Resolve<IEntityManager>();
var ghost = entityManager.SpawnEntity("MobObserver", position);
ghost.Name = mind.CharacterName;
var ghostComponent = ghost.GetComponent<GhostComponent>();
ghostComponent.CanReturnToBody = canReturn;
if (playerEntity != null &&
playerEntity.TryGetComponent(out ServerOverlayEffectsComponent? overlayComponent))
{
overlayComponent.RemoveOverlay(SharedOverlayID.CircleMaskOverlay);
}
if (canReturn)
mind.Visit(ghost);
else
mind.TransferTo(ghost);
}
}
}

View File

@@ -1,7 +1,21 @@
#nullable enable annotations
using System.Collections.Generic;
using Content.Shared.Preferences;
using Robust.Server.Interfaces.Player;
using Content.Server.Administration;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Observer;
using Content.Server.Interfaces.GameTicking;
using Content.Server.Players;
using Content.Server.Mobs;
using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Mobs.State;
using Robust.Shared.Network;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.IoC;
namespace Content.Server.GameTicking
{
@@ -18,6 +32,72 @@ namespace Content.Server.GameTicking
public virtual void OnGameStarted() { }
/// <summary>
/// Called when a player is spawned in (this includes, but is not limited to, before Start)
/// </summary>
public virtual void OnSpawnPlayerCompleted(IPlayerSession session, IEntity mob, bool lateJoin) { }
/// <summary>
/// Called when a player attempts to ghost.
/// </summary>
public virtual bool OnGhostAttempt(Mind mind, bool canReturnGlobal)
{
var playerEntity = mind.OwnedEntity;
if (playerEntity != null && playerEntity.HasComponent<GhostComponent>())
return false;
if (mind.VisitingEntity != null)
{
mind.UnVisit();
mind.VisitingEntity.Delete();
}
var position = playerEntity?.Transform.Coordinates ?? IoCManager.Resolve<IGameTicker>().GetObserverSpawnPoint();
var canReturn = false;
if (playerEntity != null && canReturnGlobal && playerEntity.TryGetComponent(out IMobStateComponent? mobState))
{
if (mobState.IsDead())
{
canReturn = true;
}
else if (mobState.IsCritical())
{
canReturn = true;
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
{
//todo: what if they dont breathe lol
damageable.ChangeDamage(DamageType.Asphyxiation, 100, true);
}
}
else
{
canReturn = false;
}
}
var entityManager = IoCManager.Resolve<IEntityManager>();
var ghost = entityManager.SpawnEntity("MobObserver", position);
ghost.Name = mind.CharacterName;
var ghostComponent = ghost.GetComponent<GhostComponent>();
ghostComponent.CanReturnToBody = canReturn;
if (playerEntity != null &&
playerEntity.TryGetComponent(out ServerOverlayEffectsComponent? overlayComponent))
{
overlayComponent.RemoveOverlay(SharedOverlayID.CircleMaskOverlay);
}
if (canReturn)
mind.Visit(ghost);
else
mind.TransferTo(ghost);
return true;
}
public virtual string GetRoundEndDescription() => "";
}
}

View File

@@ -31,6 +31,7 @@ using Robust.Server.Interfaces.Maps;
using Robust.Server.Interfaces.Player;
using Robust.Server.Player;
using Robust.Server.ServerStatus;
using Robust.Server.Interfaces.Console;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Configuration;
@@ -440,6 +441,11 @@ namespace Content.Server.GameTicking
UpdateJobsAvailable();
}
public bool OnGhostAttempt(Mind mind, bool canReturnGlobal)
{
return Preset.OnGhostAttempt(mind, canReturnGlobal);
}
public T AddGameRule<T>() where T : GameRule, new()
{
var instance = _dynamicTypeFactory.CreateInstance<T>();
@@ -887,6 +893,8 @@ namespace Content.Server.GameTicking
AddSpawnedPosition(jobId);
EquipIdCard(mob, character.Name, jobPrototype);
jobPrototype.Special?.AfterEquip(mob);
Preset.OnSpawnPlayerCompleted(session, mob, lateJoin);
}
private void EquipIdCard(IEntity mob, string characterName, JobPrototype jobPrototype)

View File

@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using Content.Server.GameTicking;
using Content.Server.Mobs;
using Content.Shared.Roles;
using Robust.Server.Interfaces.Player;
using Robust.Server.Interfaces.Console;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Timing;
@@ -42,6 +44,9 @@ namespace Content.Server.Interfaces.GameTicking
void ToggleReady(IPlayerSession player, bool ready);
void ToggleDisallowLateJoin(bool disallowLateJoin);
/// <summary>proxy to GamePreset (actual handler)</summary>
bool OnGhostAttempt(Mind mind, bool canReturnGlobal);
EntityCoordinates GetLateJoinSpawnPoint();
EntityCoordinates GetJobSpawnPoint(string jobId);
EntityCoordinates GetObserverSpawnPoint();