2021-06-20 10:09:24 +02:00
using System ;
using System.Collections.Generic ;
using System.Globalization ;
2021-11-26 03:02:46 -06:00
using System.Linq ;
2021-06-20 10:09:24 +02:00
using Content.Server.Access.Components ;
2021-10-22 05:31:07 +03:00
using Content.Server.Access.Systems ;
2021-11-15 18:14:34 +00:00
using Content.Server.Ghost ;
2021-06-20 10:09:24 +02:00
using Content.Server.Ghost.Components ;
using Content.Server.Hands.Components ;
using Content.Server.Inventory.Components ;
using Content.Server.Items ;
using Content.Server.PDA ;
using Content.Server.Players ;
using Content.Server.Roles ;
using Content.Server.Spawners.Components ;
using Content.Server.Speech.Components ;
2021-11-26 03:02:46 -06:00
using Content.Server.Station ;
using Content.Shared.Administration.Logs ;
2021-10-16 15:28:02 -07:00
using Content.Shared.CharacterAppearance.Systems ;
2021-11-28 14:56:53 +01:00
using Content.Shared.Database ;
2021-06-20 10:09:24 +02:00
using Content.Shared.GameTicking ;
2021-08-06 00:02:36 -07:00
using Content.Shared.Ghost ;
2021-06-20 10:09:24 +02:00
using Content.Shared.Inventory ;
using Content.Shared.Preferences ;
using Content.Shared.Roles ;
2021-11-26 03:02:46 -06:00
using Content.Shared.Station ;
2021-06-20 10:09:24 +02:00
using Robust.Server.Player ;
using Robust.Shared.GameObjects ;
2021-10-22 05:31:07 +03:00
using Robust.Shared.IoC ;
2021-06-20 10:09:24 +02:00
using Robust.Shared.Localization ;
using Robust.Shared.Map ;
using Robust.Shared.Random ;
using Robust.Shared.Utility ;
using Robust.Shared.ViewVariables ;
2021-11-26 03:02:46 -06:00
using static Content . Server . Station . StationSystem ;
2021-06-20 10:09:24 +02:00
namespace Content.Server.GameTicking
{
public partial class GameTicker
{
2021-10-07 11:59:59 -07:00
private const string PlayerPrototypeName = "MobHuman" ;
2021-06-20 10:09:24 +02:00
private const string ObserverPrototypeName = "MobObserver" ;
2021-10-22 05:31:07 +03:00
[Dependency] private readonly IdCardSystem _cardSystem = default ! ;
2021-11-26 03:02:46 -06:00
/// <summary>
/// Can't yet be removed because every test ever seems to depend on it. I'll make removing this a different PR.
/// </summary>
2021-06-20 10:09:24 +02:00
[ViewVariables(VVAccess.ReadWrite)]
private EntityCoordinates _spawnPoint ;
// Mainly to avoid allocations.
private readonly List < EntityCoordinates > _possiblePositions = new ( ) ;
2021-11-26 03:02:46 -06:00
private void SpawnPlayer ( IPlayerSession player , StationId station , string? jobId = null , bool lateJoin = true )
2021-06-20 10:09:24 +02:00
{
var character = GetPlayerProfile ( player ) ;
2021-11-26 03:02:46 -06:00
SpawnPlayer ( player , character , station , jobId , lateJoin ) ;
2021-06-20 10:09:24 +02:00
UpdateJobsAvailable ( ) ;
}
2021-11-26 03:02:46 -06:00
private void SpawnPlayer ( IPlayerSession player , HumanoidCharacterProfile character , StationId station , string? jobId = null , bool lateJoin = true )
2021-06-20 10:09:24 +02:00
{
2021-11-26 03:02:46 -06:00
if ( station = = StationId . Invalid )
{
var stations = _stationSystem . StationInfo . Keys . ToList ( ) ;
_robustRandom . Shuffle ( stations ) ;
if ( stations . Count = = 0 )
station = StationId . Invalid ;
else
station = stations [ 0 ] ;
}
2021-06-20 10:09:24 +02:00
// Can't spawn players with a dummy ticker!
if ( DummyTicker )
return ;
if ( lateJoin & & DisallowLateJoin )
{
MakeObserve ( player ) ;
return ;
}
2021-11-26 16:51:00 -06:00
// Pick best job best on prefs.
jobId ? ? = PickBestAvailableJob ( character , station ) ;
// If no job available, just bail out.
if ( jobId is null )
{
_chatManager . DispatchServerMessage ( player , Loc . GetString ( "game-ticker-player-no-jobs-available-when-joining" ) ) ;
return ;
}
2021-06-20 10:09:24 +02:00
PlayerJoinGame ( player ) ;
var data = player . ContentData ( ) ;
DebugTools . AssertNotNull ( data ) ;
data ! . WipeMind ( ) ;
2021-11-15 18:14:34 +00:00
var newMind = new Mind . Mind ( data . UserId )
2021-06-20 10:09:24 +02:00
{
CharacterName = character . Name
} ;
2021-11-15 18:14:34 +00:00
newMind . ChangeOwningPlayer ( data . UserId ) ;
2021-06-20 10:09:24 +02:00
var jobPrototype = _prototypeManager . Index < JobPrototype > ( jobId ) ;
2021-11-15 18:14:34 +00:00
var job = new Job ( newMind , jobPrototype ) ;
newMind . AddRole ( job ) ;
2021-06-20 10:09:24 +02:00
if ( lateJoin )
{
_chatManager . DispatchStationAnnouncement ( Loc . GetString (
"latejoin-arrival-announcement" ,
( "character" , character . Name ) ,
( "job" , CultureInfo . CurrentCulture . TextInfo . ToTitleCase ( job . Name ) )
2021-11-22 22:34:48 +00:00
) , Loc . GetString ( "latejoin-arrival-sender" ) ,
playDefaultSound : false ) ;
2021-06-20 10:09:24 +02:00
}
2021-11-26 03:02:46 -06:00
var mob = SpawnPlayerMob ( job , character , station , lateJoin ) ;
2021-12-03 15:53:09 +01:00
newMind . TransferTo ( mob ) ;
2021-06-20 10:09:24 +02:00
if ( player . UserId = = new Guid ( "{e887eb93-f503-4b65-95b6-2f282c014192}" ) )
{
2021-12-03 11:33:40 +01:00
IoCManager . Resolve < IEntityManager > ( ) . AddComponent < OwOAccentComponent > ( mob ) ;
2021-06-20 10:09:24 +02:00
}
AddManifestEntry ( character . Name , jobId ) ;
AddSpawnedPosition ( jobId ) ;
EquipIdCard ( mob , character . Name , jobPrototype ) ;
2021-09-16 15:17:19 +02:00
foreach ( var jobSpecial in jobPrototype . Special )
{
jobSpecial . AfterEquip ( mob ) ;
}
2021-06-20 10:09:24 +02:00
2021-11-27 00:43:43 -06:00
_stationSystem . TryAssignJobToStation ( station , jobPrototype ) ;
2021-11-26 03:02:46 -06:00
if ( lateJoin )
_adminLogSystem . Add ( LogType . LateJoin , LogImpact . Medium , $"Player {player.Name} late joined as {character.Name:characterName} on station {_stationSystem.StationInfo[station].Name:stationName} with {mob} as a {job.Name:jobName}." ) ;
else
_adminLogSystem . Add ( LogType . RoundStartJoin , LogImpact . Medium , $"Player {player.Name} joined as {character.Name:characterName} on station {_stationSystem.StationInfo[station].Name:stationName} with {mob} as a {job.Name:jobName}." ) ;
2021-06-20 10:09:24 +02:00
Preset ? . OnSpawnPlayerCompleted ( player , mob , lateJoin ) ;
}
public void Respawn ( IPlayerSession player )
{
player . ContentData ( ) ? . WipeMind ( ) ;
2021-11-26 03:02:46 -06:00
_adminLogSystem . Add ( LogType . Respawn , LogImpact . Medium , $"Player {player} was respawned." ) ;
2021-06-20 10:09:24 +02:00
if ( LobbyEnabled )
PlayerJoinLobby ( player ) ;
else
2021-11-26 03:02:46 -06:00
SpawnPlayer ( player , StationId . Invalid ) ;
2021-06-20 10:09:24 +02:00
}
2021-11-26 03:02:46 -06:00
public void MakeJoinGame ( IPlayerSession player , StationId station , string? jobId = null )
2021-06-20 10:09:24 +02:00
{
if ( ! _playersInLobby . ContainsKey ( player ) ) return ;
if ( ! _prefsManager . HavePreferencesLoaded ( player ) )
{
return ;
}
2021-11-26 03:02:46 -06:00
SpawnPlayer ( player , station , jobId ) ;
2021-06-20 10:09:24 +02:00
}
public void MakeObserve ( IPlayerSession player )
{
// Can't spawn players with a dummy ticker!
if ( DummyTicker )
return ;
if ( ! _playersInLobby . ContainsKey ( player ) ) return ;
PlayerJoinGame ( player ) ;
var name = GetPlayerProfile ( player ) . Name ;
var data = player . ContentData ( ) ;
DebugTools . AssertNotNull ( data ) ;
data ! . WipeMind ( ) ;
2021-11-15 18:14:34 +00:00
var newMind = new Mind . Mind ( data . UserId ) ;
newMind . ChangeOwningPlayer ( data . UserId ) ;
newMind . AddRole ( new ObserverRole ( newMind ) ) ;
2021-06-20 10:09:24 +02:00
var mob = SpawnObserverMob ( ) ;
2021-12-03 15:53:09 +01:00
IoCManager . Resolve < IEntityManager > ( ) . GetComponent < MetaDataComponent > ( mob ) . EntityName = name ;
var ghost = IoCManager . Resolve < IEntityManager > ( ) . GetComponent < GhostComponent > ( mob ) ;
2021-08-06 00:02:36 -07:00
EntitySystem . Get < SharedGhostSystem > ( ) . SetCanReturnToBody ( ghost , false ) ;
2021-12-03 15:53:09 +01:00
newMind . TransferTo ( mob ) ;
2021-06-20 10:09:24 +02:00
_playersInLobby [ player ] = LobbyPlayerStatus . Observer ;
RaiseNetworkEvent ( GetStatusSingle ( player , LobbyPlayerStatus . Observer ) ) ;
}
#region Mob Spawning Helpers
2021-12-04 12:35:33 +01:00
private EntityUid SpawnPlayerMob ( Job job , HumanoidCharacterProfile ? profile , StationId station , bool lateJoin = true )
2021-06-20 10:09:24 +02:00
{
2021-11-26 03:02:46 -06:00
var coordinates = lateJoin ? GetLateJoinSpawnPoint ( station ) : GetJobSpawnPoint ( job . Prototype . ID , station ) ;
2021-08-04 09:25:30 +02:00
var entity = EntityManager . SpawnEntity ( PlayerPrototypeName , coordinates ) ;
2021-06-20 10:09:24 +02:00
if ( job . StartingGear ! = null )
{
var startingGear = _prototypeManager . Index < StartingGearPrototype > ( job . StartingGear ) ;
EquipStartingGear ( entity , startingGear , profile ) ;
}
if ( profile ! = null )
{
2021-12-04 12:35:33 +01:00
_humanoidAppearanceSystem . UpdateFromProfile ( entity , profile ) ;
EntityManager . GetComponent < MetaDataComponent > ( entity ) . EntityName = profile . Name ;
2021-06-20 10:09:24 +02:00
}
return entity ;
}
2021-12-04 12:35:33 +01:00
private EntityUid SpawnObserverMob ( )
2021-06-20 10:09:24 +02:00
{
var coordinates = GetObserverSpawnPoint ( ) ;
2021-08-04 09:25:30 +02:00
return EntityManager . SpawnEntity ( ObserverPrototypeName , coordinates ) ;
2021-06-20 10:09:24 +02:00
}
#endregion
#region Equip Helpers
2021-12-04 12:35:33 +01:00
public void EquipStartingGear ( EntityUid entity , StartingGearPrototype startingGear , HumanoidCharacterProfile ? profile )
2021-06-20 10:09:24 +02:00
{
2021-12-03 15:53:09 +01:00
if ( IoCManager . Resolve < IEntityManager > ( ) . TryGetComponent ( entity , out InventoryComponent ? inventory ) )
2021-06-20 10:09:24 +02:00
{
foreach ( var slot in EquipmentSlotDefines . AllSlots )
{
var equipmentStr = startingGear . GetGear ( slot , profile ) ;
2021-09-30 13:35:35 +02:00
if ( ! string . IsNullOrEmpty ( equipmentStr ) )
2021-06-20 10:09:24 +02:00
{
2021-12-03 15:53:09 +01:00
var equipmentEntity = EntityManager . SpawnEntity ( equipmentStr , IoCManager . Resolve < IEntityManager > ( ) . GetComponent < TransformComponent > ( entity ) . Coordinates ) ;
2021-12-04 12:35:33 +01:00
inventory . Equip ( slot , EntityManager . GetComponent < ItemComponent > ( equipmentEntity ) ) ;
2021-06-20 10:09:24 +02:00
}
}
}
2021-12-04 12:35:33 +01:00
if ( EntityManager . TryGetComponent ( entity , out HandsComponent ? handsComponent ) )
2021-06-20 10:09:24 +02:00
{
var inhand = startingGear . Inhand ;
foreach ( var ( hand , prototype ) in inhand )
{
2021-12-04 12:35:33 +01:00
var inhandEntity = EntityManager . SpawnEntity ( prototype , EntityManager . GetComponent < TransformComponent > ( entity ) . Coordinates ) ;
2021-06-21 02:21:20 -07:00
handsComponent . TryPickupEntity ( hand , inhandEntity , checkActionBlocker : false ) ;
2021-06-20 10:09:24 +02:00
}
}
}
2021-12-04 12:35:33 +01:00
public void EquipIdCard ( EntityUid entity , string characterName , JobPrototype jobPrototype )
2021-06-20 10:09:24 +02:00
{
2021-12-04 12:35:33 +01:00
if ( ! EntityManager . TryGetComponent ( entity , out InventoryComponent ? inventory ) )
2021-06-20 10:09:24 +02:00
return ;
if ( ! inventory . TryGetSlotItem ( EquipmentSlotDefines . Slots . IDCARD , out ItemComponent ? item ) )
{
return ;
}
var itemEntity = item . Owner ;
2021-12-03 15:53:09 +01:00
if ( ! IoCManager . Resolve < IEntityManager > ( ) . TryGetComponent ( itemEntity , out PDAComponent ? pdaComponent ) | | pdaComponent . ContainedID = = null )
2021-06-20 10:09:24 +02:00
return ;
var card = pdaComponent . ContainedID ;
2021-12-03 15:53:09 +01:00
_cardSystem . TryChangeFullName ( card . Owner , characterName , card ) ;
_cardSystem . TryChangeJobTitle ( card . Owner , jobPrototype . Name , card ) ;
2021-06-20 10:09:24 +02:00
2021-12-03 15:53:09 +01:00
var access = IoCManager . Resolve < IEntityManager > ( ) . GetComponent < AccessComponent > ( card . Owner ) ;
2021-06-20 10:09:24 +02:00
var accessTags = access . Tags ;
accessTags . UnionWith ( jobPrototype . Access ) ;
2021-12-04 12:35:33 +01:00
_pdaSystem . SetOwner ( pdaComponent , characterName ) ;
2021-06-20 10:09:24 +02:00
}
#endregion
private void AddManifestEntry ( string characterName , string jobId )
{
_manifest . Add ( new ManifestEntry ( characterName , jobId ) ) ;
}
#region Spawn Points
2021-11-26 03:02:46 -06:00
public EntityCoordinates GetJobSpawnPoint ( string jobId , StationId station )
2021-06-20 10:09:24 +02:00
{
var location = _spawnPoint ;
_possiblePositions . Clear ( ) ;
2021-11-08 12:37:32 +01:00
foreach ( var ( point , transform ) in EntityManager . EntityQuery < SpawnPointComponent , TransformComponent > ( ) )
2021-06-20 10:09:24 +02:00
{
2021-11-26 03:02:46 -06:00
var matchingStation =
EntityManager . TryGetComponent < StationComponent > ( transform . ParentUid , out var stationComponent ) & &
stationComponent . Station = = station ;
DebugTools . Assert ( EntityManager . TryGetComponent < IMapGridComponent > ( transform . ParentUid , out _ ) ) ;
if ( point . SpawnType = = SpawnPointType . Job & & point . Job ? . ID = = jobId & & matchingStation )
2021-06-20 10:09:24 +02:00
_possiblePositions . Add ( transform . Coordinates ) ;
}
if ( _possiblePositions . Count ! = 0 )
location = _robustRandom . Pick ( _possiblePositions ) ;
2021-11-26 03:02:46 -06:00
else
location = GetLateJoinSpawnPoint ( station ) ; // We need a sane fallback here, so latejoin it is.
2021-06-20 10:09:24 +02:00
return location ;
}
2021-11-26 03:02:46 -06:00
public EntityCoordinates GetLateJoinSpawnPoint ( StationId station )
2021-06-20 10:09:24 +02:00
{
var location = _spawnPoint ;
_possiblePositions . Clear ( ) ;
2021-11-08 12:37:32 +01:00
foreach ( var ( point , transform ) in EntityManager . EntityQuery < SpawnPointComponent , TransformComponent > ( ) )
2021-06-20 10:09:24 +02:00
{
2021-11-26 03:02:46 -06:00
var matchingStation =
EntityManager . TryGetComponent < StationComponent > ( transform . ParentUid , out var stationComponent ) & &
stationComponent . Station = = station ;
DebugTools . Assert ( EntityManager . TryGetComponent < IMapGridComponent > ( transform . ParentUid , out _ ) ) ;
if ( point . SpawnType = = SpawnPointType . LateJoin & & matchingStation )
_possiblePositions . Add ( transform . Coordinates ) ;
2021-06-20 10:09:24 +02:00
}
if ( _possiblePositions . Count ! = 0 )
location = _robustRandom . Pick ( _possiblePositions ) ;
return location ;
}
public EntityCoordinates GetObserverSpawnPoint ( )
{
var location = _spawnPoint ;
_possiblePositions . Clear ( ) ;
2021-11-08 12:37:32 +01:00
foreach ( var ( point , transform ) in EntityManager . EntityQuery < SpawnPointComponent , TransformComponent > ( ) )
2021-06-20 10:09:24 +02:00
{
if ( point . SpawnType = = SpawnPointType . Observer )
_possiblePositions . Add ( transform . Coordinates ) ;
}
if ( _possiblePositions . Count ! = 0 )
location = _robustRandom . Pick ( _possiblePositions ) ;
return location ;
}
#endregion
}
}