597 lines
20 KiB
C#
597 lines
20 KiB
C#
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<bool> _entrySelected;
|
|
private readonly IObservable<bool> _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<PropertyEntry> _constraints = new ObservableCollection<PropertyEntry>();
|
|
|
|
private int _containerSerial;
|
|
private ICommand _defineCustomPropertiesCommand;
|
|
|
|
private bool _disableInGuardzone;
|
|
private bool _enabled;
|
|
|
|
private ICommand _insertCommand;
|
|
private ICommand _insertConstraintCommand;
|
|
private ICommand _insertMatchAnyCommand;
|
|
|
|
private ObservableCollectionEx<AutolootEntry> _items = new ObservableCollectionEx<AutolootEntry>();
|
|
|
|
private ICommand _removeCommand;
|
|
private ICommand _removeConstraintCommand;
|
|
private ICommand _removeSingleConstraintCommand;
|
|
private RelayCommand _resetContainerCommand;
|
|
private AutolootEntry _selectedItem;
|
|
|
|
private ObservableCollection<AutolootConstraintEntry> _selectedProperties =
|
|
new ObservableCollection<AutolootConstraintEntry>();
|
|
|
|
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<PropertyEntry> 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<PropertyEntry>( InsertConstraint, _entrySelected ) );
|
|
|
|
public ICommand InsertMatchAnyCommand =>
|
|
_insertMatchAnyCommand ?? ( _insertMatchAnyCommand = new RelayCommand( InsertMatchAny, o => true ) );
|
|
|
|
public ObservableCollectionEx<AutolootEntry> Items
|
|
{
|
|
get => _items;
|
|
set => SetProperty( ref _items, value );
|
|
}
|
|
|
|
public ICommand RemoveCommand =>
|
|
_removeCommand ??
|
|
( _removeCommand = ReactiveCommand.CreateFromTask<AutolootEntry>( Remove, _entrySelected ) );
|
|
|
|
public ICommand RemoveConstraintCommand =>
|
|
_removeConstraintCommand ?? ( _removeConstraintCommand =
|
|
ReactiveCommand.Create<IEnumerable<AutolootConstraintEntry>>( RemoveConstraint, _itemSelected ) );
|
|
|
|
public ICommand RemoveSingleConstraintCommand =>
|
|
_removeSingleConstraintCommand ?? ( _removeSingleConstraintCommand =
|
|
ReactiveCommand.Create<AutolootConstraintEntry>( RemoveSingleConstraint, _itemSelected ) );
|
|
|
|
public ICommand ResetContainerCommand => _resetContainerCommand = new RelayCommand( ResetContainer, o => true );
|
|
|
|
public AutolootEntry SelectedItem
|
|
{
|
|
get => _selectedItem;
|
|
set => SetProperty( ref _selectedItem, value );
|
|
}
|
|
|
|
public ObservableCollection<AutolootConstraintEntry> 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<AutolootEntry>( 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<bool>() ?? true;
|
|
DisableInGuardzone = config["DisableInGuardzone"]?.ToObject<bool>() ?? false;
|
|
ContainerSerial = config["Container"]?.ToObject<int>() ?? 0;
|
|
|
|
if ( config["Items"] != null )
|
|
{
|
|
JToken items = config["Items"];
|
|
|
|
foreach ( JToken token in items )
|
|
{
|
|
AutolootEntry entry = new AutolootEntry
|
|
{
|
|
Name = token["Name"]?.ToObject<string>() ?? "Unknown",
|
|
ID = token["ID"]?.ToObject<int>() ?? 0,
|
|
Autoloot = token["Autoloot"]?.ToObject<bool>() ?? false,
|
|
Rehue = token["Rehue"]?.ToObject<bool>() ?? false,
|
|
RehueHue = token["RehueHue"]?.ToObject<int>() ?? 0,
|
|
Enabled = token["Enabled"]?.ToObject<bool>() ?? true
|
|
};
|
|
|
|
if ( token["Properties"] != null )
|
|
{
|
|
List<AutolootConstraintEntry> constraintsList = new List<AutolootConstraintEntry>();
|
|
|
|
// ReSharper disable once LoopCanBeConvertedToQuery
|
|
foreach ( JToken constraintToken in token["Properties"] )
|
|
{
|
|
string constraintName = constraintToken["Name"]?.ToObject<string>() ?? "Unknown";
|
|
|
|
PropertyEntry propertyEntry = Constraints.FirstOrDefault( c => c.Name == constraintName );
|
|
|
|
if ( propertyEntry == null )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AutolootConstraintEntry constraintObj = new AutolootConstraintEntry
|
|
{
|
|
Property = propertyEntry,
|
|
Operator = constraintToken["Operator"]?.ToObject<AutolootOperator>() ??
|
|
AutolootOperator.Equal,
|
|
Value = constraintToken["Value"]?.ToObject<int>() ?? 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<PropertyEntry[]>( 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<PropertyEntry[]>( reader );
|
|
|
|
foreach ( PropertyEntry constraint in constraints )
|
|
{
|
|
Constraints.AddSorted( constraint );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ClipboardPaste( object obj )
|
|
{
|
|
string text = Engine.UIInvoker.GetClipboardText();
|
|
|
|
try
|
|
{
|
|
IEnumerable<AutolootConstraintEntry> entries =
|
|
JsonConvert.DeserializeObject<IEnumerable<AutolootConstraintEntry>>( 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<AutolootConstraintEntry> 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<Item> 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<Item> lootItems = new List<Item>();
|
|
|
|
// If change logic, also change in DebugAutolootViewModel
|
|
|
|
foreach ( AutolootEntry entry in Items )
|
|
{
|
|
if ( !entry.Enabled )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
IEnumerable<Item> 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<AutolootConstraintEntry> constraints ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach ( AutolootConstraintEntry constraintEntry in constraints.ToList() )
|
|
{
|
|
SelectedItem?.Constraints.Remove( constraintEntry );
|
|
}
|
|
}
|
|
|
|
private void InsertConstraint( object obj )
|
|
{
|
|
if ( !( obj is PropertyEntry propertyEntry ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
List<AutolootConstraintEntry> constraints =
|
|
new List<AutolootConstraintEntry>( SelectedItem.Constraints )
|
|
{
|
|
new AutolootConstraintEntry { Property = propertyEntry }
|
|
};
|
|
|
|
SelectedItem.Constraints = new ObservableCollection<AutolootConstraintEntry>( constraints );
|
|
}
|
|
|
|
private void DefineCustomProperties( object obj )
|
|
{
|
|
Engine.UIInvoker.InvokeDialog<CustomPropertiesViewModel>( "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<AutolootConstraintEntry>()
|
|
};
|
|
|
|
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<AutolootConstraintEntry>()
|
|
};
|
|
|
|
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;
|
|
}
|
|
}
|
|
} |