WorldBuilder/WorldNodeLink.cs
using System;
using System.Text.Json.Serialization;
using Clover.Components;
using Clover.Data;
using Clover.Items;
using Clover.Persistence;
namespace Clover;
/// <summary>
/// This class represents a link between a world and an item in the world.
/// All items placed on the grid in a world have one of these associated with them.
/// </summary>
/*
public class WorldNodeLink : IValid
{
[JsonIgnore] public World World { get; set; }
[JsonIgnore] public GameObject Node { get; set; }
public Vector2Int GridPosition => World.WorldToItemGrid( Node.WorldPosition );
public World.ItemRotation GridRotation =>
World.GetItemRotationFromDirection( World.Get4Direction( Node.WorldRotation ) );
public Vector3 WorldPosition => Node.WorldPosition;
public Rotation WorldRotation => Node.WorldRotation;
// public World.ItemPlacement GridPlacement;
public World.ItemPlacementType PlacementType { get; set; }
public Vector2Int Size { get; set; }
public string ItemId { get; set; }
// public string PrefabPath;
public PlaceableNode PlacedOn { get; set; }
[Icon( "save" )] private PersistentItem Persistence { get; set; }
public ItemData ItemData => ItemData.Get( ItemId );
public bool IsBeingPickedUp { get; set; }
// public bool IsDroppedItem => PrefabPath == "items/misc/dropped_item/dropped_item.prefab";
public bool IsValid => World != null && World.HasNodeLink( this );
public WorldNodeLink( World world, GameObject item )
{
World = world;
Node = item;
// GetData( node );
// LoadItemData();
}
public IList<Vector2Int> GetGridPositions( bool global = false )
{
var itemData = ItemData;
if ( itemData == null )
{
throw new Exception( $"Item data not found on {this} ({ItemId})" );
}
if ( PlacementType == World.ItemPlacementType.Dropped )
{
return new List<Vector2Int> { global ? GridPosition : Vector2Int.Zero };
}
return itemData.GetGridPositions( GridRotation, GridPosition );
}
public bool ShouldBeSaved()
{
/#1#/ return true;
if ( Node is IWorldItem worldItem )
{
return worldItem.ShouldBeSaved();
}#1#
return true;
}
public void DestroyNode()
{
Node.Destroy();
}
public string GetName()
{
return ItemData != null ? ItemData.Name : Node.Name;
}
// public IList<PlaceableNode> GetPlaceableNodes() => Node.GetNodesOfType<PlaceableNode>();
public IEnumerable<PlaceableNode> GetPlaceableNodes() =>
Node.Components.GetAll<PlaceableNode>( FindMode.EverythingInDescendants );
public PlaceableNode GetPlaceableNodeAtGridPosition( Vector2Int position )
{
// return GetPlaceableNodes().FirstOrDefault( n => GridPosition == World.WorldToItemGrid( n.WorldPosition ) );
return GetPlaceableNodes().MinBy( n => (World.WorldToItemGrid( n.WorldPosition ) - position).LengthSquared );
}
/*public PersistentItem GetPersistentItem()
{
var persistentItem = new PersistentItem
{
ItemId = ItemId,
};
return persistentItem;
}#1#
public void SetPersistence( PersistentItem persistentItem )
{
Persistence = persistentItem;
}
[Pure, Icon( "save" )]
public PersistentItem GetPersistence()
{
return Persistence ??= new PersistentItem();
}
public T GetPersistence<T>() where T : PersistentItem
{
if ( Persistence is T t )
{
return t;
}
return null;
}
public static WorldNodeLink Get( GameObject gameObject )
{
return WorldManager.Instance.GetWorldNodeLink( gameObject );
}
public PersistentWorldItem OnNodeSave()
{
if ( Node.Components.TryGet<WorldItem>( out var worldItem ) )
{
Log.Trace( $"Running OnItemSave on {Node}" );
worldItem.OnItemSave?.Invoke( this );
}
else
{
Log.Warning( $"No WorldItem component found on {Node}" );
}
Persistence.ItemId ??= ItemId;
/*foreach ( var saveable in Node.Components.GetAll<ISaveData>( FindMode.EverythingInSelfAndDescendants ) )
{
saveable.OnSave( this );
}
foreach ( var persistent in Node.Components.GetAll<IPersistent>( FindMode.EverythingInSelfAndDescendants ) )
{
persistent.OnSave( Persistence );
}#1#
RunSavePersistence( Persistence );
return new PersistentWorldItem
{
Position = GridPosition,
Rotation = GridRotation,
WPosition = WorldPosition.SnapToGrid( 1 ),
WAngles = WorldRotation.Angles().SnapToGrid( 1 ),
// Placement = GridPlacement,
PlacementType = PlacementType,
// PrefabPath = PrefabPath,
ItemId = ItemId,
Item = Persistence,
};
}
public void RunSavePersistence( PersistentItem item )
{
var components = Node.GetComponents<Component>();
var keys = new List<string>();
foreach ( var component in components )
{
var properties = TypeLibrary.GetPropertyDescriptions( component );
foreach ( var property in properties )
{
var saveDataAttribute = property.GetCustomAttribute<SaveDataAttribute>();
if ( saveDataAttribute == null )
{
// XLog.Debug( this, $"No save data attribute on {property.Name} on {component}" );
continue;
}
var keyName = !string.IsNullOrEmpty( saveDataAttribute.Key ) ? saveDataAttribute.Key : property.Name;
if ( keys.Contains( keyName ) )
{
Log.Error( $"Duplicate arbitrary data key {keyName} on {component}" );
continue;
}
var type = property.PropertyType;
var value = property.GetValue( component );
// XLog.Info( this,
// $"Saving arbitrary data {keyName} = {value}, type {type}/{value?.GetType()}" );
// Log.Info( $"Saving '{keyName}' = '{value}' on {component}" );
item.SetSaveData( keyName, value, type );
// XLog.Info( this, $"Saving arbitrary data {keyName} = {value}" );
keys.Add( keyName );
// Log.Info( $"Saved '{keyName}' = '{value}' on {component}" );
}
}
foreach ( var persistent in Node.Components.GetAll<IPersistent>( FindMode.EverythingInSelfAndDescendants ) )
{
persistent.OnSave( Persistence );
}
if ( Node.Components.TryGet<WorldItem>( out var worldItem ) )
{
worldItem.OnItemSave?.Invoke( this );
}
}
public void RunLoadPersistence( PersistentItem item )
{
var components = Node.GetComponents<Component>();
foreach ( var component in components )
{
var properties = TypeLibrary.GetPropertyDescriptions( component );
foreach ( var property in properties )
{
var saveDataAttribute = property.GetCustomAttribute<SaveDataAttribute>();
if ( saveDataAttribute == null )
{
continue;
}
if ( !string.IsNullOrEmpty( saveDataAttribute.Key ) )
{
// first try using the key from the attribute
var keyData = item.GetSaveData( property.PropertyType, saveDataAttribute.Key );
if ( keyData != null )
{
property.SetValue( component, keyData );
continue;
}
}
// if that fails, try using the property name
// we do this to maintain backwards compatibility with old saves that don't have the key set
var propertyData = item.GetSaveData( property.PropertyType, property.Name );
if ( propertyData != null )
{
property.SetValue( component, propertyData );
}
}
}
foreach ( var persistent in Node.Components.GetAll<IPersistent>( FindMode.EverythingInSelfAndDescendants ) )
{
persistent.OnLoad( Persistence );
}
if ( Node.Components.TryGet<WorldItem>( out var worldItem ) )
{
worldItem.OnItemLoad?.Invoke( this );
}
}
public void OnNodeLoad( PersistentWorldItem persistentItem )
{
// PrefabPath = persistentItem.PrefabPath;
ItemId = persistentItem.ItemId;
Persistence = persistentItem.Item;
foreach ( var persistent in Node.Components.GetAll<IPersistent>( FindMode.EverythingInSelfAndAncestors ) )
{
persistent.OnLoad( Persistence );
}
if ( Node.Components.TryGet<WorldItem>( out var worldItem ) )
{
// Log.Info( $"Running OnItemLoad on {Node}" );
worldItem.WorldLayerObject.SetLayer( World.Layer );
worldItem.OnItemLoad?.Invoke( this );
}
else
{
Log.Warning( $"No WorldItem component found on {Node}" );
}
Node.Name = GetName();
}
public void OnNodeAdded()
{
if ( Node.Components.TryGet<WorldItem>( out var worldItem ) )
{
Log.Trace( $"Running OnItemAdded on {Node}" );
// worldItem.OnItemAdded?.Invoke( this );
}
if ( Persistence == null )
{
Persistence = new PersistentItem();
}
}
public string GetPrefabPath()
{
var prefabPath = Node.PrefabInstanceSource;
if ( string.IsNullOrEmpty( prefabPath ) )
{
if ( Node.Components.TryGet<WorldItem>( out var worldObject ) )
{
prefabPath = worldObject.Prefab;
if ( string.IsNullOrEmpty( prefabPath ) )
{
Log.Warning( $"NodeLink {this} has no prefab path" );
return null;
}
}
}
return prefabPath;
}
public void CalculateSize()
{
var gridPositions = GetGridPositions();
var minX = gridPositions.Min( p => p.x );
var minY = gridPositions.Min( p => p.y );
var maxX = gridPositions.Max( p => p.x );
var maxY = gridPositions.Max( p => p.y );
Size = new Vector2Int( maxX - minX + 1, maxY - minY + 1 );
// Log.Info( $"Calculated size for {this}: {Size}" );
}
/// <summary>
/// Removes this item from the world.
/// </summary>
public void Remove()
{
World.RemoveItem( this );
}
}
*/