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:
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() => "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user