first commit
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ClassicAssist.UI.Misc
|
||||
{
|
||||
public class EnumDescriptionTypeConverter : EnumConverter
|
||||
{
|
||||
public EnumDescriptionTypeConverter( Type type ) : base( type )
|
||||
{
|
||||
}
|
||||
|
||||
public override object ConvertTo( ITypeDescriptorContext context, CultureInfo culture, object value,
|
||||
Type destinationType )
|
||||
{
|
||||
if ( destinationType != typeof( string ) )
|
||||
{
|
||||
return base.ConvertTo( context, culture, value, destinationType );
|
||||
}
|
||||
|
||||
if ( value == null )
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
FieldInfo fi = value.GetType().GetField( value.ToString() );
|
||||
|
||||
if ( fi != null )
|
||||
{
|
||||
DescriptionAttribute[] attributes =
|
||||
(DescriptionAttribute[]) fi.GetCustomAttributes( typeof( DescriptionAttribute ), false );
|
||||
return attributes.Length > 0 && !string.IsNullOrEmpty( attributes[0].Description )
|
||||
? attributes[0].Description
|
||||
: value.ToString();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ClassicAssist.UI.Misc
|
||||
{
|
||||
public sealed class ObservableCollectionEx<T> : ObservableCollection<T> where T : INotifyPropertyChanged
|
||||
{
|
||||
// this collection also reacts to changes in its components' properties
|
||||
|
||||
public ObservableCollectionEx()
|
||||
{
|
||||
CollectionChanged += ObservableCollectionEx_CollectionChanged;
|
||||
}
|
||||
|
||||
private void ObservableCollectionEx_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
|
||||
{
|
||||
switch ( e.Action )
|
||||
{
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
|
||||
foreach ( T item in e.OldItems )
|
||||
{
|
||||
//Removed items
|
||||
item.PropertyChanged -= EntityViewModelPropertyChanged;
|
||||
}
|
||||
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
|
||||
foreach ( T item in e.NewItems )
|
||||
{
|
||||
//Added items
|
||||
item.PropertyChanged += EntityViewModelPropertyChanged;
|
||||
}
|
||||
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public void EntityViewModelPropertyChanged( object sender, PropertyChangedEventArgs e )
|
||||
{
|
||||
//This will get called when the property of an object inside the collection changes - note you must make it a 'reset' - dunno why
|
||||
NotifyCollectionChangedEventArgs args =
|
||||
new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset );
|
||||
|
||||
if ( args.NewStartingIndex < 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnCollectionChanged( args );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#region License
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace ClassicAssist.UI.Misc
|
||||
{
|
||||
public class OptionsBindingAttribute : Attribute
|
||||
{
|
||||
public string Property { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using ClassicAssist.UO.Network.PacketFilter;
|
||||
|
||||
namespace ClassicAssist.UI.Misc
|
||||
{
|
||||
public class PacketEntry
|
||||
{
|
||||
public byte[] Data { get; set; }
|
||||
public DateTime DateTime { get; set; } = DateTime.Now;
|
||||
public PacketDirection Direction { get; set; }
|
||||
public int Length => Data.Length;
|
||||
public string Title { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace ClassicAssist.UI.Models
|
||||
{
|
||||
public class ObjectInspectorData
|
||||
{
|
||||
public string Category { get; set; }
|
||||
public bool IsExpanded { get; set; } = true;
|
||||
public string Name { get; set; }
|
||||
public Action<object> OnDoubleClick { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reflection;
|
||||
using System.Timers;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Network;
|
||||
using ClassicAssist.UO.Network.PacketFilter;
|
||||
using ClassicAssist.UO.Network.Packets;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class AboutControlViewModel : BaseViewModel
|
||||
{
|
||||
private ICommand _checkForUpdatesCommand;
|
||||
private bool _connected;
|
||||
private DateTime _connectedTime;
|
||||
private int _itemCount;
|
||||
private int _lastTargetSerial;
|
||||
private double _latency;
|
||||
private ICommand _launchHomepageCommand;
|
||||
private int _mobileCount;
|
||||
private ICommand _openPayPalCommand;
|
||||
private Timer _pingTimer;
|
||||
private string _playerName;
|
||||
private int _playerSerial;
|
||||
private string _playerStatus;
|
||||
private string _shardFeatures;
|
||||
private string _shardName = "Unknown";
|
||||
private ICommand _showItemsCommand;
|
||||
private Timer _timer;
|
||||
|
||||
public AboutControlViewModel()
|
||||
{
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
Version version = assembly.GetName().Version;
|
||||
|
||||
Version = $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
|
||||
BuildDate = $"{GetBuildDateTime( assembly ).ToLongDateString()}";
|
||||
|
||||
Engine.ConnectedEvent += OnConnectedEvent;
|
||||
Engine.DisconnectedEvent += OnDisconnectedEvent;
|
||||
Engine.PlayerInitializedEvent += PlayerInitializedEvent;
|
||||
Engine.Items.CollectionChanged += ItemsOnCollectionChanged;
|
||||
Engine.Mobiles.CollectionChanged += MobilesOnCollectionChanged;
|
||||
|
||||
IncomingPacketHandlers.MobileUpdatedEvent += OnMobileUpdatedEvent;
|
||||
}
|
||||
|
||||
public string BuildDate { get; set; }
|
||||
|
||||
public ICommand CheckForUpdatesCommand =>
|
||||
_checkForUpdatesCommand ?? ( _checkForUpdatesCommand = new RelayCommand( CheckForUpdates, o => true ) );
|
||||
|
||||
public bool Connected
|
||||
{
|
||||
get => _connected;
|
||||
set => SetProperty( ref _connected, value );
|
||||
}
|
||||
|
||||
public DateTime ConnectedTime
|
||||
{
|
||||
get => _connectedTime;
|
||||
set => SetProperty( ref _connectedTime, value );
|
||||
}
|
||||
|
||||
public int ItemCount
|
||||
{
|
||||
get => _itemCount;
|
||||
set => SetProperty( ref _itemCount, value );
|
||||
}
|
||||
|
||||
public int LastTargetSerial
|
||||
{
|
||||
get => _lastTargetSerial;
|
||||
set => SetProperty( ref _lastTargetSerial, value );
|
||||
}
|
||||
|
||||
public double Latency
|
||||
{
|
||||
get => _latency;
|
||||
set => SetProperty( ref _latency, value );
|
||||
}
|
||||
|
||||
public ICommand LaunchHomepageCommand =>
|
||||
_launchHomepageCommand ?? ( _launchHomepageCommand = new RelayCommand( LaunchHomepage, o => true ) );
|
||||
|
||||
public int MobileCount
|
||||
{
|
||||
get => _mobileCount;
|
||||
set => SetProperty( ref _mobileCount, value );
|
||||
}
|
||||
|
||||
public ICommand OpenPayPalCommand =>
|
||||
_openPayPalCommand ?? ( _openPayPalCommand = new RelayCommand( OpenPayPal, o => true ) );
|
||||
|
||||
public string PlayerName
|
||||
{
|
||||
get => _playerName;
|
||||
set => SetProperty( ref _playerName, value );
|
||||
}
|
||||
|
||||
public int PlayerSerial
|
||||
{
|
||||
get => _playerSerial;
|
||||
set => SetProperty( ref _playerSerial, value );
|
||||
}
|
||||
|
||||
public string PlayerStatus
|
||||
{
|
||||
get => _playerStatus;
|
||||
set => SetProperty( ref _playerStatus, value );
|
||||
}
|
||||
|
||||
public string Product { get; } = Strings.ProductName;
|
||||
|
||||
public string ShardFeatures
|
||||
{
|
||||
get => _shardFeatures;
|
||||
set => SetProperty( ref _shardFeatures, value );
|
||||
}
|
||||
|
||||
public string ShardName
|
||||
{
|
||||
get => _shardName;
|
||||
set => SetProperty( ref _shardName, value );
|
||||
}
|
||||
|
||||
public ICommand ShowItemsCommand =>
|
||||
_showItemsCommand ?? ( _showItemsCommand = ReactiveCommand.Create( ShowItems,
|
||||
this.WhenAnyValue( e => e.Connected ).ObserveOn( RxApp.MainThreadScheduler ) ) );
|
||||
|
||||
public string Version { get; set; }
|
||||
|
||||
private static void OpenPayPal( object obj )
|
||||
{
|
||||
Process.Start( "https://www.paypal.me/reeeetus" );
|
||||
}
|
||||
|
||||
private void LastTargetChangedEvent( int serial )
|
||||
{
|
||||
LastTargetSerial = serial;
|
||||
}
|
||||
|
||||
private void OnMobileUpdatedEvent( Mobile mobile )
|
||||
{
|
||||
if ( mobile.Serial == Engine.Player?.Serial )
|
||||
{
|
||||
PlayerInitializedEvent( Engine.Player );
|
||||
}
|
||||
}
|
||||
|
||||
private static void LaunchHomepage( object obj )
|
||||
{
|
||||
Process.Start( "https://github.com/Reetus/ClassicAssist" );
|
||||
}
|
||||
|
||||
private void MobilesOnCollectionChanged( int totalcount, bool added, Mobile[] mobiles )
|
||||
{
|
||||
MobileCount = totalcount;
|
||||
}
|
||||
|
||||
private void ItemsOnCollectionChanged( int totalcount, bool added, Item[] items )
|
||||
{
|
||||
ItemCount = Engine.Items.GetTotalItemCount();
|
||||
}
|
||||
|
||||
private static void ShowItems()
|
||||
{
|
||||
Item[] e = ItemCollection.GetAllItems( Engine.Items.GetItems() );
|
||||
|
||||
Engine.UIInvoker?.Invoke( "EntityCollectionViewer", new[] { (object) new ItemCollection( 0 ) { e } } );
|
||||
}
|
||||
|
||||
private void PlayerInitializedEvent( PlayerMobile player )
|
||||
{
|
||||
PlayerSerial = player.Serial;
|
||||
PlayerName = player.Name;
|
||||
PlayerStatus = player.Status.ToString();
|
||||
ShardFeatures = Engine.Features.ToString();
|
||||
player.LastTargetChangedEvent += LastTargetChangedEvent;
|
||||
player.MobileStatusUpdated += OnMobileStatusUpdated;
|
||||
ShardName = Engine.CurrentShard?.Name ?? "Unknown";
|
||||
}
|
||||
|
||||
private void OnMobileStatusUpdated( MobileStatus oldstatus, MobileStatus newstatus )
|
||||
{
|
||||
PlayerStatus = newstatus.ToString();
|
||||
}
|
||||
|
||||
private void OnDisconnectedEvent()
|
||||
{
|
||||
Connected = false;
|
||||
|
||||
_timer?.Stop();
|
||||
}
|
||||
|
||||
private static void CheckForUpdates( object obj )
|
||||
{
|
||||
string updaterPath = Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory,
|
||||
"ClassicAssist.Updater.exe" );
|
||||
|
||||
Version version = null;
|
||||
|
||||
if ( System.Version.TryParse(
|
||||
FileVersionInfo.GetVersionInfo( Assembly.GetExecutingAssembly().Location ).ProductVersion,
|
||||
out Version v ) )
|
||||
{
|
||||
version = v;
|
||||
}
|
||||
|
||||
if ( !File.Exists( updaterPath ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessStartInfo psi = new ProcessStartInfo( updaterPath,
|
||||
$"--pid {Process.GetCurrentProcess().Id} --path {Engine.StartupPath}" + ( version != null
|
||||
? $" --version {version}"
|
||||
: "" ) ) { UseShellExecute = false };
|
||||
|
||||
Process.Start( psi );
|
||||
}
|
||||
|
||||
private void OnConnectedEvent()
|
||||
{
|
||||
Connected = true;
|
||||
ConnectedTime = DateTime.Now;
|
||||
|
||||
_timer = new Timer( 1000 ) { AutoReset = true };
|
||||
_timer.Elapsed += ( sender, args ) => { NotifyPropertyChanged( nameof( ConnectedTime ) ); };
|
||||
_timer.Start();
|
||||
|
||||
_pingTimer = new Timer( 3000 ) { AutoReset = true };
|
||||
_pingTimer.Elapsed += ( sender, args ) => PingServer();
|
||||
_pingTimer.Start();
|
||||
}
|
||||
|
||||
private void PingServer()
|
||||
{
|
||||
_pingTimer.Interval = 30000;
|
||||
|
||||
Random random = new Random();
|
||||
|
||||
byte value = (byte) random.Next( 1, byte.MaxValue );
|
||||
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
PacketWaitEntry we = Engine.PacketWaitEntries.Add(
|
||||
new PacketFilterInfo( 0x73, new[] { new PacketFilterCondition( 1, new[] { value }, 1 ) } ),
|
||||
PacketDirection.Incoming, true );
|
||||
|
||||
Engine.SendPacketToServer( new Ping( value ) );
|
||||
|
||||
bool result = we.Lock.WaitOne( 5000 );
|
||||
|
||||
sw.Stop();
|
||||
|
||||
if ( result )
|
||||
{
|
||||
Latency = sw.ElapsedMilliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
internal static DateTime GetBuildDateTime( Assembly assembly )
|
||||
{
|
||||
try
|
||||
{
|
||||
var location = assembly?.Location;
|
||||
if ( string.IsNullOrEmpty( location ) )
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
|
||||
var productVersion = FileVersionInfo.GetVersionInfo( location ).ProductVersion;
|
||||
if ( System.Version.TryParse( productVersion, out Version version ) && version != null )
|
||||
{
|
||||
DateTime buildDateTime =
|
||||
new DateTime( 2020, 7, 3 ).Add( new TimeSpan( TimeSpan.TicksPerDay * version.Build ) );
|
||||
return buildDateTime;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Counters;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.Shared.UO;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Agents
|
||||
{
|
||||
public class CountersTabViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private ICommand _insertEntryCommand;
|
||||
private ObservableCollection<CountersAgentEntry> _items = new ObservableCollection<CountersAgentEntry>();
|
||||
private ICommand _recountCommand;
|
||||
private ICommand _removeEntryCommand;
|
||||
private CountersAgentEntry _selectedItem;
|
||||
private bool _warn = true;
|
||||
private int _warnAmount;
|
||||
|
||||
public CountersTabViewModel()
|
||||
{
|
||||
CountersManager manager = CountersManager.GetInstance();
|
||||
|
||||
manager.Items = Items;
|
||||
manager.RecountAll = Recount;
|
||||
}
|
||||
|
||||
public ICommand InsertEntryCommand =>
|
||||
_insertEntryCommand ?? ( _insertEntryCommand = new RelayCommandAsync( InsertEntry, o => true ) );
|
||||
|
||||
public ObservableCollection<CountersAgentEntry> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ICommand RecountCommand => _recountCommand ?? ( _recountCommand = ReactiveCommand.Create( Recount ) );
|
||||
|
||||
public ICommand RemoveEntryCommand =>
|
||||
_removeEntryCommand ?? ( _removeEntryCommand = ReactiveCommand.Create<CountersAgentEntry>( RemoveEntry,
|
||||
this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) );
|
||||
|
||||
public CountersAgentEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
public bool Warn
|
||||
{
|
||||
get => _warn;
|
||||
set => SetProperty( ref _warn, value );
|
||||
}
|
||||
|
||||
public int WarnAmount
|
||||
{
|
||||
get => _warnAmount;
|
||||
set => SetProperty( ref _warnAmount, value );
|
||||
}
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
JObject options = new JObject { { "Warn", Warn }, { "WarnAmount", WarnAmount } };
|
||||
|
||||
JArray items = new JArray();
|
||||
|
||||
foreach ( CountersAgentEntry entry in Items )
|
||||
{
|
||||
JObject entryObj = new JObject
|
||||
{
|
||||
{ "Name", entry.Name }, { "Graphic", entry.Graphic }, { "Color", entry.Color }
|
||||
};
|
||||
|
||||
items.Add( entryObj );
|
||||
}
|
||||
|
||||
options.Add( "Items", items );
|
||||
|
||||
json?.Add( "Counters", options );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
if ( json?["Counters"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Warn = json["Counters"]["Warn"]?.ToObject<bool>() ?? false;
|
||||
WarnAmount = json["Counters"]["WarnAmount"]?.ToObject<int>() ?? 0;
|
||||
|
||||
foreach ( JToken token in json["Counters"]["Items"] )
|
||||
{
|
||||
if ( token != null )
|
||||
{
|
||||
Items.Add( new CountersAgentEntry
|
||||
{
|
||||
Name = token["Name"].ToObject<string>() ?? "Unknown",
|
||||
Graphic = token["Graphic"].ToObject<int>(),
|
||||
Color = token["Color"].ToObject<int>()
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveEntry( object obj )
|
||||
{
|
||||
if ( !( obj is CountersAgentEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Items.Remove( entry );
|
||||
}
|
||||
|
||||
private void Recount()
|
||||
{
|
||||
foreach ( CountersAgentEntry item in Items )
|
||||
{
|
||||
int count = item.Count;
|
||||
|
||||
item.Recount();
|
||||
|
||||
if ( Warn && item.Count <= WarnAmount && count > WarnAmount )
|
||||
{
|
||||
Commands.SystemMessage(
|
||||
string.Format( Strings.Counter___0___amount_is_now__1____, item.Name, item.Count ), 61 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InsertEntry( object arg )
|
||||
{
|
||||
int serial = await Commands.GetTargeSerialAsync( Strings.Target_object___ );
|
||||
|
||||
if ( serial == 0 )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Invalid_or_unknown_object_id );
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = Engine.Items.GetItem( serial );
|
||||
|
||||
if ( item == null )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Cannot_find_item___ );
|
||||
return;
|
||||
}
|
||||
|
||||
string name = TileData.GetStaticTile( item.ID ).Name;
|
||||
|
||||
if ( string.IsNullOrEmpty( name ) )
|
||||
{
|
||||
name = item.Name;
|
||||
}
|
||||
|
||||
CountersAgentEntry entry = new CountersAgentEntry { Name = name, Graphic = item.ID, Color = item.Hue };
|
||||
|
||||
entry.Recount();
|
||||
|
||||
Items.Add( entry );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Dress;
|
||||
using ClassicAssist.Data.Hotkeys;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.Shared.UO;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Agents
|
||||
{
|
||||
public class DressTabViewModel : HotkeyEntryViewModel<DressAgentEntry>, ISettingProvider
|
||||
{
|
||||
private readonly IObservable<bool> _entrySelected;
|
||||
private readonly DressManager _manager;
|
||||
|
||||
private readonly Layer[] _validLayers =
|
||||
{
|
||||
Layer.Arms, Layer.Bracelet, Layer.Cloak, Layer.Earrings, Layer.Gloves, Layer.Helm, Layer.InnerLegs,
|
||||
Layer.InnerTorso, Layer.MiddleTorso, Layer.Neck, Layer.OneHanded, Layer.OuterLegs, Layer.OuterTorso,
|
||||
Layer.Pants, Layer.Ring, Layer.Shirt, Layer.Shoes, Layer.Talisman, Layer.TwoHanded, Layer.Waist
|
||||
};
|
||||
|
||||
private ICommand _addDressItemCommand;
|
||||
private ICommand _changeDressType;
|
||||
private ICommand _clearDressItemsCommand;
|
||||
private ICommand _dressAllItemsCommand;
|
||||
private ICommand _importItemsCommand;
|
||||
private bool _isDressingOrUndressing;
|
||||
private readonly IObservable<bool> _itemSelected;
|
||||
private bool _moveConflictingItems;
|
||||
private ICommand _newDressEntryCommand;
|
||||
private ICommand _removeDressEntryCommand;
|
||||
private ICommand _removeDressItemCommand;
|
||||
private DressAgentItem _selectedDressItem;
|
||||
private DressAgentEntry _selectedItem;
|
||||
private ICommand _setUndressContainerCommand;
|
||||
private ICommand _undressAllItemsCommand;
|
||||
private ICommand _undressItemsCommand;
|
||||
private bool _useUo3DPackets;
|
||||
|
||||
public DressTabViewModel() : base( Strings.Dress )
|
||||
{
|
||||
_manager = DressManager.GetInstance();
|
||||
|
||||
_manager.Items = Items;
|
||||
|
||||
_entrySelected = this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null );
|
||||
_itemSelected = this.WhenAnyValue( e => e.SelectedDressItem, selector: e => e != null );
|
||||
}
|
||||
|
||||
public ICommand AddDressItemCommand =>
|
||||
_addDressItemCommand ?? ( _addDressItemCommand = new RelayCommandAsync( AddDressItem, o => true ) );
|
||||
|
||||
//TODO UI
|
||||
public ICommand ChangeDressTypeCommand =>
|
||||
_changeDressType ??
|
||||
( _changeDressType = new RelayCommand( ChangeDressType, o => SelectedDressItem != null ) );
|
||||
|
||||
public ICommand ClearDressItemsCommand =>
|
||||
_clearDressItemsCommand ?? ( _clearDressItemsCommand =
|
||||
ReactiveCommand.Create<DressAgentEntry>( ClearDressItems, _entrySelected ) );
|
||||
|
||||
public ICommand DressAllItemsCommand =>
|
||||
_dressAllItemsCommand ?? ( _dressAllItemsCommand =
|
||||
ReactiveCommand.CreateFromTask<DressAgentEntry>( DressAllItems,
|
||||
_entrySelected.Merge( this.WhenAnyValue( e => e.IsDressingOrUndressing, e => !e ) ) ) );
|
||||
|
||||
public ICommand ImportItemsCommand =>
|
||||
_importItemsCommand ?? ( _importItemsCommand =
|
||||
ReactiveCommand.Create<DressAgentEntry>( ImportItems, _entrySelected ) );
|
||||
|
||||
public bool IsDressingOrUndressing
|
||||
{
|
||||
get => _isDressingOrUndressing;
|
||||
set => SetProperty( ref _isDressingOrUndressing, value );
|
||||
}
|
||||
|
||||
public bool MoveConflictingItems
|
||||
{
|
||||
get => _moveConflictingItems;
|
||||
set => SetProperty( ref _moveConflictingItems, value );
|
||||
}
|
||||
|
||||
public ICommand NewDressEntryCommand =>
|
||||
_newDressEntryCommand ?? ( _newDressEntryCommand = new RelayCommand( NewDressEntry, o => true ) );
|
||||
|
||||
public ICommand RemoveDressEntryCommand =>
|
||||
_removeDressEntryCommand ?? ( _removeDressEntryCommand =
|
||||
ReactiveCommand.Create<DressAgentEntry>( RemoveDressEntry, _entrySelected ) );
|
||||
|
||||
public ICommand RemoveDressItemCommand =>
|
||||
_removeDressItemCommand ?? ( _removeDressItemCommand =
|
||||
ReactiveCommand.Create<DressAgentItem>( RemoveDressItem, _itemSelected ) );
|
||||
|
||||
public DressAgentItem SelectedDressItem
|
||||
{
|
||||
get => _selectedDressItem;
|
||||
set => SetProperty( ref _selectedDressItem, value );
|
||||
}
|
||||
|
||||
public DressAgentEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
public ICommand SetUndressContainerCommand =>
|
||||
_setUndressContainerCommand ?? ( _setUndressContainerCommand =
|
||||
ReactiveCommand.CreateFromTask<DressAgentEntry>( SetUndressContainer, _entrySelected ) );
|
||||
|
||||
public ICommand UndressAllItemsCommand =>
|
||||
_undressAllItemsCommand ?? ( _undressAllItemsCommand = ReactiveCommand.CreateFromTask( UndressAllItems,
|
||||
this.WhenAnyValue( e => e.IsDressingOrUndressing, e => !e ) ) );
|
||||
|
||||
public ICommand UndressItemsCommand =>
|
||||
_undressItemsCommand ?? ( _undressItemsCommand =
|
||||
ReactiveCommand.CreateFromTask<DressAgentEntry>( UndressItems,
|
||||
this.WhenAnyValue( e => e.IsDressingOrUndressing, e => !e ) ) );
|
||||
|
||||
public bool UseUO3DPackets
|
||||
{
|
||||
get => _useUo3DPackets;
|
||||
set
|
||||
{
|
||||
SetProperty( ref _useUo3DPackets, value );
|
||||
|
||||
if ( _manager != null )
|
||||
{
|
||||
_manager.UseUO3DPackets = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
JObject dress = new JObject
|
||||
{
|
||||
{
|
||||
"Options",
|
||||
new JObject { ["MoveConflictingItems"] = MoveConflictingItems, ["UseUO3DPackets"] = UseUO3DPackets }
|
||||
}
|
||||
};
|
||||
|
||||
JArray dressEntries = new JArray();
|
||||
|
||||
foreach ( DressAgentEntry dae in Items )
|
||||
{
|
||||
JObject djson = new JObject();
|
||||
|
||||
SetJsonValue( djson, "Name", dae.Name );
|
||||
SetJsonValue( djson, "UndressContainer", dae.UndressContainer );
|
||||
SetJsonValue( djson, "PassToUO", dae.PassToUO );
|
||||
SetJsonValue( djson, "Keys", dae.Hotkey.ToJObject() );
|
||||
|
||||
JArray items = new JArray();
|
||||
|
||||
if ( dae.Items != null )
|
||||
{
|
||||
foreach ( DressAgentItem dressAgentItem in dae.Items )
|
||||
{
|
||||
JObject item = new JObject
|
||||
{
|
||||
{ "Layer", (int) dressAgentItem.Layer },
|
||||
{ "Serial", dressAgentItem.Serial },
|
||||
{ "ID", dressAgentItem.ID },
|
||||
{ "Type", (int) dressAgentItem.Type }
|
||||
};
|
||||
|
||||
items.Add( item );
|
||||
}
|
||||
}
|
||||
|
||||
djson.Add( "Items", items );
|
||||
dressEntries.Add( djson );
|
||||
}
|
||||
|
||||
dress.Add( "Entries", dressEntries );
|
||||
json?.Add( "Dress", dress );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
if ( json?["Dress"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JToken dress = json["Dress"];
|
||||
|
||||
MoveConflictingItems = GetJsonValue( dress["Options"], "MoveConflictingItems", false );
|
||||
UseUO3DPackets = _manager.UseUO3DPackets = GetJsonValue( dress["Options"], "UseUO3DPackets", false );
|
||||
|
||||
foreach ( JToken entry in dress["Entries"] )
|
||||
{
|
||||
DressAgentEntry dae = new DressAgentEntry
|
||||
{
|
||||
Name = GetJsonValue( entry, "Name", string.Empty ),
|
||||
UndressContainer = GetJsonValue( entry, "UndressContainer", 0 ),
|
||||
PassToUO = GetJsonValue( entry, "PassToUO", true ),
|
||||
Hotkey = new ShortcutKeys( entry["Keys"] )
|
||||
};
|
||||
|
||||
dae.Action = async hks => await DressAllItems( dae );
|
||||
|
||||
List<DressAgentItem> items = new List<DressAgentItem>();
|
||||
|
||||
if ( entry["Items"] != null )
|
||||
{
|
||||
items.AddRange( entry["Items"].Select( itemEntry => new DressAgentItem
|
||||
{
|
||||
Layer = GetJsonValue( itemEntry, "Layer", Layer.Invalid ),
|
||||
Serial = GetJsonValue( itemEntry, "Serial", 0 ),
|
||||
ID = GetJsonValue( itemEntry, "ID", -1 ),
|
||||
Type = GetJsonValue( itemEntry, "Type", DressAgentItemType.Serial )
|
||||
} ) );
|
||||
}
|
||||
|
||||
dae.Items = new List<DressAgentItem>( items );
|
||||
|
||||
Items.Add( dae );
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UndressItems( object arg )
|
||||
{
|
||||
if ( !( arg is DressAgentEntry dae ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IsDressingOrUndressing = true;
|
||||
|
||||
await dae.Undress();
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsDressingOrUndressing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SetUndressContainer( object obj )
|
||||
{
|
||||
if ( !( obj is DressAgentEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int serial = await Commands.GetTargeSerialAsync( Strings.Select_undress_container___ );
|
||||
|
||||
if ( serial <= 0 )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Invalid_container___ );
|
||||
return;
|
||||
}
|
||||
|
||||
entry.UndressContainer = serial;
|
||||
}
|
||||
|
||||
private static void ClearDressItems( object obj )
|
||||
{
|
||||
if ( !( obj is DressAgentEntry dae ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dae.Items = new List<DressAgentItem>();
|
||||
}
|
||||
|
||||
private void RemoveDressItem( object obj )
|
||||
{
|
||||
if ( !( obj is DressAgentItem removeItem ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !SelectedItem.Items.Contains( removeItem ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<DressAgentItem> list = SelectedItem.Items.ToList();
|
||||
list.Remove( removeItem );
|
||||
SelectedItem.Items = list;
|
||||
}
|
||||
|
||||
private static async Task AddDressItem( object arg )
|
||||
{
|
||||
if ( !( arg is DressAgentEntry dae ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int serial = await Commands.GetTargeSerialAsync( Strings.Target_clothing_item___ );
|
||||
|
||||
Item item = Engine.Items.GetItem( serial );
|
||||
|
||||
if ( item == null )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Cannot_find_item___ );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( item.Layer == Layer.Invalid )
|
||||
{
|
||||
Commands.SystemMessage( Strings.The_item_needs_to_be_equipped___ );
|
||||
return;
|
||||
}
|
||||
|
||||
dae.AddOrReplaceDressItem( item );
|
||||
}
|
||||
|
||||
private async Task UndressAllItems( CancellationToken cancellationToken )
|
||||
{
|
||||
IsDressingOrUndressing = true;
|
||||
await _manager.UndressAll( cancellationToken );
|
||||
IsDressingOrUndressing = false;
|
||||
}
|
||||
|
||||
private void NewDressEntry( object obj )
|
||||
{
|
||||
int count = Items.Count;
|
||||
|
||||
DressAgentEntry dae =
|
||||
new DressAgentEntry { Name = $"Dress-{count + 1}", Items = new List<DressAgentItem>() };
|
||||
|
||||
dae.Action = async hks => await DressAllItems( dae );
|
||||
|
||||
Items.Add( dae );
|
||||
}
|
||||
|
||||
private void RemoveDressEntry( object obj )
|
||||
{
|
||||
if ( !( obj is DressAgentEntry dae ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dae.Hotkey = ShortcutKeys.Default;
|
||||
Items.Remove( dae );
|
||||
}
|
||||
|
||||
private async Task DressAllItems( object obj )
|
||||
{
|
||||
if ( !( obj is DressAgentEntry dae ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsDressingOrUndressing = true;
|
||||
await _manager.DressAllItems( dae, MoveConflictingItems );
|
||||
IsDressingOrUndressing = false;
|
||||
}
|
||||
|
||||
private void ImportItems( object obj )
|
||||
{
|
||||
if ( !( obj is DressAgentEntry dae ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_manager.ImportItems( dae );
|
||||
}
|
||||
|
||||
public bool IsValidLayer( Layer layer )
|
||||
{
|
||||
return _validLayers.Any( l => l == layer );
|
||||
}
|
||||
|
||||
private static void ChangeDressType( object obj )
|
||||
{
|
||||
if ( !( obj is DressAgentItem dai ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dai.Type = dai.Type == DressAgentItemType.Serial ? DressAgentItemType.ID : DressAgentItemType.Serial;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Friends;
|
||||
using ClassicAssist.Data.Macros.Commands;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Agents
|
||||
{
|
||||
public class FriendsTabViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private ICommand _addFriendCommand;
|
||||
private ICommand _changeRehueOption;
|
||||
private Options _options;
|
||||
private ICommand _removeFriendCommand;
|
||||
private FriendEntry _selectedItem;
|
||||
private ICommand _selectHueCommand;
|
||||
|
||||
public ICommand AddFriendCommand =>
|
||||
_addFriendCommand ?? ( _addFriendCommand = new RelayCommandAsync( AddFriend, o => true ) );
|
||||
|
||||
public ICommand ChangeRehueOption =>
|
||||
_changeRehueOption ?? ( _changeRehueOption = new RelayCommand( ChangeRehue, o => true ) );
|
||||
|
||||
public Options Options
|
||||
{
|
||||
get => _options;
|
||||
set => SetProperty( ref _options, value );
|
||||
}
|
||||
|
||||
public ICommand RemoveFriendCommand =>
|
||||
_removeFriendCommand ?? ( _removeFriendCommand = ReactiveCommand.CreateFromTask<FriendEntry>( RemoveFriend,
|
||||
this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) );
|
||||
//new RelayCommandAsync( RemoveFriend, o => SelectedItem != null ) );
|
||||
|
||||
public FriendEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
public ICommand SelectHueCommand =>
|
||||
_selectHueCommand ?? ( _selectHueCommand = ReactiveCommand.CreateFromTask( SelectHue,
|
||||
this.WhenAnyValue( e => e.Options.RehueFriends, selector: e => e ) ) );
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
JObject config = new JObject
|
||||
{
|
||||
["IncludePartyMembersInFriends"] = Options.IncludePartyMembersInFriends,
|
||||
["PreventAttackingFriendsInWarMode"] = Options.PreventAttackingFriendsInWarMode,
|
||||
["PreventTargetingFriendsWithHarmful"] = Options.PreventTargetingFriendsWithHarmful
|
||||
};
|
||||
|
||||
JArray friends = new JArray();
|
||||
|
||||
foreach ( FriendEntry friend in Options.Friends )
|
||||
{
|
||||
friends.Add( new JObject { { "Name", friend.Name }, { "Serial", friend.Serial } } );
|
||||
}
|
||||
|
||||
config.Add( "Items", friends );
|
||||
|
||||
json?.Add( "Friends", config );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Options = options;
|
||||
Options.Friends.Clear();
|
||||
|
||||
if ( json?["Friends"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JToken config = json["Friends"];
|
||||
|
||||
Options.IncludePartyMembersInFriends = config["IncludePartyMembersInFriends"]?.ToObject<bool>() ?? true;
|
||||
Options.PreventAttackingFriendsInWarMode =
|
||||
config["PreventAttackingFriendsInWarMode"]?.ToObject<bool>() ?? true;
|
||||
Options.PreventTargetingFriendsWithHarmful =
|
||||
config["PreventTargetingFriendsWithHarmful"]?.ToObject<bool>() ?? false;
|
||||
|
||||
if ( config["Items"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( JToken token in config["Items"] )
|
||||
{
|
||||
Options.Friends.Add( new FriendEntry
|
||||
{
|
||||
Name = token["Name"].ToObject<string>(), Serial = token["Serial"].ToObject<int>()
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
private static void ChangeRehue( object obj )
|
||||
{
|
||||
MainCommands.Resync();
|
||||
}
|
||||
|
||||
private static async Task SelectHue( CancellationToken token )
|
||||
{
|
||||
int hue = await Engine.UIInvoker.GetHueAsync();
|
||||
|
||||
Options.CurrentOptions.RehueFriendsHue = hue;
|
||||
MainCommands.Resync();
|
||||
}
|
||||
|
||||
private static async Task AddFriend( object arg )
|
||||
{
|
||||
await Task.Run( () => MobileCommands.AddFriend() );
|
||||
}
|
||||
|
||||
private static Task RemoveFriend( object arg )
|
||||
{
|
||||
if ( !( arg is FriendEntry fe ) )
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
MobileCommands.RemoveFriend( fe.Serial );
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Hotkeys;
|
||||
using ClassicAssist.Data.Organizer;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.Shared.UO;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Agents
|
||||
{
|
||||
public class OrganizerTabViewModel : HotkeyEntryViewModel<OrganizerEntry>, ISettingProvider
|
||||
{
|
||||
private readonly OrganizerManager _manager;
|
||||
private ReactiveCommand<OrganizerEntry, Unit> _insertItemCommand;
|
||||
private bool _isOrganizing;
|
||||
private ICommand _newOrganizerEntryCommand;
|
||||
private ReactiveCommand<OrganizerEntry, Unit> _organizeCommand;
|
||||
private ReactiveCommand<OrganizerItem, Unit> _removeItemCommand;
|
||||
private ReactiveCommand<OrganizerEntry, Unit> _removeOrganizerAgentEntryCommand;
|
||||
private OrganizerEntry _selectedItem;
|
||||
private OrganizerItem _selectedOrganizerItem;
|
||||
private ReactiveCommand<OrganizerEntry, Unit> _setContainersCommand;
|
||||
|
||||
public OrganizerTabViewModel() : base( Strings.Organizer )
|
||||
{
|
||||
_manager = OrganizerManager.GetInstance();
|
||||
|
||||
_manager.Items = Items;
|
||||
}
|
||||
|
||||
public ReactiveCommand<OrganizerEntry, Unit> InsertItemCommand =>
|
||||
_insertItemCommand ?? ( _insertItemCommand = ReactiveCommand.CreateFromTask<OrganizerEntry>( InsertItem,
|
||||
this.WhenAnyValue( e => e.SelectedItem, e => e.IsOrganizing, ( e, f ) => e != null && !f ) ) );
|
||||
|
||||
public bool IsOrganizing
|
||||
{
|
||||
get => _isOrganizing;
|
||||
set
|
||||
{
|
||||
SetProperty( ref _isOrganizing, value );
|
||||
NotifyPropertyChanged( nameof( PlayStopButtonText ) );
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand NewOrganizerEntryCommand =>
|
||||
_newOrganizerEntryCommand ??
|
||||
( _newOrganizerEntryCommand = new RelayCommand( NewOrganizerEntry, o => !IsOrganizing ) );
|
||||
|
||||
public ReactiveCommand<OrganizerEntry, Unit> OrganizeCommand =>
|
||||
_organizeCommand ?? ( _organizeCommand = ReactiveCommand.CreateFromTask<OrganizerEntry>( Organize,
|
||||
this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) );
|
||||
|
||||
public string PlayStopButtonText => IsOrganizing ? Strings.Stop : Strings.Play;
|
||||
|
||||
public ReactiveCommand<OrganizerItem, Unit> RemoveItemCommand =>
|
||||
_removeItemCommand ?? ( _removeItemCommand = ReactiveCommand.Create<OrganizerItem>( RemoveItem,
|
||||
this.WhenAnyValue( e => e.IsOrganizing, e => e.SelectedOrganizerItem, ( e, f ) => !e && f != null ) ) );
|
||||
|
||||
public ReactiveCommand<OrganizerEntry, Unit> RemoveOrganizerAgentEntryCommand =>
|
||||
_removeOrganizerAgentEntryCommand ?? ( _removeOrganizerAgentEntryCommand =
|
||||
ReactiveCommand.Create<OrganizerEntry>( RemoveOrganizerAgentEntry,
|
||||
this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) );
|
||||
|
||||
public OrganizerEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
public OrganizerItem SelectedOrganizerItem
|
||||
{
|
||||
get => _selectedOrganizerItem;
|
||||
set => SetProperty( ref _selectedOrganizerItem, value );
|
||||
}
|
||||
|
||||
public ReactiveCommand<OrganizerEntry, Unit> SetContainersCommand =>
|
||||
_setContainersCommand ?? ( _setContainersCommand =
|
||||
ReactiveCommand.CreateFromTask<OrganizerEntry>( _manager.SetContainers,
|
||||
this.WhenAnyValue( e => e.SelectedItem, f => f.IsOrganizing, ( e, f ) => e != null && !f ) ) );
|
||||
//new RelayCommandAsync( _manager.SetContainers, o => SelectedItem != null && !IsOrganizing ) );
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
JArray organizer = new JArray();
|
||||
|
||||
foreach ( OrganizerEntry organizerEntry in Items )
|
||||
{
|
||||
JObject entryObj = new JObject();
|
||||
|
||||
SetJsonValue( entryObj, "Name", organizerEntry.Name );
|
||||
SetJsonValue( entryObj, "Stack", organizerEntry.Stack );
|
||||
SetJsonValue( entryObj, "SourceContainer", organizerEntry.SourceContainer );
|
||||
SetJsonValue( entryObj, "DestinationContainer", organizerEntry.DestinationContainer );
|
||||
SetJsonValue( entryObj, "Keys", organizerEntry.Hotkey.ToJObject() );
|
||||
SetJsonValue( entryObj, "Complete", organizerEntry.Complete );
|
||||
|
||||
JArray itemsArray = new JArray();
|
||||
|
||||
foreach ( OrganizerItem organizerItem in organizerEntry.Items )
|
||||
{
|
||||
JObject itemsObj = new JObject();
|
||||
|
||||
SetJsonValue( itemsObj, "Item", organizerItem.Item );
|
||||
SetJsonValue( itemsObj, "ID", organizerItem.ID );
|
||||
SetJsonValue( itemsObj, "Hue", organizerItem.Hue );
|
||||
SetJsonValue( itemsObj, "Amount", organizerItem.Amount );
|
||||
|
||||
itemsArray.Add( itemsObj );
|
||||
}
|
||||
|
||||
entryObj.Add( "Items", itemsArray );
|
||||
|
||||
organizer.Add( entryObj );
|
||||
}
|
||||
|
||||
json?.Add( "Organizer", organizer );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
if ( json?["Organizer"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( JToken token in json["Organizer"] )
|
||||
{
|
||||
OrganizerEntry entry = new OrganizerEntry
|
||||
{
|
||||
Name = GetJsonValue( token, "Name", "Organizer" ),
|
||||
Stack = GetJsonValue( token, "Stack", true ),
|
||||
SourceContainer = GetJsonValue( token, "SourceContainer", 0 ),
|
||||
DestinationContainer = GetJsonValue( token, "DestinationContainer", 0 ),
|
||||
Hotkey = new ShortcutKeys( token["Keys"] ),
|
||||
Complete = GetJsonValue( token, "Complete", false )
|
||||
};
|
||||
|
||||
entry.Action = hks => Task.Run( async () => await _manager.Organize( entry ) );
|
||||
entry.IsRunning = () => IsOrganizing;
|
||||
|
||||
foreach ( JToken itemToken in token["Items"] )
|
||||
{
|
||||
OrganizerItem item = new OrganizerItem
|
||||
{
|
||||
Item = GetJsonValue( itemToken, "Item", string.Empty ),
|
||||
ID = GetJsonValue( itemToken, "ID", 0 ),
|
||||
Hue = GetJsonValue( itemToken, "Hue", -1 ),
|
||||
Amount = GetJsonValue( itemToken, "Amount", -1 )
|
||||
};
|
||||
|
||||
entry.Items.Add( item );
|
||||
}
|
||||
|
||||
Items.Add( entry );
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Organize( object arg )
|
||||
{
|
||||
if ( !( arg is OrganizerEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsOrganizing = true;
|
||||
|
||||
await _manager.Organize( entry );
|
||||
|
||||
IsOrganizing = false;
|
||||
}
|
||||
|
||||
private void NewOrganizerEntry( object obj )
|
||||
{
|
||||
int count = Items.Count + 1;
|
||||
|
||||
OrganizerEntry entry = new OrganizerEntry
|
||||
{
|
||||
Name = $"Organizer-{count}",
|
||||
Action = hks => Task.Run( async () => await _manager.Organize( SelectedItem ) ),
|
||||
IsRunning = () => IsOrganizing
|
||||
};
|
||||
|
||||
Items.Add( entry );
|
||||
}
|
||||
|
||||
private void RemoveOrganizerAgentEntry( object obj )
|
||||
{
|
||||
if ( !( obj is OrganizerEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Items.Remove( entry );
|
||||
}
|
||||
|
||||
private void RemoveItem( object obj )
|
||||
{
|
||||
if ( !( obj is OrganizerItem item ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedItem.Items.Remove( item );
|
||||
}
|
||||
|
||||
private static async Task InsertItem( object arg )
|
||||
{
|
||||
if ( !( arg is OrganizerEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int serial = await Commands.GetTargeSerialAsync( Strings.Target_new_item___ );
|
||||
|
||||
if ( serial <= 0 )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Invalid_or_unknown_object_id );
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = Engine.Items.GetItem( serial );
|
||||
|
||||
if ( item == null )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Cannot_find_item___ );
|
||||
return;
|
||||
}
|
||||
|
||||
OrganizerItem organizerItem = new OrganizerItem
|
||||
{
|
||||
Item = TileData.GetStaticTile( item.ID ).Name, ID = item.ID, Hue = item.Hue, Amount = -1
|
||||
};
|
||||
|
||||
entry.Items.Add( organizerItem );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Scavenger;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Network;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ReactiveUI;
|
||||
using UOC = ClassicAssist.Shared.UO.Commands;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Agents
|
||||
{
|
||||
public class ScavengerTabViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private const int SCAVENGER_DISTANCE = 2;
|
||||
private readonly List<int> _ignoreList;
|
||||
private readonly object _scavengeLock = new object();
|
||||
private ICommand _clearAllCommand;
|
||||
private int _containerSerial;
|
||||
private bool _enabled;
|
||||
private ICommand _insertCommand;
|
||||
private ObservableCollection<ScavengerEntry> _items = new ObservableCollection<ScavengerEntry>();
|
||||
private ICommand _removeCommand;
|
||||
private ScavengerEntry _selectedItem;
|
||||
private ICommand _setContainerCommand;
|
||||
|
||||
public ScavengerTabViewModel()
|
||||
{
|
||||
ScavengerManager manager = ScavengerManager.GetInstance();
|
||||
manager.Items = Items;
|
||||
manager.CheckArea = () => Task.Run( CheckArea );
|
||||
_ignoreList = new List<int>();
|
||||
}
|
||||
|
||||
public ICommand ClearAllCommand =>
|
||||
_clearAllCommand ?? ( _clearAllCommand = ReactiveCommand.CreateFromTask( ClearAll ) );
|
||||
|
||||
public int ContainerSerial
|
||||
{
|
||||
get => _containerSerial;
|
||||
set => SetProperty( ref _containerSerial, value );
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set => SetProperty( ref _enabled, value );
|
||||
}
|
||||
|
||||
public ICommand InsertCommand =>
|
||||
_insertCommand ?? ( _insertCommand = new RelayCommandAsync( Insert, o => true ) );
|
||||
|
||||
public ObservableCollection<ScavengerEntry> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ICommand RemoveCommand =>
|
||||
_removeCommand ?? ( _removeCommand = ReactiveCommand.CreateFromTask<ScavengerEntry>( Remove,
|
||||
this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) );
|
||||
|
||||
public ScavengerEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
public ICommand SetContainerCommand =>
|
||||
_setContainerCommand ?? ( _setContainerCommand = new RelayCommandAsync( SetContainer, o => true ) );
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
Engine.Items.CollectionChanged -= ItemsOnCollectionChanged;
|
||||
|
||||
if ( json == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JObject scavengerObj = new JObject { { "Enabled", Enabled }, { "Container", ContainerSerial } };
|
||||
|
||||
JArray itemsArray = new JArray();
|
||||
|
||||
foreach ( ScavengerEntry entry in Items )
|
||||
{
|
||||
itemsArray.Add( new JObject
|
||||
{
|
||||
{ "Graphic", entry.Graphic },
|
||||
{ "Name", entry.Name },
|
||||
{ "Hue", entry.Hue },
|
||||
{ "Enabled", entry.Enabled }
|
||||
} );
|
||||
}
|
||||
|
||||
scavengerObj.Add( "Items", itemsArray );
|
||||
|
||||
json.Add( "Scavenger", scavengerObj );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Engine.Items.CollectionChanged += ItemsOnCollectionChanged;
|
||||
Items.Clear();
|
||||
|
||||
if ( json?["Scavenger"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JToken config = json["Scavenger"];
|
||||
|
||||
Enabled = config["Enabled"]?.ToObject<bool>() ?? true;
|
||||
ContainerSerial = config["Container"]?.ToObject<int>() ?? 0;
|
||||
|
||||
if ( config["Items"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( JToken token in config["Items"] )
|
||||
{
|
||||
ScavengerEntry entry = new ScavengerEntry
|
||||
{
|
||||
Graphic = token["Graphic"]?.ToObject<int>() ?? 0,
|
||||
Name = token["Name"]?.ToObject<string>() ?? "Unknown",
|
||||
Hue = token["Hue"]?.ToObject<int>() ?? 0,
|
||||
Enabled = token["Enabled"]?.ToObject<bool>() ?? true
|
||||
};
|
||||
|
||||
bool alreadyExists = Items.Any( s => s.Graphic == entry.Graphic && s.Hue == entry.Hue );
|
||||
|
||||
if ( !alreadyExists )
|
||||
{
|
||||
Items.Add( entry );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ItemsOnCollectionChanged( int totalcount, bool added, Item[] items )
|
||||
{
|
||||
if ( !added || !Enabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasNearby = items.Any( i => i.Distance <= SCAVENGER_DISTANCE );
|
||||
|
||||
if ( hasNearby )
|
||||
{
|
||||
CheckArea();
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckArea()
|
||||
{
|
||||
if ( !Enabled || Engine.Player == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<Item> scavengerItems = new List<Item>();
|
||||
|
||||
if ( Engine.Player.WeightMax - Engine.Player.Weight <= 25 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock ( _scavengeLock )
|
||||
{
|
||||
foreach ( ScavengerEntry entry in Items )
|
||||
{
|
||||
if ( !entry.Enabled )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Item[] matches = Engine.Items.SelectEntities( i =>
|
||||
i.Distance <= SCAVENGER_DISTANCE && i.Owner == 0 && i.ID == entry.Graphic &&
|
||||
( entry.Hue == -1 || i.Hue == entry.Hue ) && !_ignoreList.Contains( i.Serial ) );
|
||||
|
||||
if ( matches == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
scavengerItems.AddRange( matches );
|
||||
}
|
||||
|
||||
if ( scavengerItems.Count == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Item container = Engine.Items.GetItem( _containerSerial ) ?? Engine.Player.Backpack;
|
||||
|
||||
if ( container == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( Item scavengerItem in scavengerItems.Where( scavengerItem =>
|
||||
scavengerItem.Distance <= SCAVENGER_DISTANCE ).Distinct() )
|
||||
{
|
||||
_ignoreList.Add( scavengerItem.Serial );
|
||||
UOC.SystemMessage( string.Format( Strings.Scavenging___0__, scavengerItem.Name ?? "Unknown" ), 61 );
|
||||
ActionPacketQueue.EnqueueDragDrop( scavengerItem.Serial, scavengerItem.Count, container.Serial,
|
||||
QueuePriority.Low, true ).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Insert( object arg )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
string tiledataName = TileData.GetStaticTile( item.ID ).Name ?? "Unknown";
|
||||
|
||||
ScavengerEntry entry =
|
||||
new ScavengerEntry { Enabled = true, Graphic = item.ID, Hue = item.Hue, Name = tiledataName };
|
||||
|
||||
Items.Add( entry );
|
||||
}
|
||||
|
||||
private async Task Remove( object arg )
|
||||
{
|
||||
if ( !( arg is ScavengerEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Items.Remove( entry );
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task ClearAll( CancellationToken cancellationToken )
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SetContainer( object arg )
|
||||
{
|
||||
int serial = await UOC.GetTargeSerialAsync( Strings.Select_destination_container___ );
|
||||
|
||||
if ( serial == 0 )
|
||||
{
|
||||
UOC.SystemMessage( Strings.Invalid_container___ );
|
||||
return;
|
||||
}
|
||||
|
||||
ContainerSerial = serial;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Macros;
|
||||
using ClassicAssist.Data.Macros.Commands;
|
||||
using ClassicAssist.Data.Vendors;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Network;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ReactiveUI;
|
||||
using UOC = ClassicAssist.Shared.UO.Commands;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Agents
|
||||
{
|
||||
public class VendorBuyTabViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private bool _enabled;
|
||||
private bool _includeBackpackAmount;
|
||||
private bool _includePurchasedAmount;
|
||||
private ICommand _insertCommand;
|
||||
private ObservableCollection<VendorBuyAgentEntry> _items = new ObservableCollection<VendorBuyAgentEntry>();
|
||||
private ICommand _removeCommand;
|
||||
private VendorBuyAgentEntry _selectedItem;
|
||||
|
||||
public VendorBuyTabViewModel()
|
||||
{
|
||||
IncomingPacketHandlers.VendorBuyDisplayEvent += OnVendorBuyDisplayEvent;
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set => SetProperty( ref _enabled, value );
|
||||
}
|
||||
|
||||
public bool IncludeBackpackAmount
|
||||
{
|
||||
get => _includeBackpackAmount;
|
||||
set => SetProperty( ref _includeBackpackAmount, value );
|
||||
}
|
||||
|
||||
public bool IncludePurchasedAmount
|
||||
{
|
||||
get => _includePurchasedAmount;
|
||||
set => SetProperty( ref _includePurchasedAmount, value );
|
||||
}
|
||||
|
||||
public ICommand InsertCommand =>
|
||||
_insertCommand ?? ( _insertCommand = ReactiveCommand.CreateFromTask( Insert ) );
|
||||
|
||||
public ObservableCollection<VendorBuyAgentEntry> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ICommand RemoveCommand =>
|
||||
_removeCommand ?? ( _removeCommand = ReactiveCommand.Create<VendorBuyAgentEntry>( Remove,
|
||||
this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) );
|
||||
|
||||
public VendorBuyAgentEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
if ( json == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JObject config = new JObject
|
||||
{
|
||||
{ "Enabled", Enabled },
|
||||
{ "IncludeBackpackAmount", IncludeBackpackAmount },
|
||||
{ "IncludePurchasedAmount", IncludePurchasedAmount }
|
||||
};
|
||||
|
||||
JArray itemObj = new JArray();
|
||||
|
||||
foreach ( VendorBuyAgentEntry entry in Items )
|
||||
{
|
||||
JObject entryObj = new JObject
|
||||
{
|
||||
{ "Enabled", entry.Enabled },
|
||||
{ "Name", entry.Name },
|
||||
{ "Graphic", entry.Graphic },
|
||||
{ "Hue", entry.Hue },
|
||||
{ "Amount", entry.Amount },
|
||||
{ "MaxPrice", entry.MaxPrice }
|
||||
};
|
||||
|
||||
itemObj.Add( entryObj );
|
||||
}
|
||||
|
||||
config.Add( "Items", itemObj );
|
||||
|
||||
json.Add( "VendorBuy", config );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
if ( json?["VendorBuy"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JToken config = json["VendorBuy"];
|
||||
|
||||
Enabled = config["Enabled"]?.ToObject<bool>() ?? false;
|
||||
IncludeBackpackAmount = config["IncludeBackpackAmount"]?.ToObject<bool>() ?? false;
|
||||
IncludePurchasedAmount = config["IncludePurchasedAmount"]?.ToObject<bool>() ?? false;
|
||||
|
||||
foreach ( JToken token in config["Items"] )
|
||||
{
|
||||
VendorBuyAgentEntry vbae = new VendorBuyAgentEntry
|
||||
{
|
||||
Enabled = token["Enabled"]?.ToObject<bool>() ?? false,
|
||||
Name = token["Name"]?.ToObject<string>() ?? "Unknown",
|
||||
Graphic = token["Graphic"]?.ToObject<int>() ?? 0,
|
||||
Hue = token["Hue"]?.ToObject<int>() ?? 0,
|
||||
Amount = token["Amount"]?.ToObject<int>() ?? 0,
|
||||
MaxPrice = token["MaxPrice"]?.ToObject<int>() ?? 0
|
||||
};
|
||||
|
||||
Items.Add( vbae );
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVendorBuyDisplayEvent( int serial, ShopListEntry[] entries )
|
||||
{
|
||||
if ( !Enabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<VendorBuyAgentEntry> items = Items.Where( i => i.Enabled );
|
||||
|
||||
List<ShopListEntry> buyList = new List<ShopListEntry>();
|
||||
|
||||
foreach ( VendorBuyAgentEntry entry in items )
|
||||
{
|
||||
IEnumerable<ShopListEntry> matches = entries.Where( i =>
|
||||
i.Item.ID == entry.Graphic && ( entry.Hue == -1 || i.Item.Hue == entry.Hue ) &&
|
||||
( entry.MaxPrice == -1 || i.Price <= entry.MaxPrice ) );
|
||||
|
||||
foreach ( ShopListEntry match in matches )
|
||||
{
|
||||
if ( entry.Amount != -1 )
|
||||
{
|
||||
if ( match.Amount > entry.Amount )
|
||||
{
|
||||
match.Amount = entry.Amount;
|
||||
}
|
||||
|
||||
if ( IncludeBackpackAmount )
|
||||
{
|
||||
int currentAmount = ObjectCommands.CountType( entry.Graphic, "backpack", entry.Hue );
|
||||
|
||||
if ( currentAmount + match.Amount > entry.Amount )
|
||||
{
|
||||
match.Amount = entry.Amount - currentAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( match.Amount > 0 )
|
||||
{
|
||||
buyList.Add( match );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( buyList.Count > 0 )
|
||||
{
|
||||
UOC.VendorBuy( serial, buyList.ToArray() );
|
||||
}
|
||||
|
||||
if ( !MacroManager.QuietMode && buyList.Count == 0 )
|
||||
{
|
||||
UOC.SystemMessage( Strings.Buy_Agent__No_matches_found_ );
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
string name = TileData.GetStaticTile( item.ID ).Name ?? item.Name;
|
||||
|
||||
Items.Add( new VendorBuyAgentEntry
|
||||
{
|
||||
Enabled = true,
|
||||
Name = name,
|
||||
Graphic = item.ID,
|
||||
Amount = -1,
|
||||
Hue = item.Hue,
|
||||
MaxPrice = -1
|
||||
} );
|
||||
}
|
||||
|
||||
private void Remove( object obj )
|
||||
{
|
||||
if ( !( obj is VendorBuyAgentEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Items.Remove( entry );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Vendors;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Network;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ReactiveUI;
|
||||
using UOC = ClassicAssist.Shared.UO.Commands;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Agents
|
||||
{
|
||||
public class VendorSellTabViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private ICommand _insertCommand;
|
||||
private ObservableCollection<VendorSellAgentEntry> _items = new ObservableCollection<VendorSellAgentEntry>();
|
||||
private ICommand _removeCommand;
|
||||
private VendorSellAgentEntry _selectedItem;
|
||||
|
||||
public VendorSellTabViewModel()
|
||||
{
|
||||
IncomingPacketHandlers.VendorSellDisplayEvent += OnVendorSellDisplayEvent;
|
||||
}
|
||||
|
||||
public ICommand InsertCommand =>
|
||||
_insertCommand ?? ( _insertCommand = ReactiveCommand.CreateFromTask( Insert ) );
|
||||
|
||||
public ObservableCollection<VendorSellAgentEntry> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ICommand RemoveCommand =>
|
||||
_removeCommand ?? ( _removeCommand = ReactiveCommand.Create<VendorSellAgentEntry>( Remove,
|
||||
this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) );
|
||||
|
||||
public VendorSellAgentEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
if ( json == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JArray itemsObj = new JArray();
|
||||
|
||||
foreach ( VendorSellAgentEntry item in Items )
|
||||
{
|
||||
JObject itemObj = new JObject
|
||||
{
|
||||
{ "Enabled", item.Enabled },
|
||||
{ "Graphic", item.Graphic },
|
||||
{ "Hue", item.Hue },
|
||||
{ "Amount", item.Amount },
|
||||
{ "MinPrice", item.MinPrice },
|
||||
{ "Name", item.Name }
|
||||
};
|
||||
|
||||
itemsObj.Add( itemObj );
|
||||
}
|
||||
|
||||
JObject config = new JObject { { "Items", itemsObj } };
|
||||
|
||||
json.Add( "VendorSell", config );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
if ( json?["VendorSell"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JToken config = json["VendorSell"];
|
||||
|
||||
foreach ( JToken items in config?["Items"] )
|
||||
{
|
||||
VendorSellAgentEntry vsae = new VendorSellAgentEntry
|
||||
{
|
||||
Enabled = items["Enabled"]?.ToObject<bool>() ?? true,
|
||||
Graphic = items["Graphic"]?.ToObject<int>() ?? 0,
|
||||
Hue = items["Hue"]?.ToObject<int>() ?? 0,
|
||||
Amount = items["Amount"]?.ToObject<int>() ?? -1,
|
||||
MinPrice = items["MinPrice"]?.ToObject<int>() ?? 0,
|
||||
Name = items["Name"]?.ToObject<string>() ?? string.Empty
|
||||
};
|
||||
|
||||
Items.Add( vsae );
|
||||
}
|
||||
}
|
||||
|
||||
private void Remove( object arg )
|
||||
{
|
||||
if ( !( arg is VendorSellAgentEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Items.Remove( entry );
|
||||
}
|
||||
|
||||
private void OnVendorSellDisplayEvent( int serial, SellListEntry[] entries )
|
||||
{
|
||||
List<SellListEntry> sellList = new List<SellListEntry>();
|
||||
|
||||
foreach ( SellListEntry entry in entries )
|
||||
{
|
||||
VendorSellAgentEntry match = Items.FirstOrDefault( i =>
|
||||
i.Graphic == entry.ID && ( i.Hue == -1 || i.Hue == entry.Hue ) && entry.Price >= i.MinPrice &&
|
||||
i.Enabled );
|
||||
|
||||
if ( match != null && match.Amount != -1 )
|
||||
{
|
||||
entry.Amount = Math.Min( match.Amount, entry.Amount );
|
||||
}
|
||||
|
||||
if ( match != null )
|
||||
{
|
||||
sellList.Add( entry );
|
||||
}
|
||||
}
|
||||
|
||||
if ( sellList.Count > 0 )
|
||||
{
|
||||
Sell( serial, sellList.ToArray() );
|
||||
}
|
||||
}
|
||||
|
||||
public static void Sell( int vendorSerial, SellListEntry[] entries )
|
||||
{
|
||||
if ( entries == null || entries.Length == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int len = 9 + entries.Length * 6;
|
||||
|
||||
PacketWriter pw = new PacketWriter( len );
|
||||
pw.Write( (byte) 0x9F );
|
||||
pw.Write( (short) len );
|
||||
pw.Write( vendorSerial );
|
||||
pw.Write( (short) entries.Length );
|
||||
|
||||
foreach ( SellListEntry entry in entries )
|
||||
{
|
||||
pw.Write( entry.Serial );
|
||||
pw.Write( (short) entry.Amount );
|
||||
}
|
||||
|
||||
Engine.SendPacketToServer( pw );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
string name = TileData.GetStaticTile( item.ID ).Name ?? item.Name;
|
||||
|
||||
Items.Add( new VendorSellAgentEntry
|
||||
{
|
||||
Enabled = true,
|
||||
Name = name,
|
||||
Graphic = item.ID,
|
||||
Hue = item.Hue,
|
||||
Amount = -1,
|
||||
MinPrice = 0
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Data;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Autoloot
|
||||
{
|
||||
public class ClilocSelectionViewModel : BaseViewModel
|
||||
{
|
||||
private ObservableCollection<ClilocEntry> _allClilocs = new ObservableCollection<ClilocEntry>();
|
||||
private MessageBoxResult _dialogResult = MessageBoxResult.Cancel;
|
||||
private ObservableCollection<ClilocEntry> _filteredClilocs = new ObservableCollection<ClilocEntry>();
|
||||
private string _filterText;
|
||||
private ICommand _okCommand;
|
||||
private ClilocEntry _selectedCliloc;
|
||||
|
||||
public ClilocSelectionViewModel()
|
||||
{
|
||||
foreach ( KeyValuePair<int, string> kvp in Cliloc.GetItems() )
|
||||
{
|
||||
AllClilocs.Add( new ClilocEntry { Key = kvp.Key, Value = kvp.Value } );
|
||||
}
|
||||
|
||||
UpdateEntries( _filterText );
|
||||
}
|
||||
|
||||
public ObservableCollection<ClilocEntry> AllClilocs
|
||||
{
|
||||
get => _allClilocs;
|
||||
set => SetProperty( ref _allClilocs, value );
|
||||
}
|
||||
|
||||
public MessageBoxResult DialogResult
|
||||
{
|
||||
get => _dialogResult;
|
||||
set => SetProperty( ref _dialogResult, value );
|
||||
}
|
||||
|
||||
public ObservableCollection<ClilocEntry> FilteredClilocs
|
||||
{
|
||||
get => _filteredClilocs;
|
||||
set => SetProperty( ref _filteredClilocs, value );
|
||||
}
|
||||
|
||||
public string FilterText
|
||||
{
|
||||
get => _filterText;
|
||||
set
|
||||
{
|
||||
SetProperty( ref _filterText, value );
|
||||
UpdateEntries( value );
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand OKCommand => _okCommand ?? ( _okCommand = new RelayCommand( OK, o => SelectedCliloc != null ) );
|
||||
|
||||
public ClilocEntry SelectedCliloc
|
||||
{
|
||||
get => _selectedCliloc;
|
||||
set => SetProperty( ref _selectedCliloc, value );
|
||||
}
|
||||
|
||||
private void OK( object obj )
|
||||
{
|
||||
DialogResult = MessageBoxResult.OK;
|
||||
}
|
||||
|
||||
private void UpdateEntries( string filterText )
|
||||
{
|
||||
IEnumerable<ClilocEntry> matches = AllClilocs.Where( m =>
|
||||
string.IsNullOrEmpty( filterText ) || m.Key.ToString().Contains( filterText ) ||
|
||||
m.Value.ToLower().Contains( filterText.ToLower() ) );
|
||||
|
||||
FilteredClilocs.Clear();
|
||||
|
||||
foreach ( ClilocEntry clilocEntry in matches )
|
||||
{
|
||||
FilteredClilocs.Add( clilocEntry );
|
||||
}
|
||||
}
|
||||
|
||||
public class ClilocEntry
|
||||
{
|
||||
public int Key { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data.Autoloot;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.Shared.UO;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Autoloot
|
||||
{
|
||||
public class CustomPropertiesViewModel : BaseViewModel
|
||||
{
|
||||
private readonly string _propertiesFileCustom =
|
||||
Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Data", "Properties.Custom.json" );
|
||||
|
||||
private ICommand _chooseFromItemCommand;
|
||||
private ObservableCollection<CustomProperty> _properties = new ObservableCollection<CustomProperty>();
|
||||
private ICommand _removeCommand;
|
||||
private ICommand _saveCommand;
|
||||
private CustomProperty _selectedProperty;
|
||||
|
||||
public CustomPropertiesViewModel()
|
||||
{
|
||||
LoadCustomProperties();
|
||||
}
|
||||
|
||||
public ICommand ChooseFromClilocCommand => new RelayCommand( ChooseFromCliloc, o => true );
|
||||
|
||||
public ICommand ChooseFromItemCommand =>
|
||||
_chooseFromItemCommand ?? ( _chooseFromItemCommand = new RelayCommandAsync( ChooseFromItem, o => true ) );
|
||||
|
||||
public ObservableCollection<CustomProperty> Properties
|
||||
{
|
||||
get => _properties;
|
||||
set => SetProperty( ref _properties, value );
|
||||
}
|
||||
|
||||
public ICommand RemoveCommand =>
|
||||
_removeCommand ?? ( _removeCommand = new RelayCommand( Remove, o => SelectedProperty != null ) );
|
||||
|
||||
public ICommand SaveCommand => _saveCommand ?? ( _saveCommand = new RelayCommand( Save, o => true ) );
|
||||
|
||||
public CustomProperty SelectedProperty
|
||||
{
|
||||
get => _selectedProperty;
|
||||
set => SetProperty( ref _selectedProperty, value );
|
||||
}
|
||||
|
||||
private void ChooseFromCliloc( object obj )
|
||||
{
|
||||
ClilocSelectionViewModel vm = new ClilocSelectionViewModel();
|
||||
Engine.UIInvoker.InvokeDialog( "ClilocSelectionWindow", dataContext: vm );
|
||||
|
||||
if ( vm.DialogResult != MessageBoxResult.OK )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Properties.Any( p => p.Cliloc == vm.SelectedCliloc.Key ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Properties.AddSorted( new CustomProperty
|
||||
{
|
||||
Cliloc = vm.SelectedCliloc.Key,
|
||||
Name = vm.SelectedCliloc.Value,
|
||||
Arguments = vm.SelectedCliloc.Value.Contains( "~" )
|
||||
} );
|
||||
}
|
||||
|
||||
private async Task ChooseFromItem( object obj )
|
||||
{
|
||||
int serial = await Commands.GetTargeSerialAsync( Strings.Target_object___, 90000 );
|
||||
|
||||
if ( serial == 0 )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Cannot_find_item___ );
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = Engine.Items.GetItem( serial );
|
||||
|
||||
if ( item == null )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Cannot_find_item___ );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( item.Properties == null )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Item_properties_null_or_not_loaded___ );
|
||||
return;
|
||||
}
|
||||
|
||||
PropertySelectionViewModel vm = new PropertySelectionViewModel( item.Properties );
|
||||
await Engine.UIInvoker.InvokeDialog( "PropertySelectionWindow", dataContext: vm );
|
||||
|
||||
if ( vm.DialogResult != MessageBoxResult.OK )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<SelectProperties> selectedProperties = vm.Properties.Where( p => p.Selected );
|
||||
|
||||
foreach ( SelectProperties property in selectedProperties )
|
||||
{
|
||||
if ( Properties.Any( p => p.Cliloc == property.Property.Cliloc ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Properties.AddSorted( new CustomProperty
|
||||
{
|
||||
Name = property.Name,
|
||||
Cliloc = property.Property.Cliloc,
|
||||
Arguments = property.Property.Arguments != null && property.Property.Arguments.Length > 0
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
private void Remove( object obj )
|
||||
{
|
||||
if ( SelectedProperty == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Properties.Remove( SelectedProperty );
|
||||
}
|
||||
|
||||
private void Save( object obj )
|
||||
{
|
||||
SaveCustomProperties();
|
||||
}
|
||||
|
||||
private void SaveCustomProperties()
|
||||
{
|
||||
List<PropertyEntry> properties = Properties.Select( property => new PropertyEntry
|
||||
{
|
||||
ClilocIndex = property.Arguments ? 0 : -1,
|
||||
Clilocs = new[] { property.Cliloc },
|
||||
ConstraintType = 0,
|
||||
Name = property.Name
|
||||
} ).ToList();
|
||||
|
||||
File.WriteAllText( _propertiesFileCustom, JsonConvert.SerializeObject( properties ) );
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
CustomProperty customProperty = new CustomProperty
|
||||
{
|
||||
Name = constraint.Name,
|
||||
Cliloc = constraint.Clilocs[0],
|
||||
Arguments = constraint.ClilocIndex == 0
|
||||
};
|
||||
|
||||
Properties.AddSorted( customProperty );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomProperty : IComparable<CustomProperty>
|
||||
{
|
||||
public bool Arguments { get; set; }
|
||||
public int Cliloc { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public int CompareTo( CustomProperty other )
|
||||
{
|
||||
if ( ReferenceEquals( this, other ) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ReferenceEquals( null, other )
|
||||
? 1
|
||||
: string.Compare( Name, other.Name, StringComparison.InvariantCultureIgnoreCase );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Objects;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Autoloot
|
||||
{
|
||||
public class PropertySelectionViewModel : BaseViewModel
|
||||
{
|
||||
private MessageBoxResult _dialogResult = MessageBoxResult.Cancel;
|
||||
private ICommand _okCommand;
|
||||
private ObservableCollection<SelectProperties> _properties = new ObservableCollection<SelectProperties>();
|
||||
|
||||
public PropertySelectionViewModel()
|
||||
{
|
||||
}
|
||||
|
||||
public PropertySelectionViewModel( IEnumerable<Property> properties )
|
||||
{
|
||||
foreach ( Property property in properties )
|
||||
{
|
||||
Properties.Add( new SelectProperties
|
||||
{
|
||||
Name = Cliloc.GetProperty( property.Cliloc ), Property = property, Selected = false
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public MessageBoxResult DialogResult
|
||||
{
|
||||
get => _dialogResult;
|
||||
set => SetProperty( ref _dialogResult, value );
|
||||
}
|
||||
|
||||
public ICommand OKCommand => _okCommand ?? ( _okCommand = new RelayCommand( OK, o => true ) );
|
||||
|
||||
public ObservableCollection<SelectProperties> Properties
|
||||
{
|
||||
get => _properties;
|
||||
set => SetProperty( ref _properties, value );
|
||||
}
|
||||
|
||||
private void OK( object obj )
|
||||
{
|
||||
DialogResult = MessageBoxResult.OK;
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectProperties
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Property Property { get; set; }
|
||||
public bool Selected { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.UI.Misc;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class BaseViewModel : ReactiveObject
|
||||
{
|
||||
private static readonly List<BaseViewModel> _viewModels = new List<BaseViewModel>();
|
||||
protected IDispatcher _dispatcher;
|
||||
|
||||
public BaseViewModel()
|
||||
{
|
||||
_viewModels.Add( this );
|
||||
_dispatcher = Engine.Dispatcher;
|
||||
|
||||
Options.CurrentOptions.PropertyChanged += OnOptionChanged;
|
||||
}
|
||||
|
||||
public static BaseViewModel[] Instances => _viewModels.ToArray();
|
||||
|
||||
public virtual void SetProperty<T>( ref T obj, T value, [CallerMemberName] string propertyName = "" )
|
||||
{
|
||||
this.RaiseAndSetIfChanged( ref obj, value, propertyName );
|
||||
}
|
||||
|
||||
protected void NotifyPropertyChanged( [CallerMemberName] string propertyName = "" )
|
||||
{
|
||||
this.RaisePropertyChanged( propertyName );
|
||||
}
|
||||
|
||||
protected void OnOptionChanged( object sender, PropertyChangedEventArgs e )
|
||||
{
|
||||
PropertyInfo[] properties = GetType().GetProperties( BindingFlags.Public | BindingFlags.Instance );
|
||||
|
||||
foreach ( PropertyInfo property in properties )
|
||||
{
|
||||
OptionsBindingAttribute attr = property.GetCustomAttribute<OptionsBindingAttribute>();
|
||||
|
||||
if ( attr == null || attr.Property != e.PropertyName )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !( sender is Options options ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
PropertyInfo optionsProperty = options.GetType().GetProperty( e.PropertyName );
|
||||
|
||||
if ( optionsProperty == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
object propertyValue = optionsProperty.GetValue( Options.CurrentOptions );
|
||||
|
||||
property.SetValue( this, propertyValue );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RelayCommandAsync : ICommand
|
||||
{
|
||||
private readonly Func<object, bool> canExecuteMethod;
|
||||
private readonly Func<object, Task> executedMethod;
|
||||
|
||||
public RelayCommandAsync( Func<object, Task> execute, Func<object, bool> canExecute )
|
||||
{
|
||||
executedMethod = execute ?? throw new ArgumentNullException( nameof( execute ) );
|
||||
canExecuteMethod = canExecute;
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add => CanExecuteChangedInternal += value;
|
||||
remove => CanExecuteChangedInternal -= value;
|
||||
}
|
||||
|
||||
public bool CanExecute( object parameter )
|
||||
{
|
||||
return canExecuteMethod == null || canExecuteMethod( parameter );
|
||||
}
|
||||
|
||||
public async void Execute( object parameter )
|
||||
{
|
||||
await executedMethod( parameter );
|
||||
}
|
||||
|
||||
private event EventHandler CanExecuteChangedInternal;
|
||||
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
CanExecuteChangedInternal?.Invoke( this, EventArgs.Empty );
|
||||
}
|
||||
}
|
||||
|
||||
public class RelayCommand : ICommand
|
||||
{
|
||||
private readonly Func<object, bool> _canExecute;
|
||||
private readonly Action<object> _execute;
|
||||
|
||||
public RelayCommand( Action<object> execute, Func<object, bool> canExecute = null )
|
||||
{
|
||||
_execute = execute;
|
||||
_canExecute = canExecute;
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add => CanExecuteChangedInternal += value;
|
||||
remove => CanExecuteChangedInternal -= value;
|
||||
}
|
||||
|
||||
public bool CanExecute( object parameter )
|
||||
{
|
||||
return _canExecute == null || _canExecute( parameter );
|
||||
}
|
||||
|
||||
public void Execute( object parameter )
|
||||
{
|
||||
_execute( parameter );
|
||||
}
|
||||
|
||||
private event EventHandler CanExecuteChangedInternal;
|
||||
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
CanExecuteChangedInternal?.Invoke( this, EventArgs.Empty );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data.Autoloot;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.Shared.UO;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Network.Packets;
|
||||
using ClassicAssist.UO.Objects;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Debug
|
||||
{
|
||||
public class DebugAutolootViewModel : BaseViewModel
|
||||
{
|
||||
private ICommand _clearResultsCommand;
|
||||
private int _containerSerial;
|
||||
private ICommand _retestContainerCommand;
|
||||
private ICommand _testContainerCommand;
|
||||
private string _testResults;
|
||||
|
||||
public ICommand ClearResultsCommand =>
|
||||
_clearResultsCommand ?? ( _clearResultsCommand = new RelayCommand( ClearResults, o => true ) );
|
||||
|
||||
public int ContainerSerial
|
||||
{
|
||||
get => _containerSerial;
|
||||
set => SetProperty( ref _containerSerial, value );
|
||||
}
|
||||
|
||||
public ICommand RetestContainerCommand =>
|
||||
_retestContainerCommand ?? ( _retestContainerCommand = new RelayCommand( RetestContainer, o => true ) );
|
||||
|
||||
public ICommand TestContainerCommand =>
|
||||
_testContainerCommand ?? ( _testContainerCommand = new RelayCommandAsync( TestContainerAsync, o => true ) );
|
||||
|
||||
public string TestResults
|
||||
{
|
||||
get => _testResults;
|
||||
set => SetProperty( ref _testResults, value );
|
||||
}
|
||||
|
||||
private void ClearResults( object obj )
|
||||
{
|
||||
TestResults = string.Empty;
|
||||
}
|
||||
|
||||
private void RetestContainer( object obj )
|
||||
{
|
||||
TestContainer( ContainerSerial );
|
||||
}
|
||||
|
||||
private async Task TestContainerAsync( object arg )
|
||||
{
|
||||
int serial = await Commands.GetTargeSerialAsync( "Choose container...", 60000 );
|
||||
|
||||
if ( serial == 0 )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Invalid_container___ );
|
||||
}
|
||||
|
||||
ContainerSerial = serial;
|
||||
TestContainer( serial );
|
||||
}
|
||||
|
||||
private void TestContainer( int serial )
|
||||
{
|
||||
Item[] items = Engine.Items.GetItem( serial )?.Container?.GetItems();
|
||||
|
||||
if ( items == null )
|
||||
{
|
||||
TestResults += $"{Strings.Cannot_find_container___}\n";
|
||||
Commands.SystemMessage( Strings.Cannot_find_container___ );
|
||||
return;
|
||||
}
|
||||
|
||||
Engine.SendPacketToServer( new BatchQueryProperties( items.Select( i => i.Serial ).ToArray() ) );
|
||||
|
||||
foreach ( AutolootEntry entry in AutolootManager.GetInstance().GetEntries() )
|
||||
{
|
||||
if ( !entry.Enabled )
|
||||
{
|
||||
TestResults += $"Entry {entry.Name} disabled...\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
TestResults += $"Entry {entry.Name}...\n\n";
|
||||
|
||||
IEnumerable<Item> matchItems = AutolootHelpers.AutolootFilter( items, entry );
|
||||
|
||||
if ( matchItems == null )
|
||||
{
|
||||
TestResults += "No items matched...\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
foreach ( Item matchItem in matchItems )
|
||||
{
|
||||
count++;
|
||||
|
||||
TestResults += matchItem.ToString();
|
||||
|
||||
if ( matchItem.Properties != null )
|
||||
{
|
||||
TestResults += "\nProperties...\n\n";
|
||||
|
||||
foreach ( Property property in matchItem.Properties )
|
||||
{
|
||||
TestResults += $"{property.Text}\n";
|
||||
}
|
||||
}
|
||||
|
||||
TestResults += "\n";
|
||||
}
|
||||
|
||||
TestResults += $"{count} matches...\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using ClassicAssist.Data.BuffIcons;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Network;
|
||||
using Microsoft.Scripting.Utils;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Debug
|
||||
{
|
||||
public class DebugBuffIconsViewModel : BaseViewModel
|
||||
{
|
||||
private readonly BuffIconManager _manager;
|
||||
private ObservableCollection<string> _items = new ObservableCollection<string>();
|
||||
private ObservableCollection<string> _messages = new ObservableCollection<string>();
|
||||
private string _selectedItem;
|
||||
|
||||
public DebugBuffIconsViewModel()
|
||||
{
|
||||
_manager = BuffIconManager.GetInstance();
|
||||
|
||||
Items.AddRange( _manager.GetEnabledNames() );
|
||||
|
||||
IncomingPacketHandlers.BufficonEnabledDisabledEvent += OnBufficonEnabledDisabledEvent;
|
||||
}
|
||||
|
||||
public ObservableCollection<string> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ObservableCollection<string> Messages
|
||||
{
|
||||
get => _messages;
|
||||
set => SetProperty( ref _messages, value );
|
||||
}
|
||||
|
||||
public string SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
private void OnBufficonEnabledDisabledEvent( int type, bool enabled, int duration )
|
||||
{
|
||||
BuffIconData data = _manager.GetDataByID( type );
|
||||
|
||||
_dispatcher.Invoke( () =>
|
||||
{
|
||||
Messages.Add( enabled ? $"Enabled: {data.Name}" : $"Disabled: {data?.Name}" );
|
||||
|
||||
Items.Clear();
|
||||
Items.AddRange( _manager.GetEnabledNames() );
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Network;
|
||||
using ClassicAssist.UO.Objects.Gumps;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Debug
|
||||
{
|
||||
public class DebugGumpViewModel : BaseViewModel
|
||||
{
|
||||
private ObservableCollection<Gump> _items = new ObservableCollection<Gump>();
|
||||
private Gump _selectedItem;
|
||||
private string _text;
|
||||
|
||||
public DebugGumpViewModel()
|
||||
{
|
||||
if ( Engine.Gumps.GetGumps( out Gump[] gumps ) )
|
||||
{
|
||||
foreach ( Gump gump in gumps )
|
||||
{
|
||||
Items.Add( gump );
|
||||
}
|
||||
}
|
||||
|
||||
IncomingPacketHandlers.GumpEvent += ( id, serial, gump ) => _dispatcher.Invoke( () =>
|
||||
{
|
||||
if ( Items.Contains( gump ) )
|
||||
{
|
||||
UpdateText( gump );
|
||||
}
|
||||
else
|
||||
{
|
||||
Items.Add( gump );
|
||||
}
|
||||
} );
|
||||
|
||||
OutgoingPacketHandlers.GumpEvent +=
|
||||
( id, serial, gump ) => _dispatcher.Invoke( () => Items.Remove( gump ) );
|
||||
}
|
||||
|
||||
public ObservableCollection<Gump> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public Gump SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
SetProperty( ref _selectedItem, value );
|
||||
UpdateText( value );
|
||||
}
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => _text;
|
||||
set => SetProperty( ref _text, value );
|
||||
}
|
||||
|
||||
private void UpdateText( Gump value )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine( $"Gump ID: 0x{value.ID:x8}" );
|
||||
sb.AppendLine( $"Serial: 0x{value.Serial:x8}" );
|
||||
sb.AppendLine( $"Pages: {value.Pages?.Length}" );
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(
|
||||
$"Layout: ({value.Layout?.Length})\r\n\r\n{string.Join( "}\r\n", value.Layout?.Split( '}' ) ?? new string[0] )}" );
|
||||
sb.AppendLine();
|
||||
sb.AppendLine( $"Text: ({value.Strings.Length})\r\n\r\n{string.Join( "\r\n", value.Strings )}" );
|
||||
sb.AppendLine();
|
||||
sb.AppendLine( $"Elements ({value.GumpElements?.Length}):" );
|
||||
sb.AppendLine();
|
||||
|
||||
if ( value.GumpElements != null )
|
||||
{
|
||||
foreach ( GumpElement element in value.GumpElements )
|
||||
{
|
||||
sb.AppendLine(
|
||||
$"X: {element.X}, Y: {element.Y}, Type: {element.Type}, Cliloc: {element.Cliloc}, Text: {element.Text}" );
|
||||
}
|
||||
}
|
||||
|
||||
Text = sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Network;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Debug
|
||||
{
|
||||
public class DebugJournalViewModel : BaseViewModel
|
||||
{
|
||||
private ICommand _clearCommand;
|
||||
private ObservableCollection<string> _items = new ObservableCollection<string>();
|
||||
private string _selectedItem;
|
||||
|
||||
public DebugJournalViewModel()
|
||||
{
|
||||
JournalEntry[] buffer = Engine.Journal.GetBuffer();
|
||||
|
||||
foreach ( JournalEntry journalEntry in buffer )
|
||||
{
|
||||
Items.Add( GetString( journalEntry ) );
|
||||
}
|
||||
|
||||
IncomingPacketHandlers.JournalEntryAddedEvent += OnJournalEntryAddedEvent;
|
||||
}
|
||||
|
||||
public ICommand ClearCommand => _clearCommand ?? ( _clearCommand = new RelayCommand( Clear, o => true ) );
|
||||
|
||||
public ObservableCollection<string> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public string SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
private void Clear( object obj )
|
||||
{
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
private static string GetString( JournalEntry journalEntry )
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine( $"Name: {journalEntry.Name}" );
|
||||
sb.AppendLine( $"Serial: 0x{journalEntry.Serial:x8}" );
|
||||
sb.AppendLine( $"ID: 0x{journalEntry.ID:x4}" );
|
||||
sb.AppendLine( $"Cliloc: {journalEntry.Cliloc}" );
|
||||
sb.AppendLine( $"Text: {journalEntry.Text}" );
|
||||
sb.AppendLine( $"Arguments: {string.Join( ",", journalEntry.Arguments ?? new string[0] )}" );
|
||||
sb.AppendLine( $"Language: {journalEntry.SpeechLanguage}" );
|
||||
sb.AppendLine( $"Type: {journalEntry.SpeechType}" );
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void OnJournalEntryAddedEvent( JournalEntry je )
|
||||
{
|
||||
_dispatcher.Invoke( () => { Items.Add( GetString( je ) ); } );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Objects;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Debug
|
||||
{
|
||||
public class DebugMenusViewModel : BaseViewModel
|
||||
{
|
||||
private ObservableCollection<Menu> _items = new ObservableCollection<Menu>();
|
||||
private Menu _selectedItem;
|
||||
private string _text;
|
||||
|
||||
public DebugMenusViewModel()
|
||||
{
|
||||
if ( Engine.Menus.GetMenus( out Menu[] menus ) )
|
||||
{
|
||||
foreach ( Menu menu in menus )
|
||||
{
|
||||
Items.Add( menu );
|
||||
}
|
||||
}
|
||||
|
||||
Engine.Menus.CollectionChangedEvent += OnCollectionChangedEvent;
|
||||
}
|
||||
|
||||
public ObservableCollection<Menu> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public Menu SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
SetProperty( ref _selectedItem, value );
|
||||
UpdateText( value );
|
||||
}
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => _text;
|
||||
set => SetProperty( ref _text, value );
|
||||
}
|
||||
|
||||
private void UpdateText( Menu menu )
|
||||
{
|
||||
if ( menu == null )
|
||||
{
|
||||
Text = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine( $"GumpID: 0x{menu.ID:x4}" );
|
||||
sb.AppendLine( $"Serial: 0x{menu.Serial:x8}" );
|
||||
sb.AppendLine( $"Title: {menu.Title}" );
|
||||
sb.AppendLine( $"Entries: {menu.Entries?.Length ?? 0}" );
|
||||
sb.AppendLine();
|
||||
sb.AppendLine( "Entries:" );
|
||||
|
||||
if ( menu.Entries != null )
|
||||
{
|
||||
foreach ( MenuEntry menuEntry in menu.Entries )
|
||||
{
|
||||
sb.AppendLine( $"Index: {menuEntry.Index}" );
|
||||
sb.AppendLine( $"ID: 0x{menuEntry.ID:x4}" );
|
||||
sb.AppendLine( $"Hue: {menuEntry.Hue}" );
|
||||
sb.AppendLine( $"Title: {menuEntry.Title}" );
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
Text = sb.ToString();
|
||||
}
|
||||
|
||||
private void OnCollectionChangedEvent( Menu[] menus )
|
||||
{
|
||||
_dispatcher.Invoke( () =>
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
if ( menus == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( Menu menu in menus )
|
||||
{
|
||||
Items.Add( menu );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data.Macros.Commands;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.Shared.UO;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Microsoft.Scripting.Utils;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Debug
|
||||
{
|
||||
public class DebugPropertiesViewModel : BaseViewModel
|
||||
{
|
||||
private ObservableCollection<Property> _items = new ObservableCollection<Property>();
|
||||
private ICommand _targetCommand;
|
||||
|
||||
public ObservableCollection<Property> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ICommand TargetCommand =>
|
||||
_targetCommand ?? ( _targetCommand = new RelayCommandAsync( Target, o => true ) );
|
||||
|
||||
private async Task Target( object arg )
|
||||
{
|
||||
int serial = await Commands.GetTargeSerialAsync( Strings.Target_object___ );
|
||||
|
||||
if ( serial == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Entity entity = UOMath.IsMobile( serial )
|
||||
? Engine.Mobiles.GetMobile( serial )
|
||||
: (Entity) Engine.Items.GetItem( serial );
|
||||
|
||||
if ( entity == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( entity.Properties == null )
|
||||
{
|
||||
if ( Engine.Features.HasFlag( FeatureFlags.AOS ) )
|
||||
{
|
||||
PropertiesCommands.WaitForProperties( entity.Serial, 5000 );
|
||||
}
|
||||
|
||||
if ( entity.Properties == null )
|
||||
{
|
||||
Commands.SystemMessage( Strings.Item_properties_null_or_not_loaded___ );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
List<Property> properties = entity.Properties.Select( entityProperty => new Property
|
||||
{
|
||||
Cliloc = entityProperty.Cliloc,
|
||||
Text = Cliloc.GetProperty( entityProperty.Cliloc ),
|
||||
Arguments = entityProperty.Arguments
|
||||
} ).ToList();
|
||||
|
||||
Items.Clear();
|
||||
Items.AddRange( properties );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using ClassicAssist.Data.SpecialMoves;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using Microsoft.Scripting.Utils;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Debug
|
||||
{
|
||||
public class DebugSpecialMovesViewModel : BaseViewModel
|
||||
{
|
||||
private readonly SpecialMovesManager _manager;
|
||||
private ObservableCollection<string> _items = new ObservableCollection<string>();
|
||||
private ObservableCollection<string> _messages = new ObservableCollection<string>();
|
||||
private string _selectedItem;
|
||||
|
||||
public DebugSpecialMovesViewModel()
|
||||
{
|
||||
_manager = SpecialMovesManager.GetInstance();
|
||||
|
||||
Items.AddRange( _manager.GetEnabledNames() );
|
||||
|
||||
_manager.SpecialMovesChanged += OnSpecialMovesChanged;
|
||||
}
|
||||
|
||||
public ObservableCollection<string> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ObservableCollection<string> Messages
|
||||
{
|
||||
get => _messages;
|
||||
set => SetProperty( ref _messages, value );
|
||||
}
|
||||
|
||||
public string SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
private void OnSpecialMovesChanged( string name, bool enabled )
|
||||
{
|
||||
_dispatcher.Invoke( () =>
|
||||
{
|
||||
Messages.Add( enabled ? $"Enabled: {name}" : $"Disabled: {name}" );
|
||||
|
||||
Items.Clear();
|
||||
Items.AddRange( _manager.GetEnabledNames() );
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using ClassicAssist.Data.Vendors;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Network;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels.Debug
|
||||
{
|
||||
public class DebugVendorBuyViewModel : BaseViewModel
|
||||
{
|
||||
private bool _isEnabled;
|
||||
private ObservableCollection<string> _messages = new ObservableCollection<string>();
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
if ( value != _isEnabled )
|
||||
{
|
||||
if ( value )
|
||||
{
|
||||
IncomingPacketHandlers.VendorBuyDisplayEvent += OnVendorBuyDisplayEvent;
|
||||
}
|
||||
else
|
||||
{
|
||||
IncomingPacketHandlers.VendorBuyDisplayEvent -= OnVendorBuyDisplayEvent;
|
||||
}
|
||||
}
|
||||
|
||||
SetProperty( ref _isEnabled, value );
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<string> Messages
|
||||
{
|
||||
get => _messages;
|
||||
set => SetProperty( ref _messages, value );
|
||||
}
|
||||
|
||||
private void OnVendorBuyDisplayEvent( int serial, ShopListEntry[] entries )
|
||||
{
|
||||
_dispatcher.Invoke( () =>
|
||||
{
|
||||
Messages.Add( $"Vendor: Serial: 0x{serial:x}" );
|
||||
|
||||
foreach ( ShopListEntry entry in entries )
|
||||
{
|
||||
bool isCliloc = entry.Name.All( char.IsDigit );
|
||||
|
||||
Messages.Add(
|
||||
$"\tName: {entry.Name} {( isCliloc ? $"({Cliloc.GetProperty( int.Parse( entry.Name ) )})" : string.Empty )}, Amount: {entry.Amount}, Price: {entry.Price}, ItemID: {entry.Item.ID} (0x{entry.Item.ID:x})" );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Annotations;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.Misc;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Network.PacketFilter;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels
|
||||
{
|
||||
public class DebugViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private ICommand _changePacketEnabledCommand;
|
||||
private ICommand _clearCommand;
|
||||
private ICommand _exportLogCommand;
|
||||
private ObservableCollection<PacketEntry> _items = new ObservableCollection<PacketEntry>();
|
||||
|
||||
private ObservableCollection<PacketEnabledEntry>
|
||||
_packetEntries = new ObservableCollection<PacketEnabledEntry>();
|
||||
|
||||
private bool _running = true;
|
||||
private PacketEntry _selectedItem;
|
||||
private bool _topmost = true;
|
||||
private ICommand _viewPlayerEquipmentCommand;
|
||||
|
||||
public DebugViewModel()
|
||||
{
|
||||
PacketEntries.Add( new PacketEnabledEntry { Name = Strings.All_Packets, PacketID = -1, Enabled = true } );
|
||||
|
||||
for ( byte i = 0; i < 0xFF; i++ )
|
||||
{
|
||||
PacketEntries.Add( new PacketEnabledEntry { Name = $"0x{i:x2}", PacketID = i, Enabled = i != 0x73 } );
|
||||
}
|
||||
|
||||
Queue = new ThreadQueue<PacketEntry>( ProcessQueue );
|
||||
Engine.PacketReceivedEvent += OnPacketReceivedEvent;
|
||||
Engine.PacketSentEvent += OnPacketSentEvent;
|
||||
Engine.InternalPacketReceivedEvent += OnInternalPacketReceivedEvent;
|
||||
Engine.InternalPacketSentEvent += OnInternalPacketSentEvent;
|
||||
}
|
||||
|
||||
public ICommand ChangePacketEnabledCommand =>
|
||||
_changePacketEnabledCommand ??
|
||||
( _changePacketEnabledCommand = new RelayCommand( EnableDisable, o => true ) );
|
||||
|
||||
public ICommand ClearCommand => _clearCommand ?? ( _clearCommand = new RelayCommand( Clear, o => true ) );
|
||||
|
||||
public ICommand ExportLogCommand =>
|
||||
_exportLogCommand ?? ( _exportLogCommand = new RelayCommand( ExportLog, o => true ) );
|
||||
|
||||
public ObservableCollection<PacketEntry> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ObservableCollection<PacketEnabledEntry> PacketEntries
|
||||
{
|
||||
get => _packetEntries;
|
||||
set => SetProperty( ref _packetEntries, value );
|
||||
}
|
||||
|
||||
public ThreadQueue<PacketEntry> Queue { get; set; }
|
||||
|
||||
public bool Running
|
||||
{
|
||||
get => _running;
|
||||
set => SetProperty( ref _running, value );
|
||||
}
|
||||
|
||||
public PacketEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
public bool Topmost
|
||||
{
|
||||
get => _topmost;
|
||||
set => SetProperty( ref _topmost, value );
|
||||
}
|
||||
|
||||
public ICommand ViewPlayerEquipmentCommand =>
|
||||
_viewPlayerEquipmentCommand ??
|
||||
( _viewPlayerEquipmentCommand = new RelayCommand( ViewPlayerEquipment, o => true ) );
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
if ( json?["Debug"] != null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JObject options = new JObject();
|
||||
|
||||
json?.Add( "Debug", options );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
if ( json?["Debug"] == null )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportLog( object obj )
|
||||
{
|
||||
if ( !( obj is IEnumerable<PacketEntry> items ) )
|
||||
{
|
||||
}
|
||||
|
||||
//TODO UI
|
||||
//SaveFileDialog sfd = new SaveFileDialog
|
||||
//{
|
||||
// InitialDirectory = Engine.StartupPath ?? Environment.CurrentDirectory,
|
||||
// Filter = "JSON Packet Log|*.packets.json",
|
||||
// FileName = "export.packets.json"
|
||||
//};
|
||||
|
||||
//bool? result = sfd.ShowDialog();
|
||||
|
||||
//if ( !result.HasValue || !result.Value || string.IsNullOrEmpty( sfd.FileName ) )
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
//string fileName = sfd.FileName;
|
||||
|
||||
//JArray jArray = new JArray();
|
||||
|
||||
//foreach ( PacketEntry packetEntry in items )
|
||||
//{
|
||||
// jArray.Add( new JObject
|
||||
// {
|
||||
// { "Title", packetEntry.Title },
|
||||
// { "DateTime", packetEntry.DateTime },
|
||||
// { "Direction", packetEntry.Direction.ToString() },
|
||||
// { "Length", packetEntry.Length },
|
||||
// { "Data", packetEntry.Data.Aggregate( string.Empty, ( current, b ) => current + $"{b:x2} " ) },
|
||||
// { "Base64", Convert.ToBase64String( packetEntry.Data ) }
|
||||
// } );
|
||||
//}
|
||||
|
||||
//File.WriteAllText( fileName, jArray.ToString() );
|
||||
}
|
||||
|
||||
private void ProcessQueue( PacketEntry entry )
|
||||
{
|
||||
_dispatcher.Invoke( () => { Items.Add( entry ); } );
|
||||
}
|
||||
|
||||
private void OnInternalPacketSentEvent( byte[] data, int length )
|
||||
{
|
||||
if ( PacketEntries.FirstOrDefault( e => e.PacketID == data[0] )?.Enabled == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Running )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PacketEntry entry = new PacketEntry
|
||||
{
|
||||
Title = "Internal Outgoing Packet", Data = data, Direction = PacketDirection.Outgoing
|
||||
};
|
||||
|
||||
Queue.Enqueue( entry );
|
||||
}
|
||||
|
||||
private void OnInternalPacketReceivedEvent( byte[] data, int length )
|
||||
{
|
||||
if ( PacketEntries.FirstOrDefault( e => e.PacketID == data[0] )?.Enabled == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Running )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PacketEntry entry = new PacketEntry
|
||||
{
|
||||
Title = "Internal Incoming Packet", Data = data, Direction = PacketDirection.Incoming
|
||||
};
|
||||
|
||||
Queue.Enqueue( entry );
|
||||
}
|
||||
|
||||
private void OnPacketSentEvent( byte[] data, int length )
|
||||
{
|
||||
if ( PacketEntries.FirstOrDefault( e => e.PacketID == data[0] )?.Enabled == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Running )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PacketEntry entry = new PacketEntry
|
||||
{
|
||||
Title = "Outgoing Packet", Data = data, Direction = PacketDirection.Outgoing
|
||||
};
|
||||
|
||||
Queue.Enqueue( entry );
|
||||
}
|
||||
|
||||
private void OnPacketReceivedEvent( byte[] data, int length )
|
||||
{
|
||||
if ( PacketEntries.FirstOrDefault( e => e.PacketID == data[0] )?.Enabled == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Running )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PacketEntry entry = new PacketEntry
|
||||
{
|
||||
Title = "Incoming Packet", Data = data, Direction = PacketDirection.Incoming
|
||||
};
|
||||
|
||||
Queue.Enqueue( entry );
|
||||
}
|
||||
|
||||
private void ViewPlayerEquipment( object obj )
|
||||
{
|
||||
//TODO UI
|
||||
//EntityCollectionViewer window = new EntityCollectionViewer
|
||||
//{
|
||||
// DataContext = new EntityCollectionViewerViewModel( Engine.Player?.Equipment ) { Topmost = Topmost }
|
||||
//};
|
||||
|
||||
//window.Show();
|
||||
}
|
||||
|
||||
private void Clear( object obj )
|
||||
{
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
private void EnableDisable( object obj )
|
||||
{
|
||||
if ( !( obj is PacketEnabledEntry packetEnabledEntry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( packetEnabledEntry.PacketID != -1 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( PacketEnabledEntry entry in PacketEntries )
|
||||
{
|
||||
entry.Enabled = packetEnabledEntry.Enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public class PacketEnabledEntry : INotifyPropertyChanged
|
||||
{
|
||||
private bool _enabled;
|
||||
private string _name;
|
||||
private int _packetId;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set => SetProperty( ref _enabled, value );
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty( ref _name, value );
|
||||
}
|
||||
|
||||
public int PacketID
|
||||
{
|
||||
get => _packetId;
|
||||
set => SetProperty( ref _packetId, value );
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
[NotifyPropertyChangedInvocator]
|
||||
protected virtual void OnPropertyChanged( [CallerMemberName] string propertyName = null )
|
||||
{
|
||||
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
|
||||
}
|
||||
|
||||
// ReSharper disable once RedundantAssignment
|
||||
public virtual void SetProperty<T>( ref T obj, T value, [CallerMemberName] string propertyName = "" )
|
||||
{
|
||||
obj = value;
|
||||
OnPropertyChanged( propertyName );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Filters;
|
||||
using ClassicAssist.Data.Macros;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.Shared.UI.ViewModels;
|
||||
using ClassicAssist.Shared.UO;
|
||||
using ClassicAssist.UI.Misc;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class GeneralControlViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private static ICommand _saveProfileCommand;
|
||||
private ICommand _changeProfileCommand;
|
||||
private ICommand _configureFilterCommand;
|
||||
private bool _isLinkedProfile;
|
||||
private ICommand _linkUnlinkProfileCommand;
|
||||
private ICommand _newProfileCommand;
|
||||
private Options _options;
|
||||
private ObservableCollection<string> _profiles = new ObservableCollection<string>();
|
||||
private ICommand _removeSavedPasswordCommand;
|
||||
private Dictionary<string, string> _savedPasswords = new Dictionary<string, string>();
|
||||
private string _selectedProfile = Options.CurrentOptions.Name;
|
||||
|
||||
public GeneralControlViewModel()
|
||||
{
|
||||
Type[] filterTypes =
|
||||
{
|
||||
typeof( WeatherFilter ), typeof( SeasonFilter ), typeof( LightLevelFilter ),
|
||||
typeof( RepeatedMessagesFilter ), typeof( ClilocFilter ), typeof( BardsMusicFilter )
|
||||
};
|
||||
|
||||
foreach ( Type type in filterTypes )
|
||||
{
|
||||
FilterEntry fe = (FilterEntry) Activator.CreateInstance( type );
|
||||
Filters.Add( fe );
|
||||
}
|
||||
|
||||
RefreshProfiles();
|
||||
|
||||
AssistantOptions.ProfileChangedEvent += OnProfileChangedEvent;
|
||||
AssistantOptions.SavedPasswordsChanged += OnSavedPasswordsChangedEvent;
|
||||
|
||||
OnSavedPasswordsChangedEvent( this, EventArgs.Empty );
|
||||
}
|
||||
|
||||
public ICommand ChangeProfileCommand =>
|
||||
_changeProfileCommand ?? ( _changeProfileCommand = new RelayCommand( ChangeProfile, o => true ) );
|
||||
|
||||
public ICommand ConfigureFilterCommand =>
|
||||
_configureFilterCommand ?? ( _configureFilterCommand = new RelayCommand( ConfigureFilter ) );
|
||||
|
||||
public ObservableCollectionEx<FilterEntry> Filters { get; set; } = new ObservableCollectionEx<FilterEntry>();
|
||||
|
||||
public bool IsLinkedProfile
|
||||
{
|
||||
get => _isLinkedProfile;
|
||||
set => SetProperty( ref _isLinkedProfile, value );
|
||||
}
|
||||
|
||||
[OptionsBinding( Property = "LightLevel" )]
|
||||
public int LightLevelListen
|
||||
{
|
||||
get => throw new NotImplementedException();
|
||||
set
|
||||
{
|
||||
FilterEntry filter = Filters.FirstOrDefault( f => f is LightLevelFilter );
|
||||
|
||||
if ( filter == null || !filter.Enabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] packet = { 0x4F, (byte) value };
|
||||
|
||||
Engine.SendPacketToClient( packet, packet.Length );
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand LinkUnlinkProfileCommand =>
|
||||
_linkUnlinkProfileCommand ?? ( _linkUnlinkProfileCommand = new RelayCommand( LinkUnlinkProfile ) );
|
||||
|
||||
public ICommand NewProfileCommand =>
|
||||
_newProfileCommand ?? ( _newProfileCommand = new RelayCommandAsync( NewProfile, o => true ) );
|
||||
|
||||
public Options Options
|
||||
{
|
||||
get => _options;
|
||||
set => SetProperty( ref _options, value );
|
||||
}
|
||||
|
||||
public ObservableCollection<string> Profiles
|
||||
{
|
||||
get => _profiles;
|
||||
set => SetProperty( ref _profiles, value );
|
||||
}
|
||||
|
||||
public ICommand RemoveSavedPasswordCommand =>
|
||||
_removeSavedPasswordCommand ?? ( _removeSavedPasswordCommand =
|
||||
new RelayCommand( RemoveSavedPassword, o => AssistantOptions.SavePasswords ) );
|
||||
|
||||
public Dictionary<string, string> SavedPasswords
|
||||
{
|
||||
get => _savedPasswords;
|
||||
set => SetProperty( ref _savedPasswords, value );
|
||||
}
|
||||
|
||||
public bool SavePasswords
|
||||
{
|
||||
get => AssistantOptions.SavePasswords;
|
||||
set => AssistantOptions.SavePasswords = value;
|
||||
}
|
||||
|
||||
public bool SavePasswordsOnlyBlank
|
||||
{
|
||||
get => AssistantOptions.SavePasswordsOnlyBlank;
|
||||
set => AssistantOptions.SavePasswordsOnlyBlank = value;
|
||||
}
|
||||
|
||||
public ICommand SaveProfileCommand =>
|
||||
_saveProfileCommand ?? ( _saveProfileCommand = new RelayCommand( SaveProfile ) );
|
||||
|
||||
public string SelectedProfile
|
||||
{
|
||||
get => _selectedProfile;
|
||||
set => SetProperty( ref _selectedProfile, value );
|
||||
}
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
JObject obj = new JObject
|
||||
{
|
||||
["AlwaysOnTop"] = Options.CurrentOptions.AlwaysOnTop,
|
||||
["LightLevel"] = Options.CurrentOptions.LightLevel,
|
||||
["ActionDelay"] = Options.CurrentOptions.ActionDelay,
|
||||
["ActionDelayMS"] = Options.CurrentOptions.ActionDelayMS,
|
||||
["Debug"] = Options.CurrentOptions.Debug
|
||||
};
|
||||
|
||||
JArray filtersArray = new JArray();
|
||||
|
||||
foreach ( FilterEntry filterEntry in Filters )
|
||||
{
|
||||
JObject filterObj = new JObject
|
||||
{
|
||||
{ "Name", filterEntry.GetType().ToString() }, { "Enabled", filterEntry.Enabled }
|
||||
};
|
||||
|
||||
if ( filterEntry is IConfigurableFilter configurableFilter )
|
||||
{
|
||||
JObject options = configurableFilter.Serialize();
|
||||
filterObj.Add( "Options", options );
|
||||
}
|
||||
|
||||
filtersArray.Add( filterObj );
|
||||
}
|
||||
|
||||
obj.Add( "Filters", filtersArray );
|
||||
|
||||
json?.Add( "General", obj );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Options = options;
|
||||
|
||||
// Reset current filters to default value
|
||||
foreach ( FilterEntry filterEntry in Filters )
|
||||
{
|
||||
FilterOptionsAttribute a =
|
||||
(FilterOptionsAttribute) Attribute.GetCustomAttribute( filterEntry.GetType(),
|
||||
typeof( FilterOptionsAttribute ) );
|
||||
|
||||
if ( a == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
filterEntry.Enabled = a.DefaultEnabled;
|
||||
|
||||
if ( filterEntry is IConfigurableFilter configurableFilter )
|
||||
{
|
||||
configurableFilter.ResetOptions();
|
||||
}
|
||||
}
|
||||
|
||||
if ( json?["General"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JToken general = json["General"];
|
||||
|
||||
Options.LightLevel = general["LightLevel"]?.ToObject<int>() ?? 100;
|
||||
Options.ActionDelay = general["ActionDelay"]?.ToObject<bool>() ?? false;
|
||||
Options.ActionDelayMS = general["ActionDelayMS"]?.ToObject<int>() ?? 900;
|
||||
Options.AlwaysOnTop = general["AlwaysOnTop"]?.ToObject<bool>() ?? false;
|
||||
Options.Debug = general["Debug"]?.ToObject<bool>() ?? false;
|
||||
|
||||
if ( general["Filters"] == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( JToken token in general["Filters"] )
|
||||
{
|
||||
string filterName = token["Name"]?.ToObject<string>() ?? string.Empty;
|
||||
bool enabled = token["Enabled"]?.ToObject<bool>() ?? false;
|
||||
|
||||
FilterEntry filter = Filters.FirstOrDefault( f => f.GetType().ToString().Equals( filterName ) );
|
||||
|
||||
if ( filter != null )
|
||||
{
|
||||
filter.Enabled = enabled;
|
||||
}
|
||||
|
||||
if ( filter is IConfigurableFilter configurableFilter && token["Options"] != null )
|
||||
{
|
||||
configurableFilter.Deserialize( token["Options"] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSavedPasswordsChangedEvent( object sender, EventArgs e )
|
||||
{
|
||||
Dictionary<string, string> newList =
|
||||
AssistantOptions.SavedPasswords.ToDictionary( kvp => kvp.Key, kvp => kvp.Value );
|
||||
|
||||
SavedPasswords = newList;
|
||||
}
|
||||
|
||||
private void RemoveSavedPassword( object obj )
|
||||
{
|
||||
if ( !( obj is KeyValuePair<string, string> kvp ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SavedPasswords.Remove( kvp.Key );
|
||||
AssistantOptions.SavedPasswords.Remove( kvp.Key );
|
||||
AssistantOptions.OnPasswordsChanged();
|
||||
}
|
||||
|
||||
private static void ConfigureFilter( object obj )
|
||||
{
|
||||
if ( !( obj is IConfigurableFilter configurableFilter ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
configurableFilter.Configure();
|
||||
}
|
||||
|
||||
private void OnProfileChangedEvent( string profile )
|
||||
{
|
||||
_dispatcher.Invoke( () =>
|
||||
{
|
||||
Options = Options.CurrentOptions;
|
||||
|
||||
if ( Engine.Player != null )
|
||||
{
|
||||
IsLinkedProfile = AssistantOptions.GetLinkedProfile( Engine.Player.Serial ) == profile;
|
||||
}
|
||||
|
||||
SelectedProfile = profile;
|
||||
} );
|
||||
}
|
||||
|
||||
private static void SaveProfile( object obj )
|
||||
{
|
||||
Options.Save( Options.CurrentOptions );
|
||||
Commands.SystemMessage( Strings.Profile_saved___ );
|
||||
}
|
||||
|
||||
private void RefreshProfiles()
|
||||
{
|
||||
string[] profiles = Options.GetProfiles();
|
||||
|
||||
if ( profiles == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Profiles.Clear();
|
||||
|
||||
foreach ( string profile in profiles )
|
||||
{
|
||||
Profiles.Add( Path.GetFileName( profile ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeProfile( object obj )
|
||||
{
|
||||
if ( !( obj is string profileName ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MacroManager.GetInstance().StopAll();
|
||||
LoadProfile( profileName );
|
||||
Engine.UpdateWindowTitle();
|
||||
}
|
||||
|
||||
private void LinkUnlinkProfile( object obj )
|
||||
{
|
||||
if ( Engine.Player == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( AssistantOptions.GetLinkedProfile( Engine.Player.Serial ) == Options.CurrentOptions.Name )
|
||||
{
|
||||
AssistantOptions.RemoveLinkedProfile( Engine.Player.Serial );
|
||||
IsLinkedProfile = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssistantOptions.SetLinkedProfile( Engine.Player.Serial, Options.CurrentOptions.Name );
|
||||
IsLinkedProfile = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadProfile( string profile )
|
||||
{
|
||||
foreach ( FilterEntry filterEntry in Filters )
|
||||
{
|
||||
filterEntry?.Action( false );
|
||||
}
|
||||
|
||||
Options.ClearOptions();
|
||||
Options.CurrentOptions = new Options();
|
||||
Options.Load( profile, Options.CurrentOptions );
|
||||
AssistantOptions.LastProfile = profile;
|
||||
|
||||
if ( Engine.Player != null )
|
||||
{
|
||||
IsLinkedProfile = AssistantOptions.GetLinkedProfile( Engine.Player.Serial ) == profile;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NewProfile( object arg )
|
||||
{
|
||||
NewProfileViewModel vm = new NewProfileViewModel();
|
||||
|
||||
await Engine.UIInvoker.InvokeDialog( "NewProfileWindow", dataContext: vm );
|
||||
|
||||
if (!string.IsNullOrEmpty( vm.FileName ) )
|
||||
{
|
||||
RefreshProfiles();
|
||||
|
||||
SelectedProfile = vm.FileName;
|
||||
|
||||
ChangeProfile( vm.FileName );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Specialized;
|
||||
using ClassicAssist.Data.Hotkeys;
|
||||
using ClassicAssist.Data.Hotkeys.Commands;
|
||||
using ClassicAssist.UI.Misc;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public abstract class HotkeyEntryViewModel<T> : BaseViewModel where T : HotkeyEntry
|
||||
{
|
||||
private readonly HotkeyCommand _category;
|
||||
private ObservableCollectionEx<T> _items = new ObservableCollectionEx<T>();
|
||||
|
||||
protected HotkeyEntryViewModel( string name )
|
||||
{
|
||||
_category = new HotkeyCommand { Name = name, IsCategory = true };
|
||||
|
||||
HotkeyManager hotkey = HotkeyManager.GetInstance();
|
||||
|
||||
hotkey.AddCategory( _category );
|
||||
|
||||
Items.CollectionChanged += OnCollectionChanged;
|
||||
|
||||
_category.Children = new ObservableCollectionEx<HotkeyEntry>();
|
||||
}
|
||||
|
||||
public ObservableCollectionEx<T> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
~HotkeyEntryViewModel()
|
||||
{
|
||||
Items.CollectionChanged -= OnCollectionChanged;
|
||||
}
|
||||
|
||||
protected virtual void OnCollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
|
||||
{
|
||||
_category.Children = new ObservableCollectionEx<HotkeyEntry>();
|
||||
|
||||
foreach ( T item in Items )
|
||||
{
|
||||
_category.Children.Add( item );
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetJsonValue( JToken json, string name, JToken value )
|
||||
{
|
||||
json[name] = value;
|
||||
}
|
||||
|
||||
protected T2 GetJsonValue<T2>( JToken json, string name, T2 defaultValue )
|
||||
{
|
||||
if ( json == null )
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return json[name] == null ? defaultValue : json[name].ToObject<T2>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reflection;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Hotkeys;
|
||||
using ClassicAssist.Data.Hotkeys.Commands;
|
||||
using ClassicAssist.Data.Spells;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.Misc;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class HotkeysTabViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private readonly HotkeyManager _hotkeyManager;
|
||||
private readonly List<HotkeyCommand> _serializeCategories = new List<HotkeyCommand>();
|
||||
private ReactiveCommand<HotkeyEntry, Unit> _clearHotkeyCommand;
|
||||
private ReactiveCommand<HotkeyEntry, Unit> _executeCommand;
|
||||
private HotkeyCommand _masteriesCategory;
|
||||
private HotkeyEntry _selectedItem;
|
||||
private HotkeyCommand _spellsCategory;
|
||||
|
||||
public HotkeysTabViewModel()
|
||||
{
|
||||
_hotkeyManager = HotkeyManager.GetInstance();
|
||||
_hotkeyManager.ClearAllHotkeys = ClearAllHotkeys;
|
||||
}
|
||||
|
||||
public ReactiveCommand<HotkeyEntry, Unit> ClearHotkeyCommand =>
|
||||
_clearHotkeyCommand ?? ( _clearHotkeyCommand = ReactiveCommand.Create<HotkeyEntry>( ClearHotkey,
|
||||
this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) );
|
||||
|
||||
public ReactiveCommand<HotkeyEntry, Unit> ExecuteCommand =>
|
||||
_executeCommand ?? ( _executeCommand = ReactiveCommand.Create<HotkeyEntry>( ExecuteHotkey,
|
||||
this.WhenAnyValue( e => e.SelectedItem, e => e != null && !e.IsCategory ) ) );
|
||||
|
||||
public ShortcutKeys Hotkey
|
||||
{
|
||||
get => SelectedItem?.Hotkey;
|
||||
set => CheckOverwriteHotkey( SelectedItem, value );
|
||||
}
|
||||
|
||||
public ObservableCollectionEx<HotkeyCommand> Items
|
||||
{
|
||||
get => _hotkeyManager.Items;
|
||||
set => _hotkeyManager.Items = value;
|
||||
}
|
||||
|
||||
public HotkeyEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
SetProperty( ref _selectedItem, value == null || !value.IsCategory ? value : null );
|
||||
NotifyPropertyChanged( nameof( Hotkey ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
JObject hotkeys = new JObject();
|
||||
|
||||
JArray commandsArray = new JArray();
|
||||
|
||||
foreach ( HotkeyCommand category in _serializeCategories )
|
||||
{
|
||||
foreach ( HotkeyEntry categoryChild in category.Children )
|
||||
{
|
||||
if ( Equals( categoryChild.Hotkey, ShortcutKeys.Default ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
JObject entry = new JObject
|
||||
{
|
||||
{ "Type", categoryChild.GetType().FullName },
|
||||
{ "Keys", categoryChild.Hotkey.ToJObject() },
|
||||
{ "PassToUO", categoryChild.PassToUO }
|
||||
};
|
||||
|
||||
commandsArray.Add( entry );
|
||||
}
|
||||
}
|
||||
|
||||
hotkeys.Add( "Commands", commandsArray );
|
||||
|
||||
JArray spellsArray = new JArray();
|
||||
|
||||
foreach ( HotkeyEntry spellsCategoryChild in _spellsCategory.Children )
|
||||
{
|
||||
if ( Equals( spellsCategoryChild.Hotkey, ShortcutKeys.Default ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
JObject entry = new JObject
|
||||
{
|
||||
{ "Name", spellsCategoryChild.Name },
|
||||
{ "Keys", spellsCategoryChild.Hotkey.ToJObject() },
|
||||
{ "PassToUO", spellsCategoryChild.PassToUO }
|
||||
};
|
||||
|
||||
spellsArray.Add( entry );
|
||||
}
|
||||
|
||||
hotkeys.Add( "Spells", spellsArray );
|
||||
|
||||
JArray masteryArray = new JArray();
|
||||
|
||||
foreach ( HotkeyEntry masteriesCategoryChild in _masteriesCategory.Children )
|
||||
{
|
||||
if ( Equals( masteriesCategoryChild.Hotkey, ShortcutKeys.Default ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
JObject entry = new JObject
|
||||
{
|
||||
{ "Name", masteriesCategoryChild.Name },
|
||||
{ "Keys", masteriesCategoryChild.Hotkey.ToJObject() },
|
||||
{ "PassToUO", masteriesCategoryChild.PassToUO }
|
||||
};
|
||||
|
||||
masteryArray.Add( entry );
|
||||
}
|
||||
|
||||
hotkeys.Add( "Masteries", masteryArray );
|
||||
|
||||
json?.Add( "Hotkeys", hotkeys );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
IEnumerable<Type> hotkeyCommands = Assembly.GetExecutingAssembly().GetTypes()
|
||||
.Where( i => i.IsSubclassOf( typeof( HotkeyCommand ) ) );
|
||||
|
||||
foreach ( Type hotkeyCommand in hotkeyCommands )
|
||||
{
|
||||
HotkeyCommand hkc = (HotkeyCommand) Activator.CreateInstance( hotkeyCommand );
|
||||
|
||||
HotkeyCommandAttribute attr = hkc.GetType().GetCustomAttribute<HotkeyCommandAttribute>();
|
||||
|
||||
string categoryName = Strings.Commands;
|
||||
|
||||
if ( attr != null && !string.IsNullOrEmpty( attr.Category ) )
|
||||
{
|
||||
categoryName = Strings.ResourceManager.GetString( attr.Category );
|
||||
}
|
||||
|
||||
HotkeyCommand category = Items.FirstOrDefault( hke => hke.Name == categoryName && hke.IsCategory );
|
||||
|
||||
if ( category != null )
|
||||
{
|
||||
if ( category.Children == null )
|
||||
{
|
||||
category.Children = new ObservableCollectionEx<HotkeyEntry>();
|
||||
}
|
||||
|
||||
if ( category.Children.Contains( hkc ) )
|
||||
{
|
||||
category.Children.Remove( hkc );
|
||||
}
|
||||
|
||||
category.Children.Add( hkc );
|
||||
}
|
||||
else
|
||||
{
|
||||
category = new HotkeyCommand
|
||||
{
|
||||
Name = categoryName, IsCategory = true, Children = new ObservableCollectionEx<HotkeyEntry>()
|
||||
};
|
||||
|
||||
category.Children.Add( hkc );
|
||||
_hotkeyManager.AddCategory( category );
|
||||
_serializeCategories.Add( category );
|
||||
}
|
||||
}
|
||||
|
||||
JToken hotkeys = json?["Hotkeys"];
|
||||
|
||||
if ( hotkeys?["Commands"] != null )
|
||||
{
|
||||
foreach ( JToken token in hotkeys["Commands"] )
|
||||
{
|
||||
JToken type = token["Type"];
|
||||
|
||||
foreach ( HotkeyCommand category in _serializeCategories )
|
||||
{
|
||||
HotkeyEntry entry =
|
||||
category.Children.FirstOrDefault( o => o.GetType().FullName == type.ToObject<string>() );
|
||||
|
||||
if ( entry == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
JToken keys = token["Keys"];
|
||||
|
||||
entry.Hotkey = new ShortcutKeys( keys );
|
||||
entry.PassToUO = token["PassToUO"]?.ToObject<bool>() ?? true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( _spellsCategory != null )
|
||||
{
|
||||
_hotkeyManager.Items.Remove( _spellsCategory );
|
||||
}
|
||||
|
||||
_spellsCategory = new HotkeyCommand { Name = Strings.Spells, IsCategory = true };
|
||||
|
||||
SpellManager spellManager = SpellManager.GetInstance();
|
||||
|
||||
SpellData[] spells = spellManager.GetSpellData();
|
||||
|
||||
ObservableCollectionEx<HotkeyEntry> children = new ObservableCollectionEx<HotkeyEntry>();
|
||||
|
||||
foreach ( SpellData spell in spells )
|
||||
{
|
||||
HotkeyCommand hkc = new HotkeyCommand
|
||||
{
|
||||
Name = spell.Name,
|
||||
Action = hks => spellManager.CastSpell( spell.ID ),
|
||||
Hotkey = ShortcutKeys.Default,
|
||||
PassToUO = true
|
||||
};
|
||||
|
||||
children.Add( hkc );
|
||||
}
|
||||
|
||||
_spellsCategory.Children = children;
|
||||
|
||||
_hotkeyManager.AddCategory( _spellsCategory );
|
||||
|
||||
JToken spellsObj = hotkeys?["Spells"];
|
||||
|
||||
if ( spellsObj != null )
|
||||
{
|
||||
foreach ( JToken token in spellsObj )
|
||||
{
|
||||
JToken name = token["Name"];
|
||||
|
||||
HotkeyEntry entry =
|
||||
_spellsCategory.Children.FirstOrDefault( s => s.Name == name.ToObject<string>() );
|
||||
|
||||
if ( entry == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.Hotkey = new ShortcutKeys( token["Keys"] );
|
||||
entry.PassToUO = token["PassToUO"]?.ToObject<bool>() ?? true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( _masteriesCategory != null )
|
||||
{
|
||||
_hotkeyManager.Items.Remove( _masteriesCategory );
|
||||
}
|
||||
|
||||
_masteriesCategory = new HotkeyCommand { Name = Strings.Masteries, IsCategory = true };
|
||||
|
||||
SpellData[] masteries = spellManager.GetMasteryData();
|
||||
|
||||
ObservableCollectionEx<HotkeyEntry> masteryChildren = new ObservableCollectionEx<HotkeyEntry>();
|
||||
|
||||
foreach ( SpellData mastery in masteries )
|
||||
{
|
||||
HotkeyCommand hkc = new HotkeyCommand
|
||||
{
|
||||
Name = mastery.Name,
|
||||
Action = hks => spellManager.CastSpell( mastery.ID ),
|
||||
Hotkey = ShortcutKeys.Default,
|
||||
PassToUO = true
|
||||
};
|
||||
|
||||
masteryChildren.Add( hkc );
|
||||
}
|
||||
|
||||
_masteriesCategory.Children = masteryChildren;
|
||||
|
||||
_hotkeyManager.AddCategory( _masteriesCategory );
|
||||
|
||||
JToken masteryObj = hotkeys?["Masteries"];
|
||||
|
||||
if ( masteryObj != null )
|
||||
{
|
||||
foreach ( JToken token in masteryObj )
|
||||
{
|
||||
JToken name = token["Name"];
|
||||
|
||||
HotkeyEntry entry =
|
||||
_masteriesCategory.Children.FirstOrDefault( s => s.Name == name.ToObject<string>() );
|
||||
|
||||
if ( entry == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.Hotkey = new ShortcutKeys( token["Keys"] );
|
||||
entry.PassToUO = token["PassToUO"]?.ToObject<bool>() ?? true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void CheckOverwriteHotkey( HotkeyEntry selectedItem, ShortcutKeys hotkey )
|
||||
{
|
||||
HotkeyEntry conflict = null;
|
||||
|
||||
foreach ( HotkeyCommand hotkeyEntry in Items )
|
||||
{
|
||||
foreach ( HotkeyEntry entry in hotkeyEntry.Children )
|
||||
{
|
||||
if ( entry.Hotkey.Equals( hotkey ) )
|
||||
{
|
||||
conflict = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( conflict != null && !ReferenceEquals( selectedItem, conflict ) )
|
||||
{
|
||||
MessageBoxResult result = await Engine.MessageBoxProvider.Show(
|
||||
string.Format( Strings.Overwrite_existing_hotkey___0____, conflict ), Strings.Warning,
|
||||
MessageBoxButtons.YesNo );
|
||||
|
||||
if ( result == MessageBoxResult.No )
|
||||
{
|
||||
NotifyPropertyChanged( nameof( Hotkey ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SelectedItem.Hotkey = hotkey;
|
||||
NotifyPropertyChanged( nameof( Hotkey ) );
|
||||
}
|
||||
|
||||
private void ClearAllHotkeys()
|
||||
{
|
||||
foreach ( HotkeyEntry entryChild in _serializeCategories.SelectMany( hotkeyEntry => hotkeyEntry.Children ) )
|
||||
{
|
||||
entryChild.Hotkey = ShortcutKeys.Default;
|
||||
}
|
||||
|
||||
foreach ( HotkeyEntry spellEntry in _spellsCategory.Children )
|
||||
{
|
||||
spellEntry.Hotkey = ShortcutKeys.Default;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearHotkey( object obj )
|
||||
{
|
||||
if ( obj is HotkeyEntry cmd )
|
||||
{
|
||||
cmd.Hotkey = ShortcutKeys.Default;
|
||||
this.RaisePropertyChanged( nameof( Hotkey ) );
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExecuteHotkey( object obj )
|
||||
{
|
||||
if ( obj is HotkeyEntry cmd )
|
||||
{
|
||||
cmd.Action( cmd );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Hotkeys;
|
||||
using ClassicAssist.Data.Macros;
|
||||
using ClassicAssist.Data.Macros.Commands;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.Shared.UO;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class MacrosTabViewModel : HotkeyEntryViewModel<MacroEntry>, ISettingProvider
|
||||
{
|
||||
private readonly MacroManager _manager;
|
||||
private int _caretPosition;
|
||||
|
||||
private ReactiveCommand<MacroEntry, Unit> _clearHotkeyCommand;
|
||||
|
||||
//TODO
|
||||
//private TextDocument _document;
|
||||
private ReactiveCommand<MacroEntry, Unit> _executeCommand;
|
||||
private ICommand _inspectObjectCommand;
|
||||
private bool _isRecording;
|
||||
private RelayCommand _newMacroCommand;
|
||||
private ICommand _recordCommand;
|
||||
private RelayCommand _removeMacroCommand;
|
||||
private ReactiveCommand<MacroEntry, Unit> _removeMacroConfirmCommand;
|
||||
private ICommand _saveMacroCommand;
|
||||
private MacroEntry _selectedItem;
|
||||
private ICommand _showActiveObjectsWindowCommand;
|
||||
private ICommand _showCommandsCommand;
|
||||
private ICommand _showMacrosWikiCommand;
|
||||
private ReactiveCommand<MacroEntry, Unit> _stopCommand;
|
||||
|
||||
public MacrosTabViewModel() : base( Strings.Macros )
|
||||
{
|
||||
Engine.DisconnectedEvent += OnDisconnectedEvent;
|
||||
|
||||
_manager = MacroManager.GetInstance();
|
||||
|
||||
_manager.IsRecording = () => _isRecording;
|
||||
_manager.InsertDocument = str => { _dispatcher.Invoke( () => { SelectedItem.Macro += str; } ); };
|
||||
_manager.NewMacro = NewMacro;
|
||||
_manager.Items = Items;
|
||||
}
|
||||
|
||||
public int CaretPosition
|
||||
{
|
||||
get => _caretPosition;
|
||||
set => SetProperty( ref _caretPosition, value );
|
||||
}
|
||||
|
||||
public ReactiveCommand<MacroEntry, Unit> ClearHotkeyCommand =>
|
||||
_clearHotkeyCommand ?? ( _clearHotkeyCommand = ReactiveCommand.Create<MacroEntry>( ClearHotkey,
|
||||
this.WhenAnyValue( e => e.SelectedItem, selector: e => e != null ) ) );
|
||||
|
||||
//TODO
|
||||
//public TextDocument Document
|
||||
//{
|
||||
// get => _document;
|
||||
// set => SetProperty( ref _document, value );
|
||||
//}
|
||||
|
||||
public ReactiveCommand<MacroEntry, Unit> ExecuteCommand =>
|
||||
_executeCommand ?? ( _executeCommand = ReactiveCommand.CreateFromTask<MacroEntry>( Execute,
|
||||
this.WhenAnyValue( x => x.IsRecording, x => x.SelectedItem, x => x.SelectedItem.IsRunning,
|
||||
( b, entry, running ) => !b && entry != null && !running ) ) );
|
||||
|
||||
public ShortcutKeys Hotkey
|
||||
{
|
||||
get => SelectedItem?.Hotkey;
|
||||
set => CheckOverwriteHotkey( SelectedItem, value );
|
||||
}
|
||||
|
||||
//TODO
|
||||
public ICommand InspectObjectCommand =>
|
||||
_inspectObjectCommand ?? ( _inspectObjectCommand = new RelayCommandAsync( InspectObject, o => true ) );
|
||||
|
||||
public bool IsRecording
|
||||
{
|
||||
get => _isRecording;
|
||||
set => SetProperty( ref _isRecording, value );
|
||||
}
|
||||
|
||||
public RelayCommand NewMacroCommand =>
|
||||
_newMacroCommand ??
|
||||
( _newMacroCommand = new RelayCommand( NewMacro, o => !SelectedItem?.IsRunning ?? true ) );
|
||||
|
||||
public ICommand RecordCommand =>
|
||||
_recordCommand ?? ( _recordCommand = new RelayCommand( Record, o => SelectedItem != null ) );
|
||||
|
||||
public string RecordLabel => IsRecording ? Strings.Stop : Strings.Record;
|
||||
|
||||
public RelayCommand RemoveMacroCommand =>
|
||||
_removeMacroCommand ?? ( _removeMacroCommand =
|
||||
new RelayCommand( RemoveMacro, o => !SelectedItem?.IsRunning ?? SelectedItem != null ) );
|
||||
|
||||
public ReactiveCommand<MacroEntry, Unit> RemoveMacroConfirmCommand =>
|
||||
_removeMacroConfirmCommand ?? ( _removeMacroConfirmCommand =
|
||||
ReactiveCommand.Create<MacroEntry, Unit>( RemoveMacroConfirm,
|
||||
this.WhenAnyValue( o => o.SelectedItem, selector: o => o != null ) ) );
|
||||
|
||||
public ICommand SaveMacroCommand =>
|
||||
_saveMacroCommand ?? ( _saveMacroCommand = new RelayCommand( SaveMacro, o => true ) );
|
||||
|
||||
public MacroEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
SetProperty( ref _selectedItem, value );
|
||||
NotifyPropertyChanged( nameof( Hotkey ) );
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand ShowActiveObjectsWindowCommand =>
|
||||
_showActiveObjectsWindowCommand ?? ( _showActiveObjectsWindowCommand =
|
||||
new RelayCommand( ShowActiveObjectsWindow, o => true ) );
|
||||
|
||||
public ICommand ShowCommandsCommand =>
|
||||
_showCommandsCommand ?? ( _showCommandsCommand = new RelayCommand( ShowCommands, o => true ) );
|
||||
|
||||
public ICommand ShowMacrosWikiCommand =>
|
||||
_showMacrosWikiCommand ?? ( _showMacrosWikiCommand = new RelayCommand( ShowMacrosWiki, o => true ) );
|
||||
|
||||
//public ICommand StopCommand =>
|
||||
// _stopCommand ?? ( _stopCommand = new RelayCommandAsync( Stop, o => SelectedItem?.IsRunning ?? false ) );
|
||||
|
||||
public ReactiveCommand<MacroEntry, Unit> StopCommand =>
|
||||
_stopCommand ?? ( _stopCommand = ReactiveCommand.CreateFromTask<MacroEntry>( Stop,
|
||||
this.WhenAnyValue( e => e.SelectedItem.IsRunning ) ) );
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
JObject macros = new JObject();
|
||||
|
||||
JArray macroArray = new JArray();
|
||||
|
||||
IEnumerable<MacroEntry> globalMacros = Items.Where( e => e.Global );
|
||||
|
||||
if ( globalMacros.Any() )
|
||||
{
|
||||
string globalJson = JsonConvert.SerializeObject( globalMacros, Formatting.Indented );
|
||||
|
||||
File.WriteAllText( Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Macros.json" ),
|
||||
globalJson );
|
||||
}
|
||||
|
||||
foreach ( MacroEntry macroEntry in Items.Where( e => !e.Global ) )
|
||||
{
|
||||
JObject entry = new JObject
|
||||
{
|
||||
{ "Name", macroEntry.Name },
|
||||
{ "Loop", macroEntry.Loop },
|
||||
{ "DoNotAutoInterrupt", macroEntry.DoNotAutoInterrupt },
|
||||
{ "Macro", macroEntry.Macro },
|
||||
{ "PassToUO", macroEntry.PassToUO },
|
||||
{ "Keys", macroEntry.Hotkey.ToJObject() },
|
||||
{ "IsBackground", macroEntry.IsBackground },
|
||||
{ "IsAutostart", macroEntry.IsAutostart },
|
||||
{ "Disableable", macroEntry.Disableable }
|
||||
};
|
||||
|
||||
JArray aliasesArray = new JArray();
|
||||
|
||||
foreach ( JObject aliasObj in macroEntry.Aliases.Select( kvp =>
|
||||
new JObject { { "Key", kvp.Key }, { "Value", kvp.Value } } ) )
|
||||
{
|
||||
aliasesArray.Add( aliasObj );
|
||||
}
|
||||
|
||||
entry.Add( "Aliases", aliasesArray );
|
||||
|
||||
macroArray.Add( entry );
|
||||
}
|
||||
|
||||
macros.Add( "Macros", macroArray );
|
||||
|
||||
JArray aliasArray = new JArray();
|
||||
|
||||
foreach ( JObject entry in AliasCommands.GetAllAliases()
|
||||
.Select( kvp => new JObject { { "Name", kvp.Key }, { "Value", kvp.Value } } ) )
|
||||
{
|
||||
aliasArray.Add( entry );
|
||||
}
|
||||
|
||||
macros.Add( "Alias", aliasArray );
|
||||
|
||||
json?.Add( "Macros", macros );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
Items.Clear();
|
||||
|
||||
string globalPath = Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Macros.json" );
|
||||
|
||||
if ( File.Exists( globalPath ) )
|
||||
{
|
||||
string globalJson = File.ReadAllText( globalPath );
|
||||
|
||||
MacroEntry[] globalMacros = JsonConvert.DeserializeObject<MacroEntry[]>( globalJson );
|
||||
|
||||
if ( globalMacros != null )
|
||||
{
|
||||
foreach ( MacroEntry entry in globalMacros )
|
||||
{
|
||||
entry.Action = async hks => await Execute( entry );
|
||||
|
||||
if ( Options.CurrentOptions.SortMacrosAlphabetical )
|
||||
{
|
||||
Items.AddSorted( entry );
|
||||
}
|
||||
else
|
||||
{
|
||||
Items.Add( entry );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JToken config = json?["Macros"];
|
||||
|
||||
if ( config == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( config["Macros"] != null )
|
||||
{
|
||||
foreach ( JToken token in config["Macros"] )
|
||||
{
|
||||
MacroEntry entry = new MacroEntry
|
||||
{
|
||||
Name = GetJsonValue( token, "Name", string.Empty ),
|
||||
Loop = GetJsonValue( token, "Loop", false ),
|
||||
DoNotAutoInterrupt = GetJsonValue( token, "DoNotAutoInterrupt", false ),
|
||||
Macro = GetJsonValue( token, "Macro", string.Empty ),
|
||||
PassToUO = GetJsonValue( token, "PassToUO", true ),
|
||||
Hotkey = new ShortcutKeys( token["Keys"] ),
|
||||
IsBackground = GetJsonValue( token, "IsBackground", false ),
|
||||
IsAutostart = GetJsonValue( token, "IsAutostart", false ),
|
||||
Disableable = GetJsonValue( token, "Disableable", true )
|
||||
};
|
||||
|
||||
if ( token["Aliases"] != null )
|
||||
{
|
||||
foreach ( JToken aliasToken in token["Aliases"] )
|
||||
{
|
||||
entry.Aliases.Add( aliasToken["Key"].ToObject<string>(),
|
||||
aliasToken["Value"].ToObject<int>() );
|
||||
}
|
||||
}
|
||||
|
||||
// Global macro takes precedence for hotkey
|
||||
if ( Items.Any( e => Equals( e.Hotkey, entry.Hotkey ) && e.Global ) )
|
||||
{
|
||||
entry.Hotkey = ShortcutKeys.Default;
|
||||
}
|
||||
|
||||
entry.Action = async hks =>
|
||||
await Engine.Dispatcher.InvokeAsync( async () => await Execute( entry ) );
|
||||
|
||||
if ( Options.CurrentOptions.SortMacrosAlphabetical )
|
||||
{
|
||||
Items.AddSorted( entry );
|
||||
}
|
||||
else
|
||||
{
|
||||
Items.Add( entry );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( config["Alias"] != null )
|
||||
{
|
||||
foreach ( JToken token in config["Alias"] )
|
||||
{
|
||||
AliasCommands.SetAlias( token["Name"].ToObject<string>(), token["Value"].ToObject<int>() );
|
||||
}
|
||||
}
|
||||
|
||||
string modulePath = Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Modules" );
|
||||
|
||||
if ( !Directory.Exists( modulePath ) )
|
||||
{
|
||||
Directory.CreateDirectory( modulePath );
|
||||
}
|
||||
|
||||
string assistantModule = Path.Combine( modulePath, "Assistant.py" );
|
||||
|
||||
if ( !File.Exists( assistantModule ) )
|
||||
{
|
||||
File.WriteAllText( assistantModule, "from ClassicAssist.Shared import Engine as _Engine\nEngine = _Engine" );
|
||||
}
|
||||
}
|
||||
|
||||
private Unit RemoveMacroConfirm( object obj )
|
||||
{
|
||||
if ( !( obj is MacroEntry entry ) )
|
||||
{
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
//TODO
|
||||
//MessageBoxResult result = MessageBox.Show( string.Format( Strings.Really_remove_macro___0___, entry.Name ),
|
||||
// Strings.Warning, MessageBoxButton.YesNo, MessageBoxImage.Warning );
|
||||
|
||||
//if ( result == MessageBoxResult.No )
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
RemoveMacro( entry );
|
||||
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
private static void ShowMacrosWiki( object obj )
|
||||
{
|
||||
Process.Start( Strings.MACRO_WIKI_URL );
|
||||
}
|
||||
|
||||
private bool CanExecute( object arg )
|
||||
{
|
||||
if ( !( arg is MacroEntry entry ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( entry.IsRunning )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CheckOverwriteHotkey( HotkeyEntry selectedItem, ShortcutKeys hotkey )
|
||||
{
|
||||
HotkeyEntry conflict = null;
|
||||
|
||||
foreach ( HotkeyEntry hotkeyEntry in HotkeyManager.GetInstance().Items )
|
||||
{
|
||||
foreach ( HotkeyEntry entry in hotkeyEntry.Children )
|
||||
{
|
||||
if ( entry.Hotkey.Equals( hotkey ) )
|
||||
{
|
||||
conflict = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( conflict != null && !ReferenceEquals( selectedItem, conflict ) )
|
||||
{
|
||||
//TODO
|
||||
//MessageBoxResult result =
|
||||
// MessageBox.Show( string.Format( Strings.Overwrite_existing_hotkey___0____, conflict ),
|
||||
// Strings.Warning, MessageBoxButton.YesNo );
|
||||
|
||||
//if ( result == MessageBoxResult.No )
|
||||
//{
|
||||
// NotifyPropertyChanged( nameof( Hotkey ) );
|
||||
// return;
|
||||
//}
|
||||
}
|
||||
|
||||
SelectedItem.Hotkey = hotkey;
|
||||
NotifyPropertyChanged( nameof( Hotkey ) );
|
||||
}
|
||||
|
||||
private static void SaveMacro( object obj )
|
||||
{
|
||||
//Saves whole profile, think of better way
|
||||
Options.Save( Options.CurrentOptions );
|
||||
}
|
||||
|
||||
private async Task Execute( object obj )
|
||||
{
|
||||
if ( !( obj is MacroEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_manager.Execute( entry );
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static void ShowActiveObjectsWindow( object obj )
|
||||
{
|
||||
//TODO UI
|
||||
//ActiveObjectsWindow window = new ActiveObjectsWindow();
|
||||
//window.Show();
|
||||
}
|
||||
|
||||
private void OnDisconnectedEvent()
|
||||
{
|
||||
_manager.StopAll();
|
||||
}
|
||||
|
||||
private void ClearHotkey( object obj )
|
||||
{
|
||||
if ( !( obj is MacroEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entry.Hotkey = ShortcutKeys.Default;
|
||||
|
||||
this.RaisePropertyChanged( nameof( Hotkey ) );
|
||||
}
|
||||
|
||||
private static async Task InspectObject( object arg )
|
||||
{
|
||||
await Commands.InspectObjectAsync();
|
||||
}
|
||||
|
||||
private void NewMacro( object obj )
|
||||
{
|
||||
int count = Items.Count;
|
||||
|
||||
MacroEntry macro = new MacroEntry { Name = $"Macro-{count + 1}", Macro = string.Empty };
|
||||
|
||||
macro.Action = async hks => await Execute( macro );
|
||||
|
||||
Items.Add( macro );
|
||||
}
|
||||
|
||||
private void NewMacro( string name, string macroText )
|
||||
{
|
||||
MacroEntry macro = new MacroEntry { Name = name, Macro = macroText };
|
||||
|
||||
macro.Action = async hks => await Execute( macro );
|
||||
|
||||
Items.Add( macro );
|
||||
|
||||
SelectedItem = macro;
|
||||
}
|
||||
|
||||
private void RemoveMacro( object obj )
|
||||
{
|
||||
if ( obj is MacroEntry entry )
|
||||
{
|
||||
Items.Remove( entry );
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Stop( object obj )
|
||||
{
|
||||
if ( !( obj is MacroEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entry.Stop();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ShowCommands( object obj )
|
||||
{
|
||||
//TODO UI
|
||||
//MacrosCommandWindow window = new MacrosCommandWindow { DataContext = new MacrosCommandViewModel( this ) };
|
||||
//window.ShowDialog();
|
||||
}
|
||||
|
||||
private void Record( object obj )
|
||||
{
|
||||
if ( IsRecording )
|
||||
{
|
||||
IsRecording = false;
|
||||
NotifyPropertyChanged( nameof( RecordLabel ) );
|
||||
return;
|
||||
}
|
||||
|
||||
IsRecording = true;
|
||||
NotifyPropertyChanged( nameof( RecordLabel ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.Misc;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class MainWindowViewModel : BaseViewModel
|
||||
{
|
||||
private bool _alwaysOnTop;
|
||||
private ICommand _debugCommand;
|
||||
//TODO UI
|
||||
//private DebugWindow _debugWindow;
|
||||
private string _status = Strings.Ready___;
|
||||
private string _title = Strings.ProductName;
|
||||
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
Engine.UpdateWindowTitleEvent += OnUpdateWindowTitleEvent;
|
||||
}
|
||||
|
||||
[OptionsBinding( Property = "AlwaysOnTop" )]
|
||||
public bool AlwaysOnTop
|
||||
{
|
||||
get => _alwaysOnTop;
|
||||
set => SetProperty( ref _alwaysOnTop, value );
|
||||
}
|
||||
|
||||
public ICommand DebugCommand =>
|
||||
_debugCommand ?? ( _debugCommand = new RelayCommand( ShowDebugWindow, o => true ) );
|
||||
|
||||
public string Status
|
||||
{
|
||||
get => _status;
|
||||
set => SetProperty( ref _status, value );
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => SetProperty( ref _title, value );
|
||||
}
|
||||
|
||||
private void OnUpdateWindowTitleEvent()
|
||||
{
|
||||
Title = string.IsNullOrEmpty( Engine.Player?.Name )
|
||||
? Strings.ProductName
|
||||
: $"{Engine.Player?.Name} - {( Options.CurrentOptions.ShowProfileNameWindowTitle ? $"({Options.CurrentOptions.Name}) - " : "" )}{Strings.ProductName}";
|
||||
}
|
||||
|
||||
private static void ShowDebugWindow( object obj )
|
||||
{
|
||||
Engine.UIInvoker.Invoke( "DebugWindow" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System.IO;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ClassicAssist.Shared.UI.ViewModels
|
||||
{
|
||||
public enum NewProfileOption
|
||||
{
|
||||
Blank,
|
||||
Duplicate
|
||||
}
|
||||
|
||||
public class NewProfileViewModel : BaseViewModel
|
||||
{
|
||||
private ICommand _changeOptionCommand;
|
||||
private string _name;
|
||||
private ICommand _okCommand;
|
||||
private NewProfileOption _option = NewProfileOption.Duplicate;
|
||||
|
||||
// Avalonia only
|
||||
public ICommand ChangeOptionCommand =>
|
||||
_changeOptionCommand ?? ( _changeOptionCommand = ReactiveCommand.Create<NewProfileOption>( ChangeOption ) );
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty( ref _name, value );
|
||||
}
|
||||
|
||||
public ICommand OkCommand =>
|
||||
_okCommand ?? ( _okCommand = new RelayCommand( Ok, o => !string.IsNullOrEmpty( Name ) ) );
|
||||
|
||||
public NewProfileOption Option
|
||||
{
|
||||
get => _option;
|
||||
set => SetProperty( ref _option, value );
|
||||
}
|
||||
|
||||
private void Ok( object obj )
|
||||
{
|
||||
string profileName = Name?.Trim();
|
||||
|
||||
bool valid = profileName?.IndexOfAny( Path.GetInvalidFileNameChars() ) == -1;
|
||||
|
||||
if ( valid )
|
||||
{
|
||||
FileName = $"{profileName}.json";
|
||||
|
||||
if ( Option == NewProfileOption.Duplicate )
|
||||
{
|
||||
Options options = Options.CurrentOptions;
|
||||
options.Name = $"{profileName}.json";
|
||||
Options.Save( options );
|
||||
}
|
||||
else
|
||||
{
|
||||
Options.ClearOptions();
|
||||
Options options = new Options { Name = $"{profileName}.json" };
|
||||
Options.CurrentOptions = options;
|
||||
Options.Load( options.Name, options );
|
||||
Options.Save( options );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine.MessageBoxProvider.Show( Strings.Profile_name_contains_illegal_characters_ );
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeOption( NewProfileOption obj )
|
||||
{
|
||||
Option = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.Models;
|
||||
using ClassicAssist.UO;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Objects;
|
||||
using Microsoft.Scripting.Utils;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class ObjectInspectorViewModel : BaseViewModel
|
||||
{
|
||||
private ICommand _copyToClipboardCommand;
|
||||
private ObservableCollection<ObjectInspectorData> _items = new ObservableCollection<ObjectInspectorData>();
|
||||
private ObjectInspectorData _selectedItem;
|
||||
|
||||
public ObjectInspectorViewModel()
|
||||
{
|
||||
Items.AddRange( new List<ObjectInspectorData>
|
||||
{
|
||||
new ObjectInspectorData { Name = "Name", Value = "Value", Category = "Category" }
|
||||
} );
|
||||
}
|
||||
|
||||
public ObjectInspectorViewModel( Entity entity )
|
||||
{
|
||||
AddEntityProperties( entity );
|
||||
|
||||
if ( entity is Mobile mobile )
|
||||
{
|
||||
AddMobileProperties( mobile );
|
||||
}
|
||||
|
||||
//AddPublicProperties( entity );
|
||||
}
|
||||
|
||||
public ObjectInspectorViewModel( StaticTile staticTile )
|
||||
{
|
||||
AddStaticProperties( staticTile );
|
||||
}
|
||||
|
||||
public ObjectInspectorViewModel( LandTile landTile )
|
||||
{
|
||||
AddLandProperties( landTile );
|
||||
}
|
||||
|
||||
public ICommand CopyToClipboardCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
return _copyToClipboardCommand ?? ( _copyToClipboardCommand =
|
||||
new RelayCommand( o => CopyToClipboard(), o => _selectedItem != null ) );
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ObjectInspectorData> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ObjectInspectorData SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
private void AddLandProperties( LandTile landTile )
|
||||
{
|
||||
AddData( typeof( Entity ), nameof( Entity.Name ), landTile.Name, Strings.Entity );
|
||||
AddData( "Flags", landTile.Flags.ToString(), Strings.Entity );
|
||||
AddData( typeof( Entity ), nameof( Entity.ID ), landTile.ID, Strings.Entity, Strings.Graphic );
|
||||
AddData( "Position", $"{landTile.X}, {landTile.Y}, {landTile.Z}", Strings.Entity );
|
||||
}
|
||||
|
||||
private void AddStaticProperties( StaticTile staticTile )
|
||||
{
|
||||
AddData( typeof( Entity ), nameof( Entity.Name ), staticTile.Name, Strings.Entity );
|
||||
AddData( "Flags", staticTile.Flags.ToString(), Strings.Entity );
|
||||
AddData( typeof( Entity ), nameof( Entity.ID ), staticTile.ID, Strings.Entity, Strings.Graphic );
|
||||
AddData( typeof( Entity ), nameof( Entity.Hue ), staticTile.Hue, Strings.Entity, Strings.Color );
|
||||
AddData( "Position", $"{staticTile.X}, {staticTile.Y}, {staticTile.Z}", Strings.Entity );
|
||||
AddData( "Weight", staticTile.Weight.ToString(), Strings.Entity );
|
||||
AddData( "Height", staticTile.Height.ToString(), Strings.Entity );
|
||||
AddData( "Quality", staticTile.Quality.ToString(), Strings.Entity );
|
||||
AddData( "Quantity", staticTile.Quantity.ToString(), Strings.Entity );
|
||||
}
|
||||
|
||||
private void AddEntityProperties( Entity entity )
|
||||
{
|
||||
AddData( typeof( Entity ), nameof( Entity.Name ), entity.Name, Strings.Entity );
|
||||
AddData( typeof( Entity ), nameof( Entity.Serial ), entity.Serial, Strings.Entity );
|
||||
AddData( typeof( Entity ), nameof( Entity.ID ), entity.ID, Strings.Entity, Strings.Graphic );
|
||||
AddData( typeof( Entity ), nameof( Entity.Hue ), entity.Hue, Strings.Entity, Strings.Color );
|
||||
AddData( "Position", $"{entity.X}, {entity.Y}, {entity.Z}", Strings.Entity );
|
||||
}
|
||||
|
||||
private void AddMobileProperties( Mobile mobile )
|
||||
{
|
||||
AddData( typeof( Mobile ), nameof( Mobile.Name ), mobile.Name, Strings.Mobile );
|
||||
AddData( "Sex", mobile.Status.HasFlag( MobileStatus.Female ) ? "Female" : "Male", Strings.Mobile );
|
||||
|
||||
if ( mobile is PlayerMobile player )
|
||||
{
|
||||
AddData( typeof( Mobile ), nameof( Mobile.Hits ), player.Hits, Strings.Mobile );
|
||||
AddData( typeof( Mobile ), nameof( Mobile.HitsMax ), player.HitsMax, Strings.Mobile, "Max Health" );
|
||||
}
|
||||
else
|
||||
{
|
||||
float health = mobile.Hits;
|
||||
float maxHealth = mobile.HitsMax;
|
||||
float per = health / maxHealth * 100;
|
||||
AddData( "Hits", ( (uint) per ).ToString() + '%', Strings.Mobile );
|
||||
}
|
||||
|
||||
AddData( typeof( Mobile ), nameof( Mobile.Notoriety ), mobile.Notoriety, Strings.Mobile );
|
||||
AddData( typeof( Mobile ), nameof( Mobile.Status ), mobile.Status, Strings.Mobile, "Flags" );
|
||||
|
||||
#region Equipment Properties
|
||||
|
||||
Item[] equipment = GetEquipmentItems( mobile );
|
||||
|
||||
if ( equipment == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddEquipmentProperty( equipment, new[] { 1060448, 1153735 }, new[] { 0, 0 }, "Physical Resist",
|
||||
Strings.Resistances );
|
||||
AddEquipmentProperty( equipment, new[] { 1060447, 1153737 }, new[] { 0, 0 }, "Fire Resist",
|
||||
Strings.Resistances );
|
||||
AddEquipmentProperty( equipment, new[] { 1060445, 1153739 }, new[] { 0, 0 }, "Cold Resist",
|
||||
Strings.Resistances );
|
||||
AddEquipmentProperty( equipment, new[] { 1060449, 1153736 }, new[] { 0, 0 }, "Poison Resist",
|
||||
Strings.Resistances );
|
||||
AddEquipmentProperty( equipment, new[] { 1060446, 1153738 }, new[] { 0, 0 }, "Energy Resist",
|
||||
Strings.Resistances );
|
||||
|
||||
AddEquipmentProperty( equipment, 1060413, 0, "Faster Casting", Strings.Magical );
|
||||
AddEquipmentProperty( equipment, 1060412, 0, "Faster Cast Recovery", Strings.Magical );
|
||||
AddEquipmentProperty( equipment, 1060434, 0, "Lower Reagent Cost", Strings.Magical );
|
||||
AddEquipmentProperty( equipment, 1060433, 0, "Lower Mana Cost", Strings.Magical );
|
||||
AddEquipmentProperty( equipment, 1060483, 0, "Spell Damage Increase", Strings.Magical );
|
||||
|
||||
AddEquipmentProperty( equipment, 1060408, 0, "Defense Chance Increase", Strings.Combat );
|
||||
AddEquipmentProperty( equipment, 1060415, 0, "Hit Chance Increase", Strings.Combat );
|
||||
AddEquipmentProperty( equipment, 1060401, 0, "Damage Increase", Strings.Combat );
|
||||
AddEquipmentProperty( equipment, 1060402, 0, "Damage Increase", Strings.Combat );
|
||||
AddEquipmentProperty( equipment, 1060486, 0, "Swing Speed Increase", Strings.Combat );
|
||||
|
||||
AddEquipmentProperty( equipment, 1060485, 0, "Strength Bonus", Strings.Stats );
|
||||
AddEquipmentProperty( equipment, 1060432, 0, "Intelligence Bonus", Strings.Stats );
|
||||
AddEquipmentProperty( equipment, 1060409, 0, "Dexterity Bonus", Strings.Stats );
|
||||
|
||||
AddEquipmentProperty( equipment, 1060431, 0, "Hit Point Increase", Strings.Stats );
|
||||
AddEquipmentProperty( equipment, 1060439, 0, "Mana Increase", Strings.Stats );
|
||||
AddEquipmentProperty( equipment, 1060484, 0, "Stamina Increase", Strings.Stats );
|
||||
|
||||
AddEquipmentProperty( equipment, 1060444, 0, "Hit Point Regeneration", Strings.Regeneration );
|
||||
AddEquipmentProperty( equipment, 1060440, 0, "Mana Regeneration", Strings.Regeneration );
|
||||
AddEquipmentProperty( equipment, 1060443, 0, "Stamina Regeneration", Strings.Regeneration );
|
||||
|
||||
AddEquipmentProperty( equipment, 1060436, 0, "Luck", "Misc" );
|
||||
|
||||
AddEquipmentPropertyWithSymbol( equipment, 1153733, 0, 1, "Max Defense Chance Increase",
|
||||
Strings.Buffs___Penalties );
|
||||
AddEquipmentPropertyWithSymbol( equipment, 1153735, 1, 2, "Physical Resist", Strings.Buffs___Penalties );
|
||||
AddEquipmentPropertyWithSymbol( equipment, 1153737, 1, 2, "Fire Resist", Strings.Buffs___Penalties );
|
||||
AddEquipmentPropertyWithSymbol( equipment, 1153739, 1, 2, "Cold Resist", Strings.Buffs___Penalties );
|
||||
AddEquipmentPropertyWithSymbol( equipment, 1153736, 1, 2, "Poison Resist", Strings.Buffs___Penalties );
|
||||
AddEquipmentPropertyWithSymbol( equipment, 1153738, 1, 2, "Energy Resist", Strings.Buffs___Penalties );
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private static Item[] GetEquipmentItems( Mobile mobile )
|
||||
{
|
||||
List<Item> items = new List<Item>();
|
||||
|
||||
foreach ( Item item in mobile.GetEquippedItems() )
|
||||
{
|
||||
items.Add( item );
|
||||
}
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
|
||||
private void AddEquipmentProperty( Item[] items, int cliloc, int argumentIndex, string name, string category )
|
||||
{
|
||||
AddEquipmentProperty( items, new[] { cliloc }, new[] { argumentIndex }, name, category );
|
||||
}
|
||||
|
||||
private void AddEquipmentProperty( Item[] items, IReadOnlyList<int> clilocs, IReadOnlyList<int> argumentIndexs,
|
||||
string name, string category )
|
||||
{
|
||||
int amount = clilocs.Select( ( t, i ) => CountPropertyList( items.ToArray(), t, argumentIndexs[i] ) ).Sum();
|
||||
|
||||
if ( amount > 0 )
|
||||
{
|
||||
AddData( new ObjectInspectorData
|
||||
{
|
||||
Name = name, Value = amount.ToString(), Category = category, IsExpanded = false
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
private void AddEquipmentPropertyWithSymbol( Item[] items, int cliloc, int symbolIndex, int argumentIndex,
|
||||
string name, string category )
|
||||
{
|
||||
int amount = CountPropertyListWithSymbol( items.ToArray(), cliloc, symbolIndex, argumentIndex );
|
||||
|
||||
if ( amount != 0 )
|
||||
{
|
||||
AddData( new ObjectInspectorData
|
||||
{
|
||||
Name = name, Value = amount.ToString(), Category = category, IsExpanded = false
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
private static int CountProperty( int serial, int cliloc, int argumentIndex )
|
||||
{
|
||||
Item item = Engine.Items.GetItem( serial );
|
||||
|
||||
Property[] properties = item?.Properties;
|
||||
|
||||
if ( properties == null )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return properties.Where( p => p.Cliloc == cliloc ).Select( p => int.Parse( p.Arguments[argumentIndex] ) )
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static int CountPropertyList( IEnumerable<Item> items, int cliloc, int argumentIndex )
|
||||
{
|
||||
return items.Sum( t => CountProperty( t.Serial, cliloc, argumentIndex ) );
|
||||
}
|
||||
|
||||
private static int CountPropertyListWithSymbol( IReadOnlyList<Item> items, int cliloc, int symbolIndex,
|
||||
int argumentIndex )
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
foreach ( Item t in items )
|
||||
{
|
||||
int count = CountProperty( t.Serial, cliloc, argumentIndex );
|
||||
|
||||
Property[] properties = t.Properties;
|
||||
|
||||
if ( properties == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( Property p in properties )
|
||||
{
|
||||
if ( p.Cliloc != cliloc )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string symbol = p.Arguments[symbolIndex];
|
||||
|
||||
if ( symbol == "-" )
|
||||
{
|
||||
total -= count;
|
||||
}
|
||||
else
|
||||
{
|
||||
total += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private static string GetDisplayFormat( Type type, string propertyName, object value )
|
||||
{
|
||||
if ( propertyName == null )
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
DisplayFormatAttribute attr = type.GetPropertyAttribute<DisplayFormatAttribute>( propertyName );
|
||||
|
||||
return attr != null ? attr.ToString( value ) : value?.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
private void AddData( Type type, string propertyName, object value, string category, string nameOverride = "",
|
||||
Action<object> action = null )
|
||||
{
|
||||
AddData( new ObjectInspectorData
|
||||
{
|
||||
Name = string.IsNullOrEmpty( nameOverride ) ? propertyName : nameOverride,
|
||||
Value = GetDisplayFormat( type, propertyName, value ),
|
||||
Category = category,
|
||||
OnDoubleClick = action
|
||||
} );
|
||||
}
|
||||
|
||||
private void AddData( string name, string value, string category )
|
||||
{
|
||||
AddData( new ObjectInspectorData { Name = name, Value = value, Category = category } );
|
||||
}
|
||||
|
||||
private void AddData( ObjectInspectorData data )
|
||||
{
|
||||
_dispatcher.Invoke( () => { Items.Add( data ); } );
|
||||
}
|
||||
|
||||
private void CopyToClipboard()
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO
|
||||
//Clipboard.SetData( DataFormats.Text, _selectedItem.Value );
|
||||
}
|
||||
catch ( Exception )
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPublicProperties( Entity entity )
|
||||
{
|
||||
List<ObjectInspectorData> data = new List<ObjectInspectorData>();
|
||||
|
||||
IOrderedEnumerable<PropertyInfo> properties = entity.GetType().GetProperties().OrderBy( p => p.Name );
|
||||
|
||||
foreach ( PropertyInfo p in properties )
|
||||
{
|
||||
object value = p.GetValue( entity );
|
||||
string valueString = "null";
|
||||
|
||||
if ( value != null )
|
||||
{
|
||||
valueString = value.ToString();
|
||||
}
|
||||
|
||||
DisplayFormatAttribute attr = entity.GetType().GetPropertyAttribute<DisplayFormatAttribute>( p.Name );
|
||||
|
||||
if ( attr != null )
|
||||
{
|
||||
valueString = attr.ToString( value );
|
||||
}
|
||||
|
||||
data.Add( new ObjectInspectorData
|
||||
{
|
||||
Name = p.Name,
|
||||
Value = valueString,
|
||||
Category = Strings.Public_Properties,
|
||||
IsExpanded = true,
|
||||
OnDoubleClick = GetDoubleClickAction( value )
|
||||
} );
|
||||
}
|
||||
|
||||
Items.AddRange( data );
|
||||
}
|
||||
|
||||
private static Action<object> GetDoubleClickAction( object value )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value is IObjectInspectorDoubleClick )
|
||||
{
|
||||
return o => ( (IObjectInspectorDoubleClick) value ).OnDoubleClick( o );
|
||||
}
|
||||
|
||||
if ( value is ItemCollection collection )
|
||||
{
|
||||
return o => ShowItemCollection( collection );
|
||||
}
|
||||
|
||||
if ( value is Item item )
|
||||
{
|
||||
return o => InspectObject( item.Serial );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void InspectObject( int serial )
|
||||
{
|
||||
Entity entity;
|
||||
|
||||
if ( UOMath.IsMobile( serial ) )
|
||||
{
|
||||
entity = Engine.Mobiles.GetMobile( serial );
|
||||
}
|
||||
else
|
||||
{
|
||||
entity = Engine.Items.GetItem( serial );
|
||||
}
|
||||
|
||||
if ( entity == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Engine.UIInvoker.Invoke( "ObjectInspectorWindow", null, typeof( ObjectInspectorViewModel ),
|
||||
new object[] { entity } );
|
||||
}
|
||||
|
||||
private static void ShowItemCollection( ItemCollection collection )
|
||||
{
|
||||
if ( collection == null )
|
||||
{
|
||||
}
|
||||
|
||||
//TODO
|
||||
//EntityCollectionViewer window = new EntityCollectionViewer
|
||||
//{
|
||||
// DataContext = new EntityCollectionViewerViewModel( collection ),
|
||||
// Topmost = true
|
||||
//};
|
||||
|
||||
//window.ShowDialog();
|
||||
}
|
||||
}
|
||||
|
||||
public interface IObjectInspectorDoubleClick
|
||||
{
|
||||
void OnDoubleClick( object o );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Macros.Commands;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.Misc;
|
||||
using ClassicAssist.UO.Gumps;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class OptionsTabViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private Options _currentOptions;
|
||||
private ICommand _setLanguageOverrideCommand;
|
||||
|
||||
public Options CurrentOptions
|
||||
{
|
||||
get => _currentOptions;
|
||||
set => SetProperty( ref _currentOptions, value );
|
||||
}
|
||||
|
||||
public ICommand SetLanguageOverrideCommand =>
|
||||
_setLanguageOverrideCommand ?? ( _setLanguageOverrideCommand = new RelayCommand( SetLanguageOverride ) );
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
if ( CurrentOptions == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JObject options = new JObject();
|
||||
|
||||
JObject useOnce = new JObject { ["Persist"] = CurrentOptions.PersistUseOnce };
|
||||
|
||||
if ( CurrentOptions.PersistUseOnce )
|
||||
{
|
||||
JArray useOnceItems = new JArray();
|
||||
|
||||
foreach ( int serial in ActionCommands.UseOnceList )
|
||||
{
|
||||
useOnceItems.Add( serial );
|
||||
}
|
||||
|
||||
useOnce.Add( "Items", useOnceItems );
|
||||
}
|
||||
|
||||
options.Add( "UseOnce", useOnce );
|
||||
options.Add( "UseDeathScreenWhilstHidden", CurrentOptions.UseDeathScreenWhilstHidden );
|
||||
options.Add( "CommandPrefix", CurrentOptions.CommandPrefix );
|
||||
options.Add( "RangeCheckLastTarget", CurrentOptions.RangeCheckLastTarget );
|
||||
options.Add( "RangeCheckLastTargetAmount", CurrentOptions.RangeCheckLastTargetAmount );
|
||||
options.Add( "UseExperimentalFizzleDetection", CurrentOptions.UseExperimentalFizzleDetection );
|
||||
options.Add( "UseObjectQueue", CurrentOptions.UseObjectQueue );
|
||||
options.Add( "UseObjectQueueAmount", CurrentOptions.UseObjectQueueAmount );
|
||||
options.Add( "QueueLastTarget", CurrentOptions.QueueLastTarget );
|
||||
options.Add( "MaxTargetQueueLength", CurrentOptions.MaxTargetQueueLength );
|
||||
options.Add( "SmartTargetOption", CurrentOptions.SmartTargetOption.ToString() );
|
||||
options.Add( "LimitMouseWheelTrigger", CurrentOptions.LimitMouseWheelTrigger );
|
||||
options.Add( "LimitMouseWheelTriggerMS", CurrentOptions.LimitMouseWheelTriggerMS );
|
||||
options.Add( "AutoAcceptPartyInvite", CurrentOptions.AutoAcceptPartyInvite );
|
||||
options.Add( "AutoAcceptPartyOnlyFromFriends", CurrentOptions.AutoAcceptPartyOnlyFromFriends );
|
||||
options.Add( "PreventTargetingInnocentsInGuardzone", CurrentOptions.PreventTargetingInnocentsInGuardzone );
|
||||
options.Add( "PreventAttackingInnocentsInGuardzone", CurrentOptions.PreventAttackingInnocentsInGuardzone );
|
||||
options.Add( "LastTargetMessage", CurrentOptions.LastTargetMessage );
|
||||
options.Add( "FriendTargetMessage", CurrentOptions.FriendTargetMessage );
|
||||
options.Add( "EnemyTargetMessage", CurrentOptions.EnemyTargetMessage );
|
||||
options.Add( "DefaultMacroQuietMode", CurrentOptions.DefaultMacroQuietMode );
|
||||
options.Add( "GetFriendEnemyUsesIgnoreList", CurrentOptions.GetFriendEnemyUsesIgnoreList );
|
||||
options.Add( "AbilitiesGump", CurrentOptions.AbilitiesGump );
|
||||
options.Add( "AbilitiesGumpX", CurrentOptions.AbilitiesGumpX );
|
||||
options.Add( "AbilitiesGumpY", CurrentOptions.AbilitiesGumpY );
|
||||
options.Add( "ShowProfileNameWindowTitle", CurrentOptions.ShowProfileNameWindowTitle );
|
||||
options.Add( "SortMacrosAlphabetical", CurrentOptions.SortMacrosAlphabetical );
|
||||
options.Add( "ShowResurrectionWaypoints", CurrentOptions.ShowResurrectionWaypoints );
|
||||
options.Add( "RehueFriends", CurrentOptions.RehueFriends );
|
||||
options.Add( "RehueFriendsHue", CurrentOptions.RehueFriendsHue );
|
||||
options.Add( "CheckHandsPotions", CurrentOptions.CheckHandsPotions );
|
||||
options.Add( "MacrosGump", CurrentOptions.MacrosGump );
|
||||
options.Add( "MacrosGumpX", CurrentOptions.MacrosGumpX );
|
||||
options.Add( "MacrosGumpY", CurrentOptions.MacrosGumpY );
|
||||
|
||||
json?.Add( "Options", options );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
CurrentOptions = options;
|
||||
|
||||
CurrentOptions.PropertyChanged += OnOptionsChanged;
|
||||
|
||||
ActionCommands.UseOnceList.Clear();
|
||||
|
||||
JToken config = json?["Options"];
|
||||
|
||||
if ( config?["UseOnce"] != null )
|
||||
{
|
||||
CurrentOptions.PersistUseOnce = config["UseOnce"]["Persist"]?.ToObject<bool>() ?? false;
|
||||
|
||||
if ( CurrentOptions.PersistUseOnce )
|
||||
{
|
||||
foreach ( JToken token in config["UseOnce"]["Items"] )
|
||||
{
|
||||
ActionCommands.UseOnceList.Add( token.ToObject<int>() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentOptions.UseDeathScreenWhilstHidden =
|
||||
config?["UseDeathScreenWhilstHidden"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.CommandPrefix = config?["CommandPrefix"]?.ToObject<char>() ?? '=';
|
||||
CurrentOptions.RangeCheckLastTarget = config?["RangeCheckLastTarget"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.RangeCheckLastTargetAmount = config?["RangeCheckLastTargetAmount"]?.ToObject<int>() ?? 11;
|
||||
|
||||
CurrentOptions.UseExperimentalFizzleDetection =
|
||||
config?["UseExperimentalFizzleDetection"]?.ToObject<bool>() ?? false;
|
||||
|
||||
CurrentOptions.UseObjectQueue = config?["UseObjectQueue"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.UseObjectQueueAmount = config?["UseObjectQueueAmount"]?.ToObject<int>() ?? 5;
|
||||
CurrentOptions.QueueLastTarget = config?["QueueLastTarget"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.MaxTargetQueueLength = config?["MaxTargetQueueLength"]?.ToObject<int>() ?? 1;
|
||||
CurrentOptions.SmartTargetOption =
|
||||
config?["SmartTargetOption"]?.ToObject<SmartTargetOption>() ?? SmartTargetOption.None;
|
||||
CurrentOptions.LimitMouseWheelTrigger = config?["LimitMouseWheelTrigger"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.LimitMouseWheelTriggerMS = config?["LimitMouseWheelTriggerMS"]?.ToObject<int>() ?? 25;
|
||||
CurrentOptions.AutoAcceptPartyInvite = config?["AutoAcceptPartyInvite"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.AutoAcceptPartyOnlyFromFriends =
|
||||
config?["AutoAcceptPartyOnlyFromFriends"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.PreventTargetingInnocentsInGuardzone =
|
||||
config?["PreventTargetingInnocentsInGuardzone"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.PreventAttackingInnocentsInGuardzone =
|
||||
config?["PreventAttackingInnocentsInGuardzone"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.LastTargetMessage = config?["LastTargetMessage"]?.ToObject<string>() ?? "[Last Target]";
|
||||
CurrentOptions.FriendTargetMessage = config?["FriendTargetMessage"]?.ToObject<string>() ?? "[Friend]";
|
||||
CurrentOptions.EnemyTargetMessage = config?["EnemyTargetMessage"]?.ToObject<string>() ?? "[Enemy]";
|
||||
CurrentOptions.DefaultMacroQuietMode = config?["DefaultMacroQuietMode"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.GetFriendEnemyUsesIgnoreList =
|
||||
config?["GetFriendEnemyUsesIgnoreList"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.AbilitiesGump = config?["AbilitiesGump"]?.ToObject<bool>() ?? true;
|
||||
CurrentOptions.AbilitiesGumpX = config?["AbilitiesGumpX"]?.ToObject<int>() ?? 100;
|
||||
CurrentOptions.AbilitiesGumpY = config?["AbilitiesGumpY"]?.ToObject<int>() ?? 100;
|
||||
CurrentOptions.ShowProfileNameWindowTitle =
|
||||
config?["ShowProfileNameWindowTitle"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.SortMacrosAlphabetical = config?["SortMacrosAlphabetical"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.ShowResurrectionWaypoints = config?["ShowResurrectionWaypoints"]?.ToObject<bool>() ?? true;
|
||||
CurrentOptions.RehueFriends = config?["RehueFriends"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.RehueFriendsHue = config?["RehueFriendsHue"]?.ToObject<int>() ?? 35;
|
||||
CurrentOptions.CheckHandsPotions = config?["CheckHandsPotions"]?.ToObject<bool>() ?? false;
|
||||
CurrentOptions.MacrosGump = config?["MacrosGump"]?.ToObject<bool>() ?? true;
|
||||
CurrentOptions.MacrosGumpX = config?["MacrosGumpX"]?.ToObject<int>() ?? 100;
|
||||
CurrentOptions.MacrosGumpY = config?["MacrosGumpY"]?.ToObject<int>() ?? 100;
|
||||
|
||||
if ( CurrentOptions.AbilitiesGumpX < 0 )
|
||||
{
|
||||
CurrentOptions.AbilitiesGumpX = 100;
|
||||
}
|
||||
|
||||
if ( CurrentOptions.AbilitiesGumpY < 0 )
|
||||
{
|
||||
CurrentOptions.AbilitiesGumpY = 100;
|
||||
}
|
||||
|
||||
if ( CurrentOptions.MacrosGump )
|
||||
{
|
||||
MacrosGump.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
// Replay CurrentOptions changes onto Options.CurrentOptions
|
||||
// TODO: Fix Options
|
||||
private void OnOptionsChanged( object sender, PropertyChangedEventArgs args )
|
||||
{
|
||||
if ( args.PropertyName == "Name" )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
object val = CurrentOptions.GetType().GetProperty( args.PropertyName )?.GetValue( CurrentOptions );
|
||||
|
||||
if ( val == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
object oldVal = Options.CurrentOptions.GetType().GetProperty( args.PropertyName )
|
||||
?.GetValue( Options.CurrentOptions );
|
||||
|
||||
if ( !val.Equals( oldVal ) )
|
||||
{
|
||||
Options.CurrentOptions.GetType().GetProperty( args.PropertyName )
|
||||
?.SetValue( Options.CurrentOptions, val );
|
||||
}
|
||||
}
|
||||
|
||||
private static async void SetLanguageOverride( object obj )
|
||||
{
|
||||
if ( !( obj is Language language ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AssistantOptions.LanguageOverride = language;
|
||||
|
||||
await Engine.MessageBoxProvider.Show( Strings.Restart_game_for_changes_to_take_effect___,
|
||||
Strings.Restart_game_for_changes_to_take_effect___ );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.UO.Gumps;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class RepositionableGumpViewModel : BaseViewModel
|
||||
{
|
||||
private readonly RepositionableGump _gump;
|
||||
private int _horizontalMax;
|
||||
private int _verticalMax;
|
||||
private int _x;
|
||||
private int _y;
|
||||
|
||||
public RepositionableGumpViewModel()
|
||||
{
|
||||
}
|
||||
|
||||
public RepositionableGumpViewModel( RepositionableGump gump, int initialX, int initialY )
|
||||
{
|
||||
_gump = gump;
|
||||
X = initialX;
|
||||
Y = initialY;
|
||||
HorizontalMax = 3840;
|
||||
VerticalMax = 2160;
|
||||
|
||||
if ( !NativeMethods.GetWindowRect( Engine.WindowHandle, out NativeMethods.RECT rect ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HorizontalMax = rect.Right - rect.Left;
|
||||
VerticalMax = rect.Bottom - rect.Top;
|
||||
}
|
||||
|
||||
public int HorizontalMax
|
||||
{
|
||||
get => _horizontalMax;
|
||||
set => SetProperty( ref _horizontalMax, value );
|
||||
}
|
||||
|
||||
public int VerticalMax
|
||||
{
|
||||
get => _verticalMax;
|
||||
set => SetProperty( ref _verticalMax, value );
|
||||
}
|
||||
|
||||
public int X
|
||||
{
|
||||
get => _x;
|
||||
set
|
||||
{
|
||||
SetProperty( ref _x, value );
|
||||
_gump?.SetPosition( _x, _y );
|
||||
}
|
||||
}
|
||||
|
||||
public int Y
|
||||
{
|
||||
get => _y;
|
||||
set
|
||||
{
|
||||
SetProperty( ref _y, value );
|
||||
_gump?.SetPosition( _x, _y );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Hotkeys;
|
||||
using ClassicAssist.Data.Hotkeys.Commands;
|
||||
using ClassicAssist.Data.Macros.Commands;
|
||||
using ClassicAssist.Data.Skills;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.Shared.Resources;
|
||||
using ClassicAssist.UI.Misc;
|
||||
using ClassicAssist.UO;
|
||||
using ClassicAssist.UO.Data;
|
||||
using ClassicAssist.UO.Network;
|
||||
using ClassicAssist.UO.Network.Packets;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ClassicAssist.UI.ViewModels
|
||||
{
|
||||
public class SkillsTabViewModel : BaseViewModel, ISettingProvider
|
||||
{
|
||||
private HotkeyCommand _hotkeyCategory;
|
||||
private ObservableCollectionEx<SkillEntry> _items = new ObservableCollectionEx<SkillEntry>();
|
||||
private ICommand _resetDeltasCommand;
|
||||
private SkillEntry _selectedItem;
|
||||
private ICommand _setAllSkillLocksCommand;
|
||||
private ICommand _setSkillLocksCommand;
|
||||
private float _totalBase;
|
||||
private ICommand _useSkillCommand;
|
||||
|
||||
public SkillsTabViewModel()
|
||||
{
|
||||
Items.CollectionChanged += ( sender, args ) => { UpdateTotalBase(); };
|
||||
|
||||
IncomingPacketHandlers.SkillUpdatedEvent += OnSkillUpdatedEvent;
|
||||
IncomingPacketHandlers.SkillsListEvent += OnSkillsListEvent;
|
||||
|
||||
if ( Engine.Player != null && Engine.Connected )
|
||||
{
|
||||
Shared.UO.Commands.MobileQuery( Engine.Player.Serial, MobileQueryType.SkillsRequest );
|
||||
}
|
||||
|
||||
SkillManager manager = SkillManager.GetInstance();
|
||||
manager.Items = Items;
|
||||
}
|
||||
|
||||
public ObservableCollectionEx<SkillEntry> Items
|
||||
{
|
||||
get => _items;
|
||||
set => SetProperty( ref _items, value );
|
||||
}
|
||||
|
||||
public ICommand ResetDeltasCommand =>
|
||||
_resetDeltasCommand ?? ( _resetDeltasCommand = new RelayCommand( ResetDeltas, o => true ) );
|
||||
|
||||
public SkillEntry SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetProperty( ref _selectedItem, value );
|
||||
}
|
||||
|
||||
public ICommand SetAllSkillLocksCommand =>
|
||||
_setAllSkillLocksCommand ?? ( _setAllSkillLocksCommand = new RelayCommand( SetAllSkillLocks, o => true ) );
|
||||
|
||||
public ICommand SetSkillLocksCommand =>
|
||||
_setSkillLocksCommand ?? ( _setSkillLocksCommand = new RelayCommand( SetSkillLocks, o => true ) );
|
||||
|
||||
public float TotalBase
|
||||
{
|
||||
get => _totalBase;
|
||||
set => SetProperty( ref _totalBase, value );
|
||||
}
|
||||
|
||||
public ICommand UseSkillCommand =>
|
||||
_useSkillCommand ?? ( _useSkillCommand =
|
||||
new RelayCommand( UseSkill, o => SelectedItem?.Skill.Invokable ?? false ) );
|
||||
|
||||
public void Serialize( JObject json )
|
||||
{
|
||||
JArray skills = new JArray();
|
||||
|
||||
if ( _hotkeyCategory?.Children == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( HotkeyEntry hks in _hotkeyCategory.Children )
|
||||
{
|
||||
if ( Equals( hks.Hotkey, ShortcutKeys.Default ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
skills.Add( new JObject
|
||||
{
|
||||
{ "Name", hks.Name }, { "Keys", hks.Hotkey.ToJObject() }, { "PassToUO", hks.PassToUO }
|
||||
} );
|
||||
}
|
||||
|
||||
json.Add( "Skills", skills );
|
||||
}
|
||||
|
||||
public void Deserialize( JObject json, Options options )
|
||||
{
|
||||
HotkeyManager hotkey = HotkeyManager.GetInstance();
|
||||
|
||||
if ( Skills.GetSkillsArray() == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IOrderedEnumerable<SkillData> skills =
|
||||
Skills.GetSkillsArray().Where( s => s.Invokable ).OrderBy( s => s.Name );
|
||||
|
||||
ObservableCollectionEx<HotkeyEntry> hotkeyEntries = new ObservableCollectionEx<HotkeyEntry>();
|
||||
|
||||
foreach ( SkillData skill in skills )
|
||||
{
|
||||
hotkeyEntries.Add( new HotkeyCommand
|
||||
{
|
||||
Action = hks => SkillCommands.UseSkill( skill.Name ), Name = skill.Name
|
||||
} );
|
||||
}
|
||||
|
||||
if ( json["Skills"] != null )
|
||||
{
|
||||
foreach ( HotkeyEntry hke in hotkeyEntries )
|
||||
{
|
||||
JToken token = json["Skills"].FirstOrDefault( jo => jo["Name"].ToObject<string>() == hke.Name );
|
||||
|
||||
if ( token == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hke.Hotkey = new ShortcutKeys( token["Keys"] );
|
||||
hke.PassToUO = token["PassToUO"].ToObject<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
_hotkeyCategory = hotkey.Items.FirstOrDefault( hk => hk.IsCategory && hk.Name == Strings.Skills ) ??
|
||||
new HotkeyCommand { Name = Strings.Skills, IsCategory = true };
|
||||
|
||||
_hotkeyCategory.Children = hotkeyEntries;
|
||||
|
||||
hotkey.AddCategory( _hotkeyCategory );
|
||||
}
|
||||
|
||||
private void ResetDeltas( object obj )
|
||||
{
|
||||
foreach ( SkillEntry skillEntry in Items )
|
||||
{
|
||||
skillEntry.Delta = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSkillLocks( object obj )
|
||||
{
|
||||
LockStatus lockStatus = (LockStatus) obj;
|
||||
|
||||
if ( SelectedItem == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shared.UO.Commands.ChangeSkillLock( SelectedItem, lockStatus );
|
||||
}
|
||||
|
||||
private void SetAllSkillLocks( object obj )
|
||||
{
|
||||
LockStatus lockStatus = (LockStatus) (int) obj;
|
||||
|
||||
IEnumerable<SkillEntry> skillsToSet = Items.Where( i => i.LockStatus != lockStatus );
|
||||
|
||||
foreach ( SkillEntry skillEntry in skillsToSet )
|
||||
{
|
||||
Shared.UO.Commands.ChangeSkillLock( skillEntry, lockStatus, false );
|
||||
}
|
||||
|
||||
Shared.UO.Commands.MobileQuery( Engine.Player.Serial, MobileQueryType.SkillsRequest );
|
||||
}
|
||||
|
||||
private void UpdateTotalBase()
|
||||
{
|
||||
TotalBase = Items.Sum( se => se.Base );
|
||||
}
|
||||
|
||||
private void OnSkillsListEvent( SkillInfo[] skills )
|
||||
{
|
||||
_dispatcher.Invoke( () => { Items.Clear(); } );
|
||||
|
||||
foreach ( SkillInfo si in skills )
|
||||
{
|
||||
Skill skill = new Skill
|
||||
{
|
||||
ID = si.ID, Name = Skills.GetSkillName( si.ID ), Invokable = Skills.IsInvokable( si.ID )
|
||||
};
|
||||
|
||||
SkillEntry se = new SkillEntry
|
||||
{
|
||||
Skill = skill,
|
||||
Value = si.Value,
|
||||
Base = si.BaseValue,
|
||||
Cap = si.SkillCap,
|
||||
LockStatus = si.LockStatus
|
||||
};
|
||||
|
||||
_dispatcher.Invoke( () => { Items.Add( se ); } );
|
||||
}
|
||||
}
|
||||
|
||||
private void UseSkill( object obj )
|
||||
{
|
||||
if ( SelectedItem == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( SelectedItem.Skill.Invokable )
|
||||
{
|
||||
Engine.SendPacketToServer( new UseSkill( SelectedItem.Skill.ID ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSkillUpdatedEvent( int skillID, float value, float baseValue, LockStatus lockStatus,
|
||||
float skillCap )
|
||||
{
|
||||
SkillEntry entry = _items.FirstOrDefault( se => se.Skill.ID == skillID );
|
||||
|
||||
if ( entry == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_dispatcher.Invoke( () =>
|
||||
{
|
||||
entry.Delta += baseValue - entry.Base;
|
||||
entry.Value = value;
|
||||
entry.Base = baseValue;
|
||||
entry.Cap = skillCap;
|
||||
entry.LockStatus = lockStatus;
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user