AI/Guests/Guest.Persistence.cs
using HC3.Persistence;
using System.Collections.Immutable;
using System.Text.Json.Nodes;
namespace HC3;
partial class Guest
{
public sealed record SaveData( string? FullName, Vector3 Position, float Rotation, JsonObject Metadata = null );
public SaveData GetSaveData()
{
return new( FullName, WorldPosition, WorldRotation.Yaw(), GetPersistentMetadata() );
}
public void SetSaveData( SaveData saveData )
{
FullName = saveData.FullName ?? FullName;
WorldPosition = saveData.Position;
WorldRotation = Rotation.FromYaw( saveData.Rotation );
Transform.ClearInterpolation();
SetPersistentMetadata( saveData.Metadata );
}
protected virtual JsonObject GetPersistentMetadata()
{
var obj = new JsonObject();
obj["Needs"] = Needs.ToJson();
obj["Identity"] = Identity.ToJson();
obj["Delinquency"] = Delinquency;
if ( Building.IsValid() )
{
obj["Building"] = Building.GetStateJson( this );
}
if ( Thoughts.IsValid() && Thoughts.GetAll().Any() )
{
obj["Thoughts"] = Thoughts.ToJson();
}
return obj;
}
protected virtual void SetPersistentMetadata( JsonObject obj )
{
if ( obj == null )
return;
if ( obj.TryGetPropertyValue( "Delinquency", out var delinquency ) )
{
Delinquency = delinquency.GetValue<float>();
}
if ( obj.TryGetPropertyValue( "Needs", out var needs ) )
{
Needs.LoadFromJson( needs );
}
if ( obj.TryGetPropertyValue( "Identity", out var identity ) )
{
Identity = Identity.FromJson( identity );
}
if ( obj.TryGetPropertyValue( "Building", out var state ) )
{
InitializeFromState( state );
}
if ( obj.TryGetPropertyValue( "Thoughts", out var thoughts ) )
{
Thoughts?.FromJson( thoughts );
}
}
/// <summary>
/// Initializes a guest from a saved building state - right now we just load them into the building they were on.
/// In the future it could be smarter, saved slot index, shit like that.
/// </summary>
/// <param name="node"></param>
async void InitializeFromState( JsonNode node )
{
// Wait for everything else to load first
await PersistenceManager.WaitForLoad();
var gridPosition = Vector3.Parse( node["Position"]?.ToString() );
var terrain = GridNavigation.Instance.Terrain;
var worldTileIndex = terrain.GetTileIndex( gridPosition );
var cell = GridManager.Instance.GetCell( new Vector2Int( worldTileIndex ) );
if ( cell is null )
{
Log.Warning( $"When trying to initialize a guest from saved state, we couldn't find a cell." );
return;
}
var building = cell.GetComponent<Building>();
if ( !building.IsValid() ) return;
// Load us in!
if ( !building.LoadIn( this ) )
{
Log.Warning( $"When trying to load a guest into saved building, the building reported a failure.\nThis could happen if the slots changed between game updates." );
GameObject.Destroy();
return;
}
else
{
// Bit of a hack, but it works
var needsAction = ActionController.GetComponentInChildren<SatisfyNeedsAction>();
needsAction.Force( building );
}
}
}
partial class GuestManager : ISaveDataProperty<ImmutableArray<Guest.SaveData>>
{
string ISaveDataProperty.PropertyName => "Guests";
int ISaveDataProperty.PropertyOrder => 1_000;
ImmutableArray<Guest.SaveData> ISaveDataProperty<ImmutableArray<Guest.SaveData>>.WriteValue( Scene scene )
{
return Scene.GetAll<Guest>()
.Select( x => x.GetSaveData() )
.ToImmutableArray();
}
void ISaveDataProperty<ImmutableArray<Guest.SaveData>>.ReadValue( Scene scene, ImmutableArray<Guest.SaveData> array )
{
foreach ( var guest in Scene.GetAll<Guest>().ToArray() )
{
guest.GameObject.Destroy();
}
foreach ( var saveData in array )
{
CreateGuestCore( guest => guest.SetSaveData( saveData ) );
}
}
}