first commit
This commit is contained in:
@@ -0,0 +1,620 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Data.Autoloot;
|
||||
using ClassicAssist.Data.Macros.Commands;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.Models;
|
||||
using ClassicAssist.UI.Views;
|
||||
using ClassicAssist.UO;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Network;
|
||||
using ClassicAssist.UO.Network.Packets;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class EntityCollectionViewerViewModel : BaseViewModel
|
||||
{
|
||||
private ICommand _applyFiltersCommand;
|
||||
private ICommand _cancelActionCommand;
|
||||
private CancellationTokenSource _cancellationToken;
|
||||
private ICommand _changeSortStyleCommand;
|
||||
private ItemCollection _collection;
|
||||
private ICommand _contextContextMenuRequestCommand;
|
||||
private ICommand _contextMoveToBackpackCommand;
|
||||
private ICommand _contextMoveToContainerCommand;
|
||||
private ICommand _contextUseItemCommand;
|
||||
private ObservableCollection<EntityCollectionData> _entities;
|
||||
private ICommand _equipItemCommand;
|
||||
private IEnumerable<EntityCollectionFilter> _filters;
|
||||
private bool _isPerformingAction;
|
||||
private ICommand _itemDoubleClickCommand;
|
||||
private ICommand _refreshCommand;
|
||||
|
||||
private ObservableCollection<EntityCollectionData> _selectedItems =
|
||||
new ObservableCollection<EntityCollectionData>();
|
||||
|
||||
private bool _showProperties;
|
||||
|
||||
private IComparer<Entity> _sorter = new IDThenSerialComparer();
|
||||
private string _statusLabel;
|
||||
private ICommand _togglePropertiesCommand;
|
||||
private bool _topmost;
|
||||
|
||||
public EntityCollectionViewerViewModel()
|
||||
{
|
||||
_collection = new ItemCollection( 0 );
|
||||
|
||||
for ( int i = 0; i < 5; i++ )
|
||||
{
|
||||
_collection.Add( new Item( i + 1 ) { ID = 100 + i } );
|
||||
}
|
||||
|
||||
_collection.Add( new Item( 6 ) { ID = 106, Hue = 2413 } );
|
||||
|
||||
Entities = new ObservableCollection<EntityCollectionData>( _collection.ToEntityCollectionData( _sorter ) );
|
||||
}
|
||||
|
||||
public EntityCollectionViewerViewModel( ItemCollection collection )
|
||||
{
|
||||
_collection = collection;
|
||||
|
||||
Entities = new ObservableCollection<EntityCollectionData>( collection.ToEntityCollectionData( _sorter ) );
|
||||
|
||||
SelectedItems.CollectionChanged += ( sender, args ) =>
|
||||
{
|
||||
if ( !( sender is ObservableCollection<EntityCollectionData> ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStatusLabel();
|
||||
};
|
||||
|
||||
UpdateStatusLabel();
|
||||
}
|
||||
|
||||
public ICommand ApplyFiltersCommand =>
|
||||
_applyFiltersCommand ?? ( _applyFiltersCommand = new RelayCommand( ApplyFilters, o => true ) );
|
||||
|
||||
public ICommand CancelActionCommand =>
|
||||
_cancelActionCommand ?? ( _cancelActionCommand = new RelayCommandAsync( CancelAction,
|
||||
o => _cancellationToken != null ) );
|
||||
|
||||
public ICommand ChangeSortStyleCommand =>
|
||||
_changeSortStyleCommand ?? ( _changeSortStyleCommand = new RelayCommand( ChangeSortStyle, o => true ) );
|
||||
|
||||
public ICommand ContextContextMenuRequestCommand =>
|
||||
_contextContextMenuRequestCommand ?? ( _contextContextMenuRequestCommand =
|
||||
new RelayCommand( ContextMenuRequest, o => SelectedItems.Count > 0 ) );
|
||||
|
||||
public ICommand ContextMoveToBackpackCommand =>
|
||||
_contextMoveToBackpackCommand ?? ( _contextMoveToBackpackCommand =
|
||||
new RelayCommandAsync( ContextMoveToBackpack, o => SelectedItems != null && !IsPerformingAction ) );
|
||||
|
||||
public ICommand ContextMoveToContainerCommand =>
|
||||
_contextMoveToContainerCommand ?? ( _contextMoveToContainerCommand =
|
||||
new RelayCommandAsync( ContextMoveToContainer, o => SelectedItems != null && !IsPerformingAction ) );
|
||||
|
||||
public ICommand ContextUseItemCommand =>
|
||||
_contextUseItemCommand ?? ( _contextUseItemCommand =
|
||||
new RelayCommandAsync( ContextUseItem, o => SelectedItems != null && !IsPerformingAction ) );
|
||||
|
||||
public ObservableCollection<EntityCollectionData> Entities
|
||||
{
|
||||
get => _entities;
|
||||
set => SetProperty( ref _entities, value );
|
||||
}
|
||||
|
||||
public ICommand EquipItemCommand =>
|
||||
_equipItemCommand ?? ( _equipItemCommand = new RelayCommand( EquipItem, o => SelectedItems != null ) );
|
||||
|
||||
public bool IsPerformingAction
|
||||
{
|
||||
get => _isPerformingAction;
|
||||
set => SetProperty( ref _isPerformingAction, value );
|
||||
}
|
||||
|
||||
public ICommand ItemDoubleClickCommand =>
|
||||
_itemDoubleClickCommand ?? ( _itemDoubleClickCommand = new RelayCommand( ItemDoubleClick, o => true ) );
|
||||
|
||||
public static Lazy<Dictionary<int, int>> MountIDEntries { get; set; } =
|
||||
new Lazy<Dictionary<int, int>>( LoadMountIDEntries );
|
||||
|
||||
public ICommand RefreshCommand =>
|
||||
_refreshCommand ?? ( _refreshCommand = new RelayCommand( Refresh, o => _collection?.Serial != 0 ) );
|
||||
|
||||
public ObservableCollection<EntityCollectionData> SelectedItems
|
||||
{
|
||||
get => _selectedItems;
|
||||
set => SetProperty( ref _selectedItems, value );
|
||||
}
|
||||
|
||||
public bool ShowProperties
|
||||
{
|
||||
get => _showProperties;
|
||||
set => SetProperty( ref _showProperties, value );
|
||||
}
|
||||
|
||||
public static Lazy<int[]> ShrinkEntries { get; set; } = new Lazy<int[]>( LoadShrinkTable );
|
||||
|
||||
public string StatusLabel
|
||||
{
|
||||
get => _statusLabel;
|
||||
set => SetProperty( ref _statusLabel, value );
|
||||
}
|
||||
|
||||
public ICommand TogglePropertiesCommand =>
|
||||
_togglePropertiesCommand ?? ( _togglePropertiesCommand = new RelayCommand( ToggleProperties, o => true ) );
|
||||
|
||||
public bool Topmost
|
||||
{
|
||||
get => _topmost;
|
||||
set => SetProperty( ref _topmost, value );
|
||||
}
|
||||
|
||||
private static Dictionary<int, int> LoadMountIDEntries()
|
||||
{
|
||||
Dictionary<int, int> entries = new Dictionary<int, int>();
|
||||
|
||||
string fileName = Path.Combine( Engine.StartupPath, "Data", "MountID.json" );
|
||||
|
||||
if ( !File.Exists( fileName ) )
|
||||
{
|
||||
return entries;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
entries = JsonConvert.DeserializeObject<Dictionary<int, int>>( File.ReadAllText( fileName ) );
|
||||
}
|
||||
catch ( Exception )
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static int[] LoadShrinkTable()
|
||||
{
|
||||
int[] table = new int[ushort.MaxValue];
|
||||
|
||||
using ( StringReader reader = new StringReader( Properties.Resources.Shrink ) )
|
||||
{
|
||||
string line;
|
||||
|
||||
while ( ( line = reader.ReadLine() ) != null )
|
||||
{
|
||||
if ( line.Length == 0 || line.StartsWith( "#" ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] split = line.Split( '\t' );
|
||||
|
||||
if ( split.Length < 2 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int body = Convert.ToInt32( split[0] );
|
||||
int item = Convert.ToInt32( split[1], 16 );
|
||||
|
||||
if ( body >= 0 && body < table.Length )
|
||||
{
|
||||
table[body] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private void EquipItem( object obj )
|
||||
{
|
||||
foreach ( EntityCollectionData item in SelectedItems )
|
||||
{
|
||||
StaticTile td = TileData.GetStaticTile( item.Entity.ID );
|
||||
|
||||
if ( !td.Flags.HasFlag( TileFlags.Wearable ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Layer layer = (Layer) td.Quality;
|
||||
|
||||
if ( layer == Layer.Invalid )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( item.Entity is Item equipItem )
|
||||
{
|
||||
Shared.UO.Commands.EquipItem( equipItem, layer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyFilters( object obj )
|
||||
{
|
||||
if ( !( obj is IEnumerable<EntityCollectionFilter> list ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_filters = list;
|
||||
|
||||
Entities.Clear();
|
||||
|
||||
Entities = new ObservableCollection<EntityCollectionData>( _collection.Filter( _filters )
|
||||
.ToEntityCollectionData( _sorter ) );
|
||||
|
||||
UpdateStatusLabel();
|
||||
}
|
||||
|
||||
private void ChangeSortStyle( object obj )
|
||||
{
|
||||
if ( !( obj is EntityCollectionSortStyle val ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( val )
|
||||
{
|
||||
case EntityCollectionSortStyle.Name:
|
||||
_sorter = new NameThenSerialComparer();
|
||||
|
||||
break;
|
||||
|
||||
case EntityCollectionSortStyle.Serial:
|
||||
_sorter = new SerialComparer();
|
||||
|
||||
break;
|
||||
|
||||
case EntityCollectionSortStyle.Hue:
|
||||
_sorter = new HueThenAmountComparer();
|
||||
|
||||
break;
|
||||
|
||||
case EntityCollectionSortStyle.ID:
|
||||
_sorter = new IDThenSerialComparer();
|
||||
|
||||
break;
|
||||
|
||||
case EntityCollectionSortStyle.Quantity:
|
||||
_sorter = new QuantityThenSerialComparer();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
Entities = new ObservableCollection<EntityCollectionData>( _collection.ToEntityCollectionData( _sorter ) );
|
||||
}
|
||||
|
||||
private void UpdateStatusLabel()
|
||||
{
|
||||
StatusLabel = string.Format( Strings._0__items___1__selected___2__total_amount, Entities.Count,
|
||||
SelectedItems?.Count ?? 0,
|
||||
SelectedItems?.Select( i => i.Entity ).Where( i => i is Item ).Cast<Item>().Sum( i => i.Count ) );
|
||||
}
|
||||
|
||||
private void Refresh( object obj )
|
||||
{
|
||||
ItemCollection collection = new ItemCollection( _collection.Serial );
|
||||
|
||||
Entity entity = Engine.Items.GetItem( _collection.Serial ) ??
|
||||
(Entity) Engine.Mobiles.GetMobile( _collection.Serial );
|
||||
|
||||
if ( entity == null )
|
||||
{
|
||||
Shared.UO.Commands.SystemMessage( Strings.Cannot_find_item___ );
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( entity )
|
||||
{
|
||||
case Item item:
|
||||
if ( item.Container == null )
|
||||
{
|
||||
Shared.UO.Commands.WaitForContainerContents( item.Serial, 1000 );
|
||||
}
|
||||
|
||||
collection = item.Container;
|
||||
break;
|
||||
case Mobile mobile:
|
||||
collection = new ItemCollection( entity.Serial ) { mobile.GetEquippedItems() };
|
||||
break;
|
||||
}
|
||||
|
||||
if ( collection == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_collection = collection;
|
||||
|
||||
Entities = new ObservableCollection<EntityCollectionData>( _collection.ToEntityCollectionData( _sorter ) );
|
||||
}
|
||||
|
||||
private async Task ContextUseItem( object arg )
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource();
|
||||
|
||||
try
|
||||
{
|
||||
IsPerformingAction = true;
|
||||
|
||||
await Task.Run( () =>
|
||||
{
|
||||
int[] items = SelectedItems.Select( i => i.Entity.Serial ).ToArray();
|
||||
|
||||
foreach ( int item in items )
|
||||
{
|
||||
ObjectCommands.UseObject( item );
|
||||
|
||||
if ( _cancellationToken.Token.IsCancellationRequested )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsPerformingAction = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ContextMoveToContainer( object arg )
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource();
|
||||
|
||||
int serial = 0;
|
||||
|
||||
if ( arg is int s )
|
||||
{
|
||||
serial = s;
|
||||
}
|
||||
|
||||
if ( serial == 0 )
|
||||
{
|
||||
serial = await Shared.UO.Commands.GetTargeSerialAsync( Strings.Target_container___ );
|
||||
}
|
||||
|
||||
if ( serial == 0 )
|
||||
{
|
||||
Shared.UO.Commands.SystemMessage( Strings.Invalid_container___ );
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IsPerformingAction = true;
|
||||
|
||||
int[] items = SelectedItems.Select( i => i.Entity.Serial ).ToArray();
|
||||
|
||||
foreach ( int item in items )
|
||||
{
|
||||
await ActionPacketQueue.EnqueueDragDrop( item, -1, serial );
|
||||
|
||||
if ( _cancellationToken.Token.IsCancellationRequested )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsPerformingAction = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ContextMoveToBackpack( object arg )
|
||||
{
|
||||
await ContextMoveToContainer( Engine.Player.Backpack.Serial );
|
||||
}
|
||||
|
||||
private async Task CancelAction( object arg )
|
||||
{
|
||||
_cancellationToken?.Cancel();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ToggleProperties( object obj )
|
||||
{
|
||||
if ( !( obj is bool showProperties ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ShowProperties = showProperties;
|
||||
}
|
||||
|
||||
private static void ItemDoubleClick( object obj )
|
||||
{
|
||||
if ( !( obj is EntityCollectionData ecd ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectInspectorWindow window =
|
||||
new ObjectInspectorWindow { DataContext = new ObjectInspectorViewModel( ecd.Entity ) };
|
||||
|
||||
window.ShowDialog();
|
||||
}
|
||||
|
||||
private void ContextMenuRequest( object obj )
|
||||
{
|
||||
int[] items = SelectedItems.Select( i => i.Entity.Serial ).ToArray();
|
||||
|
||||
foreach ( int serial in items )
|
||||
{
|
||||
Engine.SendPacketToServer( new ContextMenuRequest( serial ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class EntityCollectionData
|
||||
{
|
||||
public BitmapSource Bitmap
|
||||
{
|
||||
get
|
||||
{
|
||||
BitmapSource result = Art.GetStatic( Entity.ID, Entity.Hue ).ToBitmapSource();
|
||||
|
||||
if ( !( Entity is Item item ) || item.Layer != Layer.Mount )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( !( EntityCollectionViewerViewModel.MountIDEntries.Value?.ContainsKey( Entity.ID ) ?? false ) )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
int id = (int) EntityCollectionViewerViewModel.MountIDEntries.Value?[Entity.ID];
|
||||
|
||||
result = Art.GetStatic( id, Entity.Hue ).ToBitmapSource();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public Entity Entity { get; set; }
|
||||
public string FullName => GetProperties( Entity );
|
||||
public string Name => GetName( Entity );
|
||||
|
||||
private static string GetProperties( Entity entity )
|
||||
{
|
||||
return entity.Properties == null
|
||||
? GetName( entity )
|
||||
: entity.Properties.Aggregate( "",
|
||||
( current, entityProperty ) => current + entityProperty.Text + "\r\n" ).TrimTrailingNewLine();
|
||||
}
|
||||
|
||||
private static string GetName( Entity entity )
|
||||
{
|
||||
if ( !( entity is Item item ) || item.Layer != Layer.Mount )
|
||||
{
|
||||
return entity.Name;
|
||||
}
|
||||
|
||||
if ( !( EntityCollectionViewerViewModel.MountIDEntries.Value?.ContainsKey( entity.ID ) ?? false ) )
|
||||
{
|
||||
return entity.Name;
|
||||
}
|
||||
|
||||
int id = (int) EntityCollectionViewerViewModel.MountIDEntries.Value?[entity.ID];
|
||||
|
||||
if ( id == 0 )
|
||||
{
|
||||
return entity.Name;
|
||||
}
|
||||
|
||||
StaticTile tileData = TileData.GetStaticTile( id );
|
||||
|
||||
return !string.IsNullOrEmpty( tileData.Name ) ? tileData.Name : entity.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExtensionMethods
|
||||
{
|
||||
public static ItemCollection Filter( this ItemCollection collection,
|
||||
IEnumerable<EntityCollectionFilter> filterList )
|
||||
{
|
||||
ItemCollection newCollection = new ItemCollection( collection.Serial );
|
||||
|
||||
IEnumerable<Predicate<Item>> predicates = FiltersToPredicates( filterList ).ToList();
|
||||
|
||||
if ( !predicates.Any() )
|
||||
{
|
||||
return collection;
|
||||
}
|
||||
|
||||
foreach ( Item item in collection.GetItems() )
|
||||
{
|
||||
if ( predicates.All( p => p( item ) ) )
|
||||
{
|
||||
newCollection.Add( item );
|
||||
}
|
||||
}
|
||||
|
||||
return newCollection;
|
||||
}
|
||||
|
||||
private static IEnumerable<Predicate<Item>> FiltersToPredicates(
|
||||
IEnumerable<EntityCollectionFilter> filterList )
|
||||
{
|
||||
List<Predicate<Item>> predicates = new List<Predicate<Item>>();
|
||||
|
||||
foreach ( EntityCollectionFilter filter in filterList )
|
||||
{
|
||||
PropertyEntry constraint = filter.Constraint;
|
||||
|
||||
switch ( constraint.ConstraintType )
|
||||
{
|
||||
case PropertyType.Properties:
|
||||
{
|
||||
predicates.Add( i => i.Properties != null && constraint.Clilocs.Any( cliloc =>
|
||||
i.Properties.Any( p => AutolootHelpers.MatchProperty( p, cliloc,
|
||||
constraint, filter.Operator, filter.Value ) ) ) );
|
||||
|
||||
break;
|
||||
}
|
||||
case PropertyType.Object:
|
||||
{
|
||||
predicates.Add( i =>
|
||||
AutolootHelpers.ItemHasObjectProperty( i, constraint.Name ) && AutolootHelpers.Operation(
|
||||
filter.Operator, AutolootHelpers.GetItemObjectPropertyValue<int>( i, constraint.Name ),
|
||||
filter.Value ) );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
return predicates;
|
||||
}
|
||||
|
||||
public static string TrimTrailingNewLine( this string s )
|
||||
{
|
||||
return s.TrimEnd( '\r', '\n' );
|
||||
}
|
||||
|
||||
public static List<EntityCollectionData> ToEntityCollectionData( this ItemCollection itemCollection,
|
||||
IComparer<Entity> comparer )
|
||||
{
|
||||
if ( itemCollection == null )
|
||||
{
|
||||
return new List<EntityCollectionData>();
|
||||
}
|
||||
|
||||
Item[] items = itemCollection.GetItems();
|
||||
|
||||
IEnumerable<Item> noNames = items.Where( i => string.IsNullOrEmpty( i.Name ) );
|
||||
|
||||
foreach ( Item item in noNames )
|
||||
{
|
||||
item.Name = $"0x{item.Serial:x8}";
|
||||
}
|
||||
|
||||
return items.OrderBy( i => i, comparer ).Select( item => new EntityCollectionData { Entity = item } )
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user