2021-06-20 10:09:24 +02:00
using System ;
using System.Diagnostics.CodeAnalysis ;
using Content.Server.GameTicking.Presets ;
2021-12-21 21:23:29 +01:00
using Content.Server.GameTicking.Rules ;
using Content.Server.Ghost.Components ;
2021-06-20 10:09:24 +02:00
using Content.Shared.CCVar ;
2021-12-21 21:23:29 +01:00
using Content.Shared.Damage ;
using Content.Shared.Damage.Prototypes ;
using Content.Shared.MobState.Components ;
using Robust.Shared.GameObjects ;
2021-06-20 10:09:24 +02:00
using Robust.Shared.IoC ;
namespace Content.Server.GameTicking
{
public partial class GameTicker
{
public const float PresetFailedCooldownIncrease = 30f ;
2021-12-21 21:23:29 +01:00
private GamePresetPrototype ? _preset ;
2021-06-20 10:09:24 +02:00
2021-12-21 21:23:29 +01:00
private void InitializeGamePreset ( )
2021-06-20 10:09:24 +02:00
{
2021-12-21 21:23:29 +01:00
SetGamePreset ( _configurationManager . GetCVar ( CCVars . GameLobbyDefaultPreset ) ) ;
2021-06-20 10:09:24 +02:00
}
2021-12-21 21:23:29 +01:00
public void SetGamePreset ( GamePresetPrototype preset , bool force = false )
2021-12-21 18:56:47 +01:00
{
2021-12-21 21:23:29 +01:00
// Do nothing if this game ticker is a dummy!
if ( DummyTicker )
return ;
2021-06-20 10:09:24 +02:00
2021-12-21 21:23:29 +01:00
_preset = preset ;
UpdateInfoText ( ) ;
if ( force )
2021-12-21 18:56:47 +01:00
{
2021-12-21 21:23:29 +01:00
StartRound ( true ) ;
}
}
2021-12-21 19:25:52 +01:00
2021-12-21 21:23:29 +01:00
public void SetGamePreset ( string preset , bool force = false )
{
var proto = FindGamePreset ( preset ) ;
if ( proto ! = null )
SetGamePreset ( proto , force ) ;
}
public GamePresetPrototype ? FindGamePreset ( string preset )
{
if ( _prototypeManager . TryIndex ( preset , out GamePresetPrototype ? presetProto ) )
return presetProto ;
2021-12-21 19:25:52 +01:00
2021-12-21 21:23:29 +01:00
foreach ( var proto in _prototypeManager . EnumeratePrototypes < GamePresetPrototype > ( ) )
{
foreach ( var alias in proto . Alias )
2021-06-20 10:09:24 +02:00
{
2021-12-21 21:23:29 +01:00
if ( preset . Equals ( alias , StringComparison . InvariantCultureIgnoreCase ) )
return proto ;
2021-06-20 10:09:24 +02:00
}
}
2021-12-21 21:23:29 +01:00
return null ;
2021-06-20 10:09:24 +02:00
}
2021-12-21 21:23:29 +01:00
public bool TryFindGamePreset ( string preset , [ NotNullWhen ( true ) ] out GamePresetPrototype ? prototype )
2021-06-20 10:09:24 +02:00
{
2021-12-21 21:23:29 +01:00
prototype = FindGamePreset ( preset ) ;
return prototype ! = null ;
2021-06-20 10:09:24 +02:00
}
2021-12-21 21:23:29 +01:00
private bool AddGamePresetRules ( )
2021-06-20 10:09:24 +02:00
{
2021-12-21 21:23:29 +01:00
if ( DummyTicker | | _preset = = null )
return false ;
foreach ( var rule in _preset . Rules )
{
if ( ! _prototypeManager . TryIndex ( rule , out GameRulePrototype ? ruleProto ) )
continue ;
AddGameRule ( ruleProto ) ;
}
return true ;
2021-06-20 10:09:24 +02:00
}
2022-02-15 20:06:28 -07:00
private void StartGamePresetRules ( )
{
foreach ( var rule in _addedGameRules )
{
StartGameRule ( rule ) ;
}
}
2021-12-21 21:23:29 +01:00
public bool OnGhostAttempt ( Mind . Mind mind , bool canReturnGlobal )
2021-06-20 10:09:24 +02:00
{
2021-12-21 21:23:29 +01:00
var handleEv = new GhostAttemptHandleEvent ( mind , canReturnGlobal ) ;
RaiseLocalEvent ( handleEv ) ;
2021-06-20 10:09:24 +02:00
2021-12-21 21:23:29 +01:00
// Something else has handled the ghost attempt for us! We return its result.
if ( handleEv . Handled )
return handleEv . Result ;
2021-12-21 18:56:47 +01:00
2021-12-21 21:23:29 +01:00
var playerEntity = mind . OwnedEntity ;
2021-06-20 10:09:24 +02:00
2021-12-21 21:23:29 +01:00
var entities = IoCManager . Resolve < IEntityManager > ( ) ;
if ( entities . HasComponent < GhostComponent > ( playerEntity ) )
return false ;
if ( mind . VisitingEntity ! = default )
2021-12-21 18:56:47 +01:00
{
2021-12-21 21:23:29 +01:00
mind . UnVisit ( ) ;
2021-12-21 18:56:47 +01:00
}
2021-12-21 21:23:29 +01:00
var position = playerEntity is { Valid : true }
? Transform ( playerEntity . Value ) . Coordinates
: GetObserverSpawnPoint ( ) ;
// Ok, so, this is the master place for the logic for if ghosting is "too cheaty" to allow returning.
// There's no reason at this time to move it to any other place, especially given that the 'side effects required' situations would also have to be moved.
// + If CharacterDeadPhysically applies, we're physically dead. Therefore, ghosting OK, and we can return (this is critical for gibbing)
// Note that we could theoretically be ICly dead and still physically alive and vice versa.
// (For example, a zombie could be dead ICly, but may retain memories and is definitely physically active)
// + If we're in a mob that is critical, and we're supposed to be able to return if possible,
// we're succumbing - the mob is killed. Therefore, character is dead. Ghosting OK.
// (If the mob survives, that's a bug. Ghosting is kept regardless.)
var canReturn = canReturnGlobal & & mind . CharacterDeadPhysically ;
if ( canReturnGlobal & & TryComp ( playerEntity , out MobStateComponent ? mobState ) )
{
if ( mobState . IsCritical ( ) )
{
canReturn = true ;
//todo: what if they dont breathe lol
//cry deeply
DamageSpecifier damage = new ( _prototypeManager . Index < DamageTypePrototype > ( "Asphyxiation" ) , 200 ) ;
_damageable . TryChangeDamage ( playerEntity , damage , true ) ;
}
}
var ghost = Spawn ( "MobObserver" , position . ToMap ( entities ) ) ;
// Try setting the ghost entity name to either the character name or the player name.
// If all else fails, it'll default to the default entity prototype name, "observer".
// However, that should rarely happen.
var meta = MetaData ( ghost ) ;
if ( ! string . IsNullOrWhiteSpace ( mind . CharacterName ) )
meta . EntityName = mind . CharacterName ;
else if ( ! string . IsNullOrWhiteSpace ( mind . Session ? . Name ) )
meta . EntityName = mind . Session . Name ;
var ghostComponent = Comp < GhostComponent > ( ghost ) ;
if ( mind . TimeOfDeath . HasValue )
2021-06-20 10:09:24 +02:00
{
2021-12-21 21:23:29 +01:00
ghostComponent . TimeOfDeath = mind . TimeOfDeath ! . Value ;
2021-06-20 10:09:24 +02:00
}
2021-12-21 21:23:29 +01:00
_ghosts . SetCanReturnToBody ( ghostComponent , canReturn ) ;
if ( canReturn )
mind . Visit ( ghost ) ;
else
mind . TransferTo ( ghost ) ;
return true ;
2021-06-20 10:09:24 +02:00
}
2021-12-21 21:23:29 +01:00
}
public class GhostAttemptHandleEvent : HandledEntityEventArgs
{
public Mind . Mind Mind { get ; }
public bool CanReturnGlobal { get ; }
public bool Result { get ; set ; }
2021-06-20 10:09:24 +02:00
2021-12-21 21:23:29 +01:00
public GhostAttemptHandleEvent ( Mind . Mind mind , bool canReturnGlobal )
2021-06-20 10:09:24 +02:00
{
2021-12-21 21:23:29 +01:00
Mind = mind ;
CanReturnGlobal = canReturnGlobal ;
2021-06-20 10:09:24 +02:00
}
}
}