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 ClassicAssist.Data; using ClassicAssist.Data.Autoloot; using ClassicAssist.Data.Regions; using ClassicAssist.Misc; using ClassicAssist.Shared.Resources; using ClassicAssist.Shared.UI.ViewModels.Autoloot; using ClassicAssist.UI.Misc; using ClassicAssist.UI.ViewModels; using ClassicAssist.UO.Data; using ClassicAssist.UO.Network; using ClassicAssist.UO.Network.PacketFilter; using ClassicAssist.UO.Network.Packets; using ClassicAssist.UO.Objects; using Microsoft.Scripting.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using ReactiveUI; using UOC = ClassicAssist.Shared.UO.Commands; namespace ClassicAssist.Shared.UI.ViewModels.Agents { public class AutolootViewModel : BaseViewModel, ISettingProvider { private const int LOOT_TIMEOUT = 5000; private readonly object _autolootLock = new object(); private readonly IObservable _entrySelected; private readonly IObservable _itemSelected; private readonly string _propertiesFile = Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Data", "Properties.json" ); private readonly string _propertiesFileCustom = Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Data", "Properties.Custom.json" ); private ICommand _clipboardCopyCommand; private ICommand _clipboardPasteCommand; private ObservableCollection _constraints = new ObservableCollection(); private int _containerSerial; private ICommand _defineCustomPropertiesCommand; private bool _disableInGuardzone; private bool _enabled; private ICommand _insertCommand; private ICommand _insertConstraintCommand; private ICommand _insertMatchAnyCommand; private ObservableCollectionEx _items = new ObservableCollectionEx(); private ICommand _removeCommand; private ICommand _removeConstraintCommand; private ICommand _removeSingleConstraintCommand; private RelayCommand _resetContainerCommand; private AutolootEntry _selectedItem; private ObservableCollection _selectedProperties = new ObservableCollection(); private AutolootConstraintEntry _selectedProperty; private ICommand _selectHueCommand; private ICommand _setContainerCommand; public AutolootViewModel() { if ( !File.Exists( _propertiesFile ) ) { return; } LoadProperties(); LoadCustomProperties(); AutolootHelpers.SetAutolootContainer = serial => ContainerSerial = serial; IncomingPacketHandlers.CorpseContainerDisplayEvent += OnCorpseEvent; AutolootManager.GetInstance().GetEntries = () => _items.ToList(); _entrySelected = this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ); _itemSelected = this.WhenAnyValue( e => e.SelectedProperty, selector: e => e != null ); } public ICommand ClipboardCopyCommand => _clipboardCopyCommand ?? ( _clipboardCopyCommand = new RelayCommand( ClipboardCopy, o => true ) ); public ICommand ClipboardPasteCommand => _clipboardPasteCommand ?? ( _clipboardPasteCommand = new RelayCommand( ClipboardPaste, o => true ) ); public ObservableCollection Constraints { get => _constraints; set => SetProperty( ref _constraints, value ); } public int ContainerSerial { get => _containerSerial; set => SetProperty( ref _containerSerial, value ); } public ICommand DefineCustomPropertiesCommand => _defineCustomPropertiesCommand ?? ( _defineCustomPropertiesCommand = new RelayCommand( DefineCustomProperties, o => true ) ); public bool DisableInGuardzone { get => _disableInGuardzone; set => SetProperty( ref _disableInGuardzone, value ); } public bool Enabled { get => _enabled; set => SetProperty( ref _enabled, value ); } public ICommand InsertCommand => _insertCommand ?? ( _insertCommand = ReactiveCommand.CreateFromTask( Insert ) ); public ICommand InsertConstraintCommand => _insertConstraintCommand ?? ( _insertConstraintCommand = ReactiveCommand.Create( InsertConstraint, _entrySelected ) ); public ICommand InsertMatchAnyCommand => _insertMatchAnyCommand ?? ( _insertMatchAnyCommand = new RelayCommand( InsertMatchAny, o => true ) ); public ObservableCollectionEx Items { get => _items; set => SetProperty( ref _items, value ); } public ICommand RemoveCommand => _removeCommand ?? ( _removeCommand = ReactiveCommand.CreateFromTask( Remove, _entrySelected ) ); public ICommand RemoveConstraintCommand => _removeConstraintCommand ?? ( _removeConstraintCommand = ReactiveCommand.Create>( RemoveConstraint, _itemSelected ) ); public ICommand RemoveSingleConstraintCommand => _removeSingleConstraintCommand ?? ( _removeSingleConstraintCommand = ReactiveCommand.Create( RemoveSingleConstraint, _itemSelected ) ); public ICommand ResetContainerCommand => _resetContainerCommand = new RelayCommand( ResetContainer, o => true ); public AutolootEntry SelectedItem { get => _selectedItem; set => SetProperty( ref _selectedItem, value ); } public ObservableCollection SelectedProperties { get => _selectedProperties; set => SetProperty( ref _selectedProperties, value ); } public AutolootConstraintEntry SelectedProperty { get => _selectedProperty; set => SetProperty( ref _selectedProperty, value ); } public ICommand SelectHueCommand => _selectHueCommand ?? ( _selectHueCommand = ReactiveCommand.CreateFromTask( SelectHue, _entrySelected ) ); public ICommand SetContainerCommand => _setContainerCommand ?? ( _setContainerCommand = new RelayCommandAsync( SetContainer, o => true ) ); public void Serialize( JObject json ) { if ( json == null ) { return; } JObject autolootObj = new JObject { { "Enabled", Enabled }, { "DisableInGuardzone", DisableInGuardzone }, { "Container", ContainerSerial } }; JArray itemsArray = new JArray(); foreach ( AutolootEntry entry in Items ) { JObject entryObj = new JObject { { "Name", entry.Name }, { "ID", entry.ID }, { "Autoloot", entry.Autoloot }, { "Rehue", entry.Rehue }, { "RehueHue", entry.RehueHue }, { "Enabled", entry.Enabled } }; if ( entry.Constraints != null ) { JArray constraintsArray = new JArray(); foreach ( AutolootConstraintEntry constraint in entry.Constraints ) { JObject constraintObj = new JObject { { "Name", constraint.Property.Name }, { "Operator", constraint.Operator.ToString() }, { "Value", constraint.Value } }; constraintsArray.Add( constraintObj ); } entryObj.Add( "Properties", constraintsArray ); } itemsArray.Add( entryObj ); } autolootObj.Add( "Items", itemsArray ); json.Add( "Autoloot", autolootObj ); } public void Deserialize( JObject json, Options options ) { Items.Clear(); if ( json?["Autoloot"] == null ) { return; } JToken config = json["Autoloot"]; Enabled = config["Enabled"]?.ToObject() ?? true; DisableInGuardzone = config["DisableInGuardzone"]?.ToObject() ?? false; ContainerSerial = config["Container"]?.ToObject() ?? 0; if ( config["Items"] != null ) { JToken items = config["Items"]; foreach ( JToken token in items ) { AutolootEntry entry = new AutolootEntry { Name = token["Name"]?.ToObject() ?? "Unknown", ID = token["ID"]?.ToObject() ?? 0, Autoloot = token["Autoloot"]?.ToObject() ?? false, Rehue = token["Rehue"]?.ToObject() ?? false, RehueHue = token["RehueHue"]?.ToObject() ?? 0, Enabled = token["Enabled"]?.ToObject() ?? true }; if ( token["Properties"] != null ) { List constraintsList = new List(); // ReSharper disable once LoopCanBeConvertedToQuery foreach ( JToken constraintToken in token["Properties"] ) { string constraintName = constraintToken["Name"]?.ToObject() ?? "Unknown"; PropertyEntry propertyEntry = Constraints.FirstOrDefault( c => c.Name == constraintName ); if ( propertyEntry == null ) { continue; } AutolootConstraintEntry constraintObj = new AutolootConstraintEntry { Property = propertyEntry, Operator = constraintToken["Operator"]?.ToObject() ?? AutolootOperator.Equal, Value = constraintToken["Value"]?.ToObject() ?? 0 }; constraintsList.Add( constraintObj ); } entry.Constraints.AddRange( constraintsList ); } Items.Add( entry ); } } } private void RemoveSingleConstraint( AutolootConstraintEntry obj ) { SelectedItem?.Constraints.Remove( obj ); } private void LoadCustomProperties() { if ( !File.Exists( _propertiesFileCustom ) ) { return; } JsonSerializer serializer = new JsonSerializer(); using ( StreamReader sr = new StreamReader( _propertiesFileCustom ) ) { using ( JsonTextReader reader = new JsonTextReader( sr ) ) { PropertyEntry[] constraints = serializer.Deserialize( reader ); foreach ( PropertyEntry constraint in constraints ) { Constraints.AddSorted( constraint ); } } } } private void LoadProperties() { JsonSerializer serializer = new JsonSerializer(); using ( StreamReader sr = new StreamReader( _propertiesFile ) ) { using ( JsonTextReader reader = new JsonTextReader( sr ) ) { PropertyEntry[] constraints = serializer.Deserialize( reader ); foreach ( PropertyEntry constraint in constraints ) { Constraints.AddSorted( constraint ); } } } } private void ClipboardPaste( object obj ) { string text = Engine.UIInvoker.GetClipboardText(); try { IEnumerable entries = JsonConvert.DeserializeObject>( text ); if ( entries == null ) { return; } foreach ( AutolootConstraintEntry entry in entries ) { if ( !SelectedItem.Constraints.Contains( entry ) ) { SelectedItem?.Constraints.Add( entry ); } } } catch ( Exception ) { // ignored } } private static void ClipboardCopy( object obj ) { if ( !( obj is IList entries ) ) { return; } string text = JsonConvert.SerializeObject( entries ); Engine.UIInvoker.SetClipboardText( text ); } public void OnCorpseEvent( int serial ) { if ( !Enabled ) { return; } lock ( _autolootLock ) { Item item = Engine.Items.GetItem( serial ); if ( item == null || item.ID != 0x2006 ) { return; } PacketWaitEntry we = Engine.PacketWaitEntries.Add( new PacketFilterInfo( 0x3C, new[] { PacketFilterConditions.IntAtPositionCondition( serial, 19 ) } ), PacketDirection.Incoming ); we.Lock.WaitOne( 2000 ); IEnumerable items = Engine.Items.GetItem( serial )?.Container?.GetItems(); if ( items == null ) { return; } if ( Engine.Features.HasFlag( FeatureFlags.AOS ) ) { Engine.SendPacketToServer( new BatchQueryProperties( items.Select( i => i.Serial ).ToArray() ) ); Thread.Sleep( 1000 ); } List lootItems = new List(); // If change logic, also change in DebugAutolootViewModel foreach ( AutolootEntry entry in Items ) { if ( !entry.Enabled ) { continue; } IEnumerable matchItems = AutolootHelpers.AutolootFilter( items, entry ); if ( matchItems == null ) { continue; } foreach ( Item matchItem in matchItems ) { if ( entry.Rehue ) { Engine.SendPacketToClient( new ContainerContentUpdate( matchItem.Serial, matchItem.ID, matchItem.Direction, matchItem.Count, matchItem.X, matchItem.Y, matchItem.Grid, matchItem.Owner, entry.RehueHue ) ); } if ( DisableInGuardzone && Engine.Player.GetRegion().Attributes.HasFlag( RegionAttributes.Guarded ) ) { continue; } if ( entry.Autoloot ) { lootItems.Add( matchItem ); } } } foreach ( Item lootItem in lootItems.Distinct() ) { int containerSerial = ContainerSerial; if ( containerSerial == 0 || Engine.Items.GetItem( containerSerial ) == null ) { containerSerial = Engine.Player.GetLayer( Layer.Backpack ); } UOC.SystemMessage( string.Format( Strings.Autolooting___0__, lootItem.Name ), 61 ); Task t = ActionPacketQueue.EnqueueDragDrop( lootItem.Serial, lootItem.Count, containerSerial, QueuePriority.Medium, true, true ); t.Wait( LOOT_TIMEOUT ); } } } private void RemoveConstraint( object obj ) { if ( !( obj is IEnumerable constraints ) ) { return; } foreach ( AutolootConstraintEntry constraintEntry in constraints.ToList() ) { SelectedItem?.Constraints.Remove( constraintEntry ); } } private void InsertConstraint( object obj ) { if ( !( obj is PropertyEntry propertyEntry ) ) { return; } List constraints = new List( SelectedItem.Constraints ) { new AutolootConstraintEntry { Property = propertyEntry } }; SelectedItem.Constraints = new ObservableCollection( constraints ); } private void DefineCustomProperties( object obj ) { Engine.UIInvoker.InvokeDialog( "CustomPropertiesWindow" ); Constraints.Clear(); LoadProperties(); LoadCustomProperties(); } private void ResetContainer( object obj ) { ContainerSerial = 0; } private async Task SetContainer( object arg ) { int serial = await UOC.GetTargeSerialAsync( Strings.Target_container___ ); if ( serial == 0 ) { UOC.SystemMessage( Strings.Invalid_or_unknown_object_id ); return; } ContainerSerial = serial; } private void InsertMatchAny( object obj ) { AutolootEntry entry = new AutolootEntry { Name = Strings.Any, ID = -1, Constraints = new ObservableCollection() }; Items.Add( entry ); } private async Task Insert( CancellationToken cancellationToken ) { int serial = await UOC.GetTargeSerialAsync( Strings.Target_object___ ); if ( serial == 0 ) { UOC.SystemMessage( Strings.Invalid_or_unknown_object_id ); return; } Item item = Engine.Items.GetItem( serial ); if ( item == null ) { UOC.SystemMessage( Strings.Cannot_find_item___ ); return; } AutolootEntry entry = new AutolootEntry { Name = TileData.GetStaticTile( item.ID ).Name, ID = item.ID, Constraints = new ObservableCollection() }; Items.Add( entry ); } private async Task Remove( object arg ) { if ( !( arg is AutolootEntry entry ) ) { return; } Items.Remove( entry ); await Task.CompletedTask; } private static async Task SelectHue( object obj ) { if ( !( obj is AutolootEntry entry ) ) { return; } int hue = await Engine.UIInvoker.GetHueAsync(); entry.RehueHue = hue; } } }