108 Commits

Author SHA1 Message Date
Иванов Иван
46c877f036 fix: add l2.bin to valid process name 2024-08-15 20:14:20 +02:00
Иванов Иван
2943f7a50b feat: add combat and deleveling AI 2024-08-15 17:23:24 +02:00
Иванов Иван
bdd026519f feat: use WH_GETMESSAGE for hook 2024-08-15 17:22:49 +02:00
Иванов Иван
a11cde4121 fix: fix null pointer exception 2024-08-15 17:22:22 +02:00
Иванов Иван
b3c3a5fa85 fix: escape json strings 2024-08-14 17:48:19 +02:00
Иванов Иван
b97a2ea63c feat: add force skill using 2024-08-12 22:06:01 +02:00
Иванов Иван
75a5b842f7 feat: clear all attackers when hero died 2024-08-12 18:29:25 +02:00
Иванов Иван
c61019961b feat: add resurrection 2024-08-12 18:20:55 +02:00
Иванов Иван
c3f6d10dc6 fix: fix attackers removing 2024-08-12 18:11:02 +02:00
Иванов Иван
b7d8dd1db0 fix: fix null pointer exception 2024-08-12 18:10:18 +02:00
Иванов Иван
f761a0577e feat: increase distance of viewing 2024-08-10 12:27:05 +02:00
Иванов Иван
7f81380c89 feat: show attackes in list and map 2024-08-10 12:04:47 +02:00
Иванов Иван
e3e0db29ad fix: fix null pointer exception 2024-08-10 12:02:08 +02:00
Иванов Иван
ee0b8db572 fix: fix memory leak 2024-08-08 21:13:54 +02:00
Иванов Иван
e95d748f00 fix: temporary disable abmormal effects 2024-08-08 21:12:57 +02:00
Иванов Иван
38c8a67bfb fix: fix acquire target action 2024-08-08 21:12:24 +02:00
Иванов Иван
c84a086447 fix: change absolute paths to relative 2024-07-25 18:15:29 +02:00
k0t9i
e42ff2b5e7 feat: add attackers handling 2023-11-11 15:44:03 +04:00
k0t9i
129381e13c refactor: create only new entity 2023-11-11 14:33:22 +04:00
k0t9i
63833745b6 refactor: add line break 2023-10-29 22:04:33 +05:00
k0t9i
385f02a3d8 refactor: add pathfinding description 2023-10-29 22:03:51 +05:00
k0t9i
17a4f82f24 feat: add pathfinding 2023-10-29 20:55:36 +04:00
k0t9i
efffe8a7cb feat: request item list on hero created 2023-10-26 22:16:39 +04:00
k0t9i
604ba8af9b fix: remove exception throwing from game threads 2023-10-26 22:13:23 +04:00
k0t9i
ee11faf0ce Merge branch 'master' of https://github.com/k0t9i/L2Bot2.0 2023-10-19 10:05:35 +04:00
k0t9i
2e025fd0cf feat: add some low level error handling 2023-10-19 10:04:52 +04:00
k0t9i
641e20e82a fix: restore UGameEngine::Tick on exit 2023-10-18 23:29:51 +04:00
k0t9i
8ea599ccc6 fix: do not stop world handler in destructor 2023-10-18 23:28:15 +04:00
k0t9i
24b7055136 refactor: config log channels for debug env 2023-10-18 15:59:18 +04:00
k0t9i
521af7c00e feat: add exceptions 2023-10-18 13:28:29 +04:00
k0t9i
a2428bb0d1 feat: add some info and app logging messages 2023-10-17 23:08:41 +04:00
k0t9i
ab85800275 feat: add logger 2023-10-17 20:20:25 +04:00
k0t9i
03e61a9b9a refactor: move events and service locator into core project 2023-10-17 14:16:58 +04:00
k0t9i
9836ef3a17 refactor: switch world handler parameters to map of repositories 2023-10-16 23:50:29 +04:00
k0t9i
ede55a870e refactor: move event disptacher into service locator 2023-10-16 23:38:01 +04:00
k0t9i
b948273172 fix: send HeroDeletedEvent only if hero actually removed 2023-10-16 17:51:11 +04:00
k0t9i
2c475e69f3 refactor: add services for imcoming and outgoing messages 2023-10-16 17:41:25 +04:00
k0t9i
787f4969ed refactor: remove unnecessary classes 2023-10-16 00:03:43 +04:00
k0t9i
a7a9b626f4 refactor: change skill updating 2023-10-13 00:12:55 +04:00
k0t9i
3050c58881 fix: fix bot screenshot 2023-08-09 02:20:18 +05:00
k0t9i
d47ad68ff0 refactor: create LICENSE 2023-08-09 02:14:09 +05:00
k0t9i
c31bc2597e refactor: create README.md 2023-08-09 02:09:36 +05:00
k0t9i
ac7bde68e9 feat: add vital stats to players 2023-02-27 23:43:08 +04:00
k0t9i
f450a7cc0e feat: add vital stats to players 2023-02-27 23:37:53 +04:00
k0t9i
8eea2ded26 fix: fix binding to item name 2023-02-16 21:24:16 +04:00
k0t9i
7276b24249 fix: fix toggle soulshot method 2023-02-10 19:00:56 +04:00
k0t9i
81e26a7e52 refactor: replace unique_ptr to shared_ptr 2023-02-10 18:49:47 +04:00
k0t9i
bb4538794b feat: change zoom action to mouse wheel 2023-02-10 13:58:54 +04:00
k0t9i
123d039263 feat: add highlighting for target in creature list 2023-02-09 23:07:57 +04:00
k0t9i
ad5d7a5159 feat: add outgoing messages to client 2023-02-09 22:45:08 +04:00
k0t9i
abcf3b20c0 fix: hook GetItem method 2023-02-09 22:44:27 +04:00
k0t9i
eefb5d40ae feat: add incoming messages to bot 2023-02-09 18:30:07 +04:00
k0t9i
130cdc6bca fix: change target on hero created 2023-02-07 23:18:43 +04:00
k0t9i
2480563914 feat: add creatures and drops on the map 2023-02-07 22:21:15 +04:00
k0t9i
d03f37fbf7 feat: add map drawing 2023-02-02 21:54:08 +04:00
k0t9i
c35f4e317a feat: add items #wip 2023-02-02 16:54:38 +04:00
k0t9i
03423d0c41 feat: add update items from server 2023-02-01 23:37:44 +04:00
k0t9i
0e6d8dec86 fix: move some methods from network handler to game engine wrapper 2023-02-01 22:44:39 +04:00
k0t9i
6cbf61d67f fix: fix item creation 2023-02-01 22:29:36 +04:00
k0t9i
546b371dd6 fix: fix game crash on skill list open 2023-02-01 21:51:28 +04:00
k0t9i
ec54d22c65 refactor: remove possible null warning from Skill 2023-02-01 16:45:45 +04:00
k0t9i
1bd107ad0a fix: add lock to repositories 2023-02-01 16:45:12 +04:00
k0t9i
9a0204c6ed featL add skill component 2023-02-01 14:48:59 +04:00
k0t9i
32fdef9b1c feat: add skills 2023-02-01 00:30:20 +04:00
k0t9i
823241ef32 fix: update all skill on skill created event 2023-02-01 00:15:20 +04:00
k0t9i
123037cf23 fix: nake weight and max weight 32bit 2023-02-01 00:14:22 +04:00
k0t9i
4dc08f3139 feat: add more info to target panel 2023-01-31 20:18:10 +04:00
k0t9i
24e6c4a180 refactor: add creature extensions 2023-01-31 19:14:45 +04:00
k0t9i
5388dea95f refactor: rename NotifyPropertyChanged to ObservableObject 2023-01-31 19:09:07 +04:00
k0t9i
bd50473bfb feat: add hero target 2023-01-31 17:35:18 +04:00
k0t9i
d2b20e0666 feat: add events for drop and chat message 2023-01-31 16:14:13 +04:00
k0t9i
31febdd341 feat: create creature interface 2023-01-31 15:14:19 +04:00
k0t9i
b8b92b7cf8 feat: add event bus 2023-01-31 14:45:40 +04:00
k0t9i
ea6778a3bd feat: cleanup in summary info 2023-01-31 01:12:02 +04:00
k0t9i
89cec4f40f feat: add npc info (level and aggro radius) 2023-01-31 00:32:26 +04:00
k0t9i
a8734a4ca0 feat: send test command to the bot 2023-01-31 00:31:57 +04:00
k0t9i
57d6a61791 feat: add StatsBar component 2023-01-30 19:33:48 +04:00
k0t9i
35e3f5e487 refactor: add removeAll method to observable collection 2023-01-30 19:13:37 +04:00
k0t9i
905b189bf2 fix: fix calculation of exp percentage 2023-01-30 19:13:01 +04:00
k0t9i
a8adf23959 feat: add hero stats panel 2023-01-29 23:38:42 +04:00
k0t9i
f8b0b4b894 feat: add experience info helper 2023-01-29 21:46:46 +04:00
k0t9i
8b27c84a9e feat: add view models for all entities 2023-01-29 20:44:24 +04:00
k0t9i
16630de6a4 feat: add config file 2023-01-28 17:43:41 +04:00
k0t9i
4778f1c478 feat: add npc and players 2023-01-28 16:44:41 +04:00
k0t9i
340e91b325 feat: add chat messages 2023-01-28 16:32:10 +04:00
k0t9i
42d594bbbb feat: create window client app 2023-01-28 14:54:49 +04:00
k0t9i
1d77bceeff refactor: add fixme 2023-01-26 15:07:56 +04:00
k0t9i
112c20b7d3 feat: change message api to one interface 2023-01-26 14:58:19 +04:00
k0t9i
2a3e39019e fix: rename id-field to id 2023-01-26 14:57:06 +04:00
k0t9i
54a4514a53 refactor: move dll loading into c# client 2023-01-26 02:03:08 +04:00
k0t9i
7ad3ff4081 refactor: add simple c# client 2023-01-26 01:36:49 +04:00
k0t9i
eb7bfc779b refactor: switch to wide string 2023-01-25 19:53:02 +04:00
k0t9i
f7198a13ef feat: add chat messages 2023-01-25 18:42:53 +04:00
k0t9i
b3bb4a52f3 refactor: rename enums 2023-01-25 17:49:50 +04:00
k0t9i
d7c0375416 feat: add enchant helper 2023-01-25 15:59:34 +04:00
k0t9i
1d0dbf86c3 feat: remove outdated items and skills 2023-01-25 14:55:38 +04:00
k0t9i
e0c7445ef7 feat: add abnormal effects 2023-01-25 01:56:21 +04:00
k0t9i
d6c15ee0c2 refactor: change abbreviations to full form 2023-01-25 00:24:31 +04:00
k0t9i
d48e910603 fix: change creature pAttack, pDefence to 32 bit 2023-01-25 00:19:16 +04:00
k0t9i
a2bcb25f0e feat: add autouse of items 2023-01-25 00:17:35 +04:00
k0t9i
4cfb986ce0 feat: add enchant value to pdef/mdef, patk/matk 2023-01-24 23:50:59 +04:00
k0t9i
ca0183603f feat: add weapon and shield items 2023-01-24 18:37:47 +04:00
k0t9i
2b94f823f2 feat: add armor item 2023-01-24 17:23:53 +04:00
k0t9i
43f38c7a57 feat: add base item 2023-01-24 02:49:35 +04:00
k0t9i
6ca8752108 fix: set is new object attribute to false 2023-01-24 01:08:49 +04:00
k0t9i
3755e45ac6 feat: check if a creature really sweepable 2023-01-24 00:36:12 +04:00
k0t9i
e6e20a1e08 refactor: use references where possible 2023-01-24 00:05:06 +04:00
k0t9i
e127d94902 refactor: switch strings to string references 2023-01-23 23:58:45 +04:00
4973 changed files with 118524 additions and 2310 deletions

9
Client/App.xaml Normal file
View File

@@ -0,0 +1,9 @@
<Application x:Class="Client.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Client"
ShutdownMode="OnMainWindowClose">
<Application.Resources>
</Application.Resources>
</Application>

158
Client/App.xaml.cs Normal file
View File

@@ -0,0 +1,158 @@
using Client.Domain.Factories;
using Client.Infrastructure.Factories;
using Client.Domain.Parsers;
using Client.Infrastructure.Parsers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Windows;
using Client.Domain.Transports;
using Client.Infrastructure.Transports;
using Client.Domain.Entities;
using Client.Domain.Service;
using Client.Domain.ValueObjects;
using Microsoft.Extensions.Configuration;
using System.Reflection;
using Client.Application.Views;
using Client.Application.ViewModels;
using Client.Domain.Helpers;
using Client.Infrastructure.Helpers;
using Client.Domain.Events;
using Client.Infrastructure.Events;
using System;
using Client.Infrastructure.Service;
using System.Collections.Generic;
using System.Linq;
using Client.Domain.AI;
using Client.Infrastructure.AI.IO;
using Client.Domain.AI.IO;
namespace Client
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : System.Windows.Application
{
public static IHost? AppHost { get; private set; }
public App()
{
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices(
(hostContext, services) => ConfigureServices(services)
)
.Build();
}
protected override async void OnStartup(StartupEventArgs e)
{
await AppHost!.StartAsync();
var startupForm = AppHost.Services.GetRequiredService<MainWindow>();
startupForm.Show();
base.OnStartup(e);
var application = AppHost.Services.GetRequiredService<Bot>();
await application.StartAsync();
}
protected override async void OnExit(ExitEventArgs e)
{
await AppHost!.StopAsync();
base.OnExit(e);
}
private void ConfigureServices(IServiceCollection services)
{
IConfiguration config = new ConfigurationBuilder()
.AddJsonFile("config.json", optional: false, reloadOnChange: true)
.AddJsonFile("Assets/data/experience.json", optional: false, reloadOnChange: true)
.AddJsonFile("Assets/data/npcInfo.json", optional: false, reloadOnChange: true)
.AddJsonFile("Assets/data/itemInfo.json", optional: false, reloadOnChange: true)
.AddJsonFile("Assets/data/skillInfo.json", optional: false, reloadOnChange: true)
.Build();
services
.AddSingleton(typeof(IConfiguration), config)
.AddSingleton<MainWindow>()
.AddSingleton(
typeof(Bot),
x => new Bot(
x.GetRequiredService<IServiceProvider>(),
config.GetValue<string>("DLLName") ?? ""
)
)
.AddSingleton(typeof(EntityHandlerFactoryInterface), typeof(EntityHandlerFactory))
.AddSingleton(typeof(MessageParserInterface), typeof(JsonMessageParser))
.AddSingleton(typeof(OutgoingMessageBuilderInterface), typeof(JsonOutgoingMessageBuilder))
.AddSingleton(
typeof(TransportInterface),
x => new NamedPipeTransport(
config.GetValue<string>("ConnectionPipeName") ?? ""
)
)
.AddSingleton(typeof(ExperienceHelperInterface), typeof(ConfigurationExperienceHelper))
.AddSingleton(typeof(NpcInfoHelperInterface), typeof(ConfigurationNpcInfoHelper))
.AddSingleton(typeof(ItemInfoHelperInterface), typeof(ConfigurationItemInfoHelper))
.AddSingleton(typeof(SkillInfoHelperInterface), typeof(ConfigurationSkillInfoHelper))
.AddSingleton(typeof(EventBusInterface), typeof(InMemoryEventBus))
.AddTransient(typeof(EntityFactoryInterface<Entity>), typeof(EntityFactory<Entity>))
.AddTransient(typeof(EntityFactoryInterface<Hero>), typeof(EntityFactory<Hero>))
.AddTransient(typeof(EntityFactoryInterface<Drop>), typeof(EntityFactory<Drop>))
.AddTransient(typeof(EntityFactoryInterface<NPC>), typeof(EntityFactory<NPC>))
.AddTransient(typeof(EntityFactoryInterface<Player>), typeof(EntityFactory<Player>))
.AddTransient(typeof(EntityFactoryInterface<ChatMessage>), typeof(EntityFactory<ChatMessage>))
.AddTransient(typeof(EntityFactoryInterface<Skill>), typeof(EntityFactory<Skill>))
.AddTransient(typeof(EntityFactoryInterface<ItemInterface>), typeof(ItemFactory))
.AddSingleton<HeroHandler>()
.AddSingleton<DropHandler>()
.AddSingleton<NpcHandler>()
.AddSingleton<PlayerHandler>()
.AddSingleton<ChatMessageHandler>()
.AddSingleton<SkillHandler>()
.AddSingleton<ItemHander>()
.AddSingleton<WorldHandler>()
.AddSingleton(
typeof(PathfinderInterface),
x => new L2jGeoDataPathfinder(
config.GetValue<string>("GeoDataDirectory") ?? "",
config.GetValue<ushort>("MaxPassableHeight")
)
)
.AddSingleton(
typeof(AsyncPathMoverInterface),
x => new AsyncPathMover(
x.GetRequiredService<WorldHandler>(),
x.GetRequiredService<PathfinderInterface>(),
config.GetValue<int>("PathNumberOfAttempts"),
config.GetValue<double>("NodeWaitingTime"),
config.GetValue<int>("NodeDistanceTolerance"),
config.GetValue<int>("NextNodeDistanceTolerance")
)
)
.AddSingleton<Config>()
.AddSingleton<TransitionBuilderLocator>()
.AddSingleton(
typeof(AIInterface),
x => new AI(
x.GetRequiredService<WorldHandler>(),
x.GetRequiredService<Config>(),
x.GetRequiredService<AsyncPathMoverInterface>(),
x.GetRequiredService<TransitionBuilderLocator>()
)
)
.AddTransient(typeof(ConfigSerializerInterface), typeof(JsonConfigSerializer))
.AddTransient(typeof(ConfigDeserializerInterface), typeof(JsonConfigDeserializer))
.AddSingleton<MainViewModel>()
.AddSingleton<AIConfigViewModel>();
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Client.Application.Commands
{
public class RelayCommand : ICommand
{
private Action<object?> execute;
private Func<object?, bool>? canExecute;
public event EventHandler? CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object?> execute, Func<object?, bool>? canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object? parameter)
{
return canExecute == null || canExecute(parameter);
}
public void Execute(object? parameter)
{
execute(parameter);
}
}
}

View File

@@ -0,0 +1,36 @@
<Button x:Class="Client.Application.Components.Item"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client.Application.Components"
xmlns:services="clr-namespace:Client.Application.Services"
mc:Ignorable="d"
ToolTipService.ShowOnDisabled="True"
x:Name="root">
<Button.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding MouseLeftClickCommand}" />
<MouseBinding Gesture="RightClick" Command="{Binding MouseRightClickCommand}" />
</Button.InputBindings>
<Button.Resources>
<services:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<services:NullToVisibilityConverter x:Key="NullToVisibilityConverter"/>
<DataTemplate x:Key="ToolTipContent">
<StackPanel MaxWidth="300">
<TextBlock Text="{Binding Source={x:Reference root},Path=ItemName,Mode=OneWay}" TextWrapping="Wrap" FontWeight="Bold" FontSize="14" Margin="0,0,0,5" />
<TextBlock
TextWrapping="Wrap"
Text="{Binding Source={x:Reference root},Path=Description,Mode=OneWay}"
/>
</StackPanel>
</DataTemplate>
</Button.Resources>
<Button.Content>
<Grid>
<Image Source="{Binding Path=ImageSource,ElementName=root}" Height="32" Width="32" />
</Grid>
</Button.Content>
<Button.ToolTip >
<ContentControl ContentTemplate="{StaticResource ToolTipContent}"/>
</Button.ToolTip>
</Button>

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Client.Application.Components
{
/// <summary>
/// Interaction logic for Item.xaml
/// </summary>
public partial class Item : Button
{
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(Item), new PropertyMetadata(default(ImageSource)));
public static readonly DependencyProperty ItemNameProperty =
DependencyProperty.Register("ItemName", typeof(string), typeof(Item), new PropertyMetadata("Item"));
public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register("Description", typeof(string), typeof(Item), new PropertyMetadata(""));
public Item()
{
InitializeComponent();
}
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public string ItemName
{
get { return (string)GetValue(ItemNameProperty); }
set { SetValue(ItemNameProperty, value); }
}
public string? Description
{
get { return (string?)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
}
}

View File

@@ -0,0 +1,304 @@
<ContentControl x:Class="Client.Application.Components.Map"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:services="clr-namespace:Client.Application.Services"
xmlns:components="clr-namespace:Client.Application.Components"
mc:Ignorable="d"
services:SizeObserver.Observe="True"
services:SizeObserver.ObservedWidth="{Binding ViewportWidth, Mode=OneWayToSource}"
services:SizeObserver.ObservedHeight="{Binding ViewportHeight, Mode=OneWayToSource}"
MouseWheel="ContentControl_MouseWheel"
MouseLeave="ContentControl_MouseLeave"
MouseMove="ContentControl_MouseMove"
>
<Grid Background="Transparent">
<Grid.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding MouseLeftClickCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Grid}}" />
</Grid.InputBindings>
<ItemsControl ItemsSource="{Binding Path=Blocks}">
<ItemsControl.Resources>
<services:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas ClipToBounds="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image
Source="{Binding ImageSource,Mode=OneWay}"
Width="{Binding Size,Mode=OneWay}"
Height="{Binding Size,Mode=OneWay}"
Visibility="{Binding Visible,Converter={StaticResource BooleanToVisibilityConverter}}"
>
<Image.RenderTransform>
<TranslateTransform X="{Binding DeltaX}" Y="{Binding DeltaY}"/>
</Image.RenderTransform>
</Image>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Grid DataContext="{Binding CombatZone}" ClipToBounds="True">
<Path StrokeThickness="2" Fill="#1100ff00" Stroke="#3300ff00" >
<Path.Data>
<EllipseGeometry
RadiusX="{Binding Radius,Mode=OneWay}"
RadiusY="{Binding Radius,Mode=OneWay}"/>
</Path.Data>
<Path.RenderTransform>
<TranslateTransform X="{Binding Center.X,Mode=OneWay}" Y="{Binding Center.Y,Mode=OneWay}"/>
</Path.RenderTransform>
</Path>
</Grid>
<ItemsControl ItemsSource="{Binding Path=Creatures}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas ClipToBounds="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Path x:Name="CreatureAggroRadius" StrokeThickness="2" Visibility="Hidden" Fill="#11ff0000" Stroke="#33ff0000">
<Path.Data>
<EllipseGeometry
RadiusX="{Binding AggroRadius,Mode=OneWay}"
RadiusY="{Binding AggroRadius,Mode=OneWay}" />
</Path.Data>
<Path.RenderTransform>
<TranslateTransform X="{Binding Position.X,Mode=OneWay}" Y="{Binding Position.Y,Mode=OneWay}"/>
</Path.RenderTransform>
</Path>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsAggressive,Mode=OneWay}" Value="True">
<Setter TargetName="CreatureAggroRadius" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Path=Creatures}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas ClipToBounds="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid x:Name="CreatureMain">
<Grid.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding MouseLeftClickCommand}" />
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding MouseLeftDoubleClickCommand}" />
<MouseBinding Gesture="RightClick" Command="{Binding MouseRightClickCommand}" />
</Grid.InputBindings>
<Path x:Name="CreatureBody" StrokeThickness="2" Fill="Transparent">
<Path.Data>
<EllipseGeometry
RadiusX="{Binding Radius,Mode=OneWay}"
RadiusY="{Binding Radius,Mode=OneWay}" />
</Path.Data>
<Path.RenderTransform>
<TranslateTransform X="{Binding Position.X,Mode=OneWay}" Y="{Binding Position.Y,Mode=OneWay}"/>
</Path.RenderTransform>
</Path>
<Line x:Name="CreatureDirection" X1="0" Y1="0" X2="{Binding Direction.X,Mode=OneWay}" Y2="{Binding Direction.Y,Mode=OneWay}" StrokeThickness="2">
<Line.RenderTransform>
<TranslateTransform X="{Binding Position.X,Mode=OneWay}" Y="{Binding Position.Y,Mode=OneWay}"/>
</Line.RenderTransform>
</Line>
</Grid>
<Grid x:Name="CreatureName" Visibility="Hidden" VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="-2" ScaleY="-1" />
<TranslateTransform
X="{Binding Path=ActualWidth,RelativeSource={RelativeSource AncestorType=Grid}}"
Y="{Binding Path=ActualHeight,RelativeSource={RelativeSource AncestorType=Grid}}"
/>
<ScaleTransform ScaleX="-0.5" ScaleY="-1" />
<ScaleTransform ScaleY="-0.5" />
<TranslateTransform Y="{Binding Radius}" />
<ScaleTransform ScaleY="-2" />
<TranslateTransform X="{Binding Position.X,Mode=OneWay}" Y="{Binding Position.Y,Mode=OneWay}"/>
</TransformGroup>
</Grid.RenderTransform>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name,Mode=OneWay}" Foreground="Black">
<TextBlock.Effect>
<BlurEffect
Radius="2"
KernelType="Box"/>
</TextBlock.Effect>
</TextBlock>
<TextBlock Text="{Binding Name,Mode=OneWay}" Foreground="White" />
<components:StatsBar
Grid.Row="1"
BackgroundSource="/Assets/icons/ps_hpbar_back.png"
ForegroundSource="/Assets/icons/ps_hpbar.png"
Current="{Binding VitalStats.Hp}"
Total="{Binding VitalStats.MaxHp}"
Height="{Binding Radius}"
OnlyBar="True"
Margin="0 2 0 0"
/>
</Grid>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Type,Mode=OneWay}" Value="NPC">
<Setter TargetName="CreatureBody" Property="Stroke" Value="DarkGreen" />
<Setter TargetName="CreatureDirection" Property="Stroke" Value="DarkGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding IsHostile,Mode=OneWay}" Value="True">
<Setter TargetName="CreatureBody" Property="Stroke" Value="LimeGreen" />
<Setter TargetName="CreatureDirection" Property="Stroke" Value="LimeGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding IsDead,Mode=OneWay}" Value="True">
<Setter TargetName="CreatureBody" Property="Stroke" Value="Gray" />
<Setter TargetName="CreatureDirection" Property="Stroke" Value="Gray" />
</DataTrigger>
<DataTrigger Binding="{Binding Type,Mode=OneWay}" Value="Player">
<Setter TargetName="CreatureBody" Property="Stroke" Value="Blue" />
<Setter TargetName="CreatureDirection" Property="Stroke" Value="Blue" />
</DataTrigger>
<DataTrigger Binding="{Binding Type,Mode=OneWay}" Value="Hero">
<Setter TargetName="CreatureBody" Property="Stroke" Value="Black" />
<Setter TargetName="CreatureDirection" Property="Stroke" Value="Black" />
</DataTrigger>
<DataTrigger Binding="{Binding IsAttacker,Mode=OneWay}" Value="True">
<Setter TargetName="CreatureBody" Property="Stroke" Value="Red" />
<Setter TargetName="CreatureDirection" Property="Stroke" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSweepable,Mode=OneWay}" Value="True">
<Setter TargetName="CreatureBody" Property="Stroke" Value="Magenta" />
<Setter TargetName="CreatureDirection" Property="Stroke" Value="Magenta" />
</DataTrigger>
<DataTrigger Binding="{Binding IsTarget,Mode=OneWay}" Value="True">
<Setter TargetName="CreatureName" Property="Visibility" Value="Visible" />
</DataTrigger>
<Trigger SourceName="CreatureMain" Property="IsMouseOver" Value="True">
<Setter TargetName="CreatureName" Property="Visibility" Value="Visible"></Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Path=Drops}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas ClipToBounds="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid x:Name="DropMain">
<Grid.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding MouseLeftClickCommand}" />
<MouseBinding Gesture="RightClick" Command="{Binding MouseRightClickCommand}" />
</Grid.InputBindings>
<Path StrokeThickness="2" Stroke="Gold" Fill="Transparent">
<Path.Data>
<EllipseGeometry
RadiusX="{Binding Radius,Mode=OneWay}"
RadiusY="{Binding Radius,Mode=OneWay}" />
</Path.Data>
<Path.RenderTransform>
<TranslateTransform X="{Binding Position.X,Mode=OneWay}" Y="{Binding Position.Y,Mode=OneWay}"/>
</Path.RenderTransform>
</Path>
</Grid>
<Grid x:Name="DropName" Visibility="Hidden" VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="-2" ScaleY="-1" />
<TranslateTransform
X="{Binding Path=ActualWidth,RelativeSource={RelativeSource AncestorType=Grid}}"
Y="{Binding Path=ActualHeight,RelativeSource={RelativeSource AncestorType=Grid}}"
/>
<ScaleTransform ScaleX="-0.5" ScaleY="-1" />
<ScaleTransform ScaleY="-0.5" />
<TranslateTransform Y="{Binding Radius}" />
<ScaleTransform ScaleY="-2" />
<TranslateTransform X="{Binding Position.X,Mode=OneWay}" Y="{Binding Position.Y,Mode=OneWay}"/>
</TransformGroup>
</Grid.RenderTransform>
<TextBlock Text="{Binding Name,Mode=OneWay}" Foreground="Black" FontSize="11">
<TextBlock.Effect>
<BlurEffect
Radius="2"
KernelType="Box"/>
</TextBlock.Effect>
</TextBlock>
<TextBlock Text="{Binding Name,Mode=OneWay}" Foreground="White" FontSize="11"/>
</Grid>
</Grid>
<DataTemplate.Triggers>
<Trigger SourceName="DropMain" Property="IsMouseOver" Value="True">
<Setter TargetName="DropName" Property="Visibility" Value="Visible"></Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Path=Path}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas ClipToBounds="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Line
X1="{Binding From.X}"
Y1="{Binding From.Y}"
X2="{Binding To.X}"
Y2="{Binding To.Y}"
Stroke="Red"
StrokeDashArray="1 3"
StrokeThickness="1"
/>
<Path
Stroke="Red"
StrokeThickness="1"
>
<Path.Data>
<EllipseGeometry
RadiusX="{Binding Radius,Mode=OneWay}"
RadiusY="{Binding Radius,Mode=OneWay}" />
</Path.Data>
<Path.RenderTransform>
<TranslateTransform X="{Binding To.X,Mode=OneWay}" Y="{Binding To.Y,Mode=OneWay}"/>
</Path.RenderTransform>
</Path>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Right" Background="#66ffffff">
<Grid Margin="10 5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Padding="0 0 0 3">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0:F0}, {1:F0}">
<Binding Path="MousePosition.X" Mode="OneWay"/>
<Binding Path="MousePosition.Y" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="1" Text="{Binding Scale,Mode=OneWay,StringFormat='{}1:{0}'}" HorizontalAlignment="Right" />
</Grid>
</StackPanel>
</Grid>
</ContentControl>

View File

@@ -0,0 +1,55 @@
using Client.Application.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Client.Application.Components
{
/// <summary>
/// Interaction logic for Map.xaml
/// </summary>
public partial class Map : ContentControl
{
public Map()
{
InitializeComponent();
}
private void ContentControl_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (DataContext is MapViewModel)
{
var model = (MapViewModel)DataContext;
model.OnMouseWheel(sender, e);
}
}
private void ContentControl_MouseLeave(object sender, MouseEventArgs e)
{
if (DataContext is MapViewModel)
{
var model = (MapViewModel)DataContext;
model.OnMouseLeave(sender, e);
}
}
private void ContentControl_MouseMove(object sender, MouseEventArgs e)
{
if (DataContext is MapViewModel)
{
var model = (MapViewModel)DataContext;
model.OnMouseMove(sender, e);
}
}
}
}

View File

@@ -0,0 +1,102 @@
<UserControl x:Class="Client.Application.Components.MultipleObjectSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Client.Application.Components"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
x:Name="root">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{Binding Header,ElementName=root}" />
<StackPanel Grid.Row="1" Grid.Column="0">
<TextBox
x:Name="sourceSearch"
HorizontalAlignment="Stretch">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction
Command="{x:Static local:MultipleObjectSelector.SearchSourceCommand}"
CommandParameter="{Binding Text,ElementName=sourceSearch}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<ListBox
ItemsSource="{Binding Source,ElementName=root}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Height="100"
Width="Auto"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
x:Name="source">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}">
<TextBlock.InputBindings>
<MouseBinding
MouseAction="LeftDoubleClick"
Command="{x:Static local:MultipleObjectSelector.AddItemCommand}"
CommandParameter="{Binding SelectedValue,ElementName=source}"/>
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="2" Grid.Column="0">
<Button
Width="20"
Command="{x:Static local:MultipleObjectSelector.AddItemCommand}"
CommandParameter="{Binding SelectedValue,ElementName=source}"
>&#8595;</Button>
<Button
Width="20"
Command="{x:Static local:MultipleObjectSelector.RemoveItemCommand}"
CommandParameter="{Binding SelectedValue,ElementName=target}"
>&#8593;</Button>
</StackPanel>
<StackPanel Grid.Row="3" Grid.Column="0">
<TextBox
x:Name="targetSearch"
HorizontalAlignment="Stretch">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction
Command="{x:Static local:MultipleObjectSelector.SearchTargetCommand}"
CommandParameter="{Binding Text,ElementName=targetSearch}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<ListBox
ItemsSource="{Binding Target,ElementName=root}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Height="100"
Width="Auto"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
x:Name="target">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}">
<TextBlock.InputBindings>
<MouseBinding
MouseAction="LeftDoubleClick"
Command="{x:Static local:MultipleObjectSelector.RemoveItemCommand}"
CommandParameter="{Binding SelectedValue,ElementName=target}"/>
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,109 @@
using Client.Domain.Helpers;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace Client.Application.Components
{
/// <summary>
/// Interaction logic for MobSelector.xaml
/// </summary>
public partial class MultipleObjectSelector : UserControl
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(ICollection<ObjectInfo>), typeof(MultipleObjectSelector), new PropertyMetadata(default(ICollection<ObjectInfo>)));
public ICollection<ObjectInfo> Source
{
get { return (ICollection<ObjectInfo>)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.Register("Target", typeof(ICollection<ObjectInfo>), typeof(MultipleObjectSelector), new PropertyMetadata(default(ICollection<ObjectInfo>)));
public ICollection<ObjectInfo> Target
{
get { return (ICollection<ObjectInfo>)GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(MultipleObjectSelector), new PropertyMetadata(""));
public static RoutedUICommand AddItemCommand { get; } = new RoutedUICommand(
"Add item",
nameof(AddItemCommand),
typeof(MultipleObjectSelector)
);
public static RoutedUICommand RemoveItemCommand { get; } = new RoutedUICommand(
"Remove item",
nameof(RemoveItemCommand),
typeof(MultipleObjectSelector)
);
public static RoutedUICommand SearchSourceCommand { get; } = new RoutedUICommand(
"Search in source",
nameof(SearchSourceCommand),
typeof(MultipleObjectSelector)
);
public static RoutedUICommand SearchTargetCommand { get; } = new RoutedUICommand(
"Search in target",
nameof(SearchTargetCommand),
typeof(MultipleObjectSelector)
);
public MultipleObjectSelector()
{
InitializeComponent();
CommandBindings.Add(new CommandBinding(AddItemCommand, AddItem));
CommandBindings.Add(new CommandBinding(RemoveItemCommand, RemoveItem));
CommandBindings.Add(new CommandBinding(SearchSourceCommand, SearchSource));
CommandBindings.Add(new CommandBinding(SearchTargetCommand, SearchTarget));
}
private void AddItem(object sender, ExecutedRoutedEventArgs e)
{
var item = e.Parameter as ObjectInfo;
if (item != null)
{
Source.Remove(item);
Target.Add(item);
}
}
private void RemoveItem(object sender, ExecutedRoutedEventArgs e)
{
var item = e.Parameter as ObjectInfo;
if (item != null)
{
Target.Remove(item);
Source.Add(item);
}
}
private void SearchSource(object sender, ExecutedRoutedEventArgs e)
{
var searchPredicate = (string)e.Parameter ?? null;
if (searchPredicate != null)
{
CollectionViewSource.GetDefaultView(Source).Filter = item => (item as ObjectInfo)?.Name.Contains(searchPredicate, StringComparison.OrdinalIgnoreCase) ?? false;
}
}
private void SearchTarget(object sender, ExecutedRoutedEventArgs e)
{
var searchPredicate = (string)e.Parameter ?? null;
if (searchPredicate != null)
{
CollectionViewSource.GetDefaultView(Target).Filter = item => (item as ObjectInfo)?.Name.Contains(searchPredicate, StringComparison.OrdinalIgnoreCase) ?? false;
}
}
}
}

View File

@@ -0,0 +1,30 @@
<UserControl x:Class="Client.Application.Components.ObjectSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Client.Application.Components"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
x:Name="root">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Content="{Binding Header,ElementName=root}" Grid.Row="0" Grid.Column="0"/>
<ComboBox
Grid.Row="1" Grid.Column="0"
SelectedValuePath="Id"
DisplayMemberPath="Name"
ItemsSource="{Binding Source,ElementName=root}"
SelectedValue="{Binding SelectedValue,ElementName=root}"
>
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</Grid>
</UserControl>

View File

@@ -0,0 +1,55 @@
using Client.Domain.Entities;
using Client.Domain.Helpers;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Client.Application.Components
{
/// <summary>
/// Interaction logic for SearchableCombobox.xaml
/// </summary>
public partial class ObjectSelector : UserControl
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(ICollection<ObjectInfo>), typeof(ObjectSelector), new PropertyMetadata(default(ICollection<ObjectInfo>)));
public ICollection<ObjectInfo> Source
{
get { return (ICollection<ObjectInfo>)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(object), typeof(ObjectSelector), new PropertyMetadata(default(object)));
public object SelectedValue
{
get { return GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(ObjectSelector), new PropertyMetadata(""));
public ObjectSelector()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,53 @@
<Button x:Class="Client.Application.Components.Skill"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client.Application.Components"
xmlns:services="clr-namespace:Client.Application.Services"
mc:Ignorable="d"
IsEnabled="{Binding Path=IsButtonActive,ElementName=root,Mode=OneWay}"
ToolTipService.ShowOnDisabled="True"
x:Name="root"
Command="{Binding MouseLeftClickCommand}">
<Button.Resources>
<services:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<services:NullToVisibilityConverter x:Key="NullToVisibilityConverter"/>
<DataTemplate x:Key="ToolTipContent">
<StackPanel MaxWidth="300">
<TextBlock TextWrapping="Wrap" FontWeight="Bold" FontSize="14" Margin="0,0,0,5">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} Lv {1}">
<Binding Path="SkillName" Mode="OneWay" Source="{x:Reference root}"/>
<Binding Path="Level" Mode="OneWay" Source="{x:Reference root}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock
TextWrapping="Wrap"
Text="{Binding Source={x:Reference root},Path=Cost,Mode=OneWay,StringFormat='MP Cost {0}'}"
Visibility="{Binding Source={x:Reference root},Path=Cost,Converter={StaticResource NullToVisibilityConverter},ConverterParameter=Hidden,Mode=OneWay}"
/>
<TextBlock
TextWrapping="Wrap"
Text="{Binding Source={x:Reference root},Path=Range,Mode=OneWay,StringFormat='Range {0}'}"
Visibility="{Binding Source={x:Reference root},Path=Range,Converter={StaticResource NullToVisibilityConverter},ConverterParameter=Hidden,Mode=OneWay}"
/>
<TextBlock
TextWrapping="Wrap"
Text="{Binding Source={x:Reference root},Path=Description,Mode=OneWay}"
Visibility="{Binding Source={x:Reference root},Path=Description,Converter={StaticResource NullToVisibilityConverter},ConverterParameter=Hidden,Mode=OneWay}"
/>
</StackPanel>
</DataTemplate>
</Button.Resources>
<Button.Content>
<Grid>
<Image Source="{Binding Path=ImageSource,ElementName=root}" Height="32" Width="32" />
<Rectangle Fill="Black" Opacity=".5" Visibility="{Binding Path=IsReadyToUse,ElementName=root,Converter={StaticResource BooleanToVisibilityConverter},ConverterParameter=Inverted,Mode=OneWay}"/>
</Grid>
</Button.Content>
<Button.ToolTip >
<ContentControl ContentTemplate="{StaticResource ToolTipContent}"/>
</Button.ToolTip>
</Button>

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Client.Application.Components
{
/// <summary>
/// Interaction logic for Skill.xaml
/// </summary>
public partial class Skill : Button
{
public static readonly DependencyProperty IsButtonActiveProperty =
DependencyProperty.Register("IsButtonActive", typeof(bool), typeof(Skill), new PropertyMetadata(true, null, OnCoerceButtonActivity));
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(Skill), new PropertyMetadata(default(ImageSource)));
public static readonly DependencyProperty IsReadyToUseProperty =
DependencyProperty.Register("IsReadyToUse", typeof(bool), typeof(Skill), new PropertyMetadata(true, OnButtonActivityChanged));
public static readonly DependencyProperty SkillNameProperty =
DependencyProperty.Register("SkillName", typeof(string), typeof(Skill), new PropertyMetadata("Skill"));
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level", typeof(uint), typeof(Skill), new PropertyMetadata((uint)1));
public static readonly DependencyProperty CostProperty =
DependencyProperty.Register("Cost", typeof(uint?), typeof(Skill), new PropertyMetadata(null));
public static readonly DependencyProperty RangeProperty =
DependencyProperty.Register("Range", typeof(int?), typeof(Skill), new PropertyMetadata(null));
public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register("Description", typeof(string), typeof(Skill), new PropertyMetadata(null));
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.Register("IsActive", typeof(bool), typeof(Skill), new PropertyMetadata(true, OnButtonActivityChanged));
private static void OnButtonActivityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var model = (Skill)d;
model.CoerceValue(IsButtonActiveProperty);
}
private static object OnCoerceButtonActivity(DependencyObject d, object baseValue)
{
var model = (Skill)d;
return model.IsReadyToUse && model.IsActive;
}
public Skill()
{
InitializeComponent();
}
public bool IsButtonActive
{
get { return (bool)GetValue(IsButtonActiveProperty); }
set { SetValue(IsButtonActiveProperty, value); }
}
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public bool IsReadyToUse
{
get { return (bool)GetValue(IsReadyToUseProperty); }
set { SetValue(IsReadyToUseProperty, value); }
}
public string SkillName
{
get { return (string)GetValue(SkillNameProperty); }
set { SetValue(SkillNameProperty, value); }
}
public uint Level
{
get { return (uint)GetValue(LevelProperty); }
set { SetValue(LevelProperty, value); }
}
public uint? Cost
{
get { return (uint?)GetValue(CostProperty); }
set { SetValue(CostProperty, value); }
}
public int? Range
{
get { return (int?)GetValue(RangeProperty); }
set { SetValue(RangeProperty, value); }
}
public string? Description
{
get { return (string?)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
public bool IsActive
{
get { return (bool)GetValue(IsActiveProperty); }
set { SetValue(IsActiveProperty, value); }
}
}
}

View File

@@ -0,0 +1,23 @@
<UserControl x:Class="Client.Application.Components.StatsBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client.Application.Components"
x:Name="root"
mc:Ignorable="d">
<Grid>
<Image Stretch="Fill" Source="{Binding BackgroundSource,ElementName=root,Mode=OneWay}" />
<Image Stretch="Fill" Source="{Binding ForegroundSource,ElementName=root,Mode=OneWay}" Width="{Binding ForegroundWidth,ElementName=root,Mode=OneWay}" HorizontalAlignment="Left" />
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding Text,ElementName=root,Mode=OneWay}" Foreground="Black">
<TextBlock.Effect>
<BlurEffect
Radius="1.0"
KernelType="Box"/>
</TextBlock.Effect>
</TextBlock>
<TextBlock Text="{Binding Text,ElementName=root,Mode=OneWay}" Foreground="White" />
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Client.Application.Components
{
public partial class StatsBar : UserControl
{
public static readonly DependencyProperty BackgroundSourceProperty =
DependencyProperty.Register("BackgroundSource", typeof(ImageSource), typeof(StatsBar), new PropertyMetadata(default(ImageSource)));
public static readonly DependencyProperty ForegroundSourceProperty =
DependencyProperty.Register("ForegroundSource", typeof(ImageSource), typeof(StatsBar), new PropertyMetadata(default(ImageSource)));
public static readonly DependencyProperty CurrentProperty =
DependencyProperty.Register("Current", typeof(double), typeof(StatsBar), new PropertyMetadata(0.0, OnDataChanged));
public static readonly DependencyProperty TotalProperty =
DependencyProperty.Register("Total", typeof(double), typeof(StatsBar), new PropertyMetadata(0.0, OnDataChanged));
public static readonly DependencyProperty FormatProperty =
DependencyProperty.Register("Format", typeof(string), typeof(StatsBar), new PropertyMetadata("{0}/{1}", OnFormatChanged));
public static readonly DependencyProperty ForegroundWidthProperty =
DependencyProperty.Register("ForegroundWidth", typeof(double), typeof(StatsBar), new PropertyMetadata(0.0, null, OnCoerceForegroundWidth));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatsBar), new PropertyMetadata("", null, OnCoerceText));
public static readonly DependencyProperty OnlyBarProperty =
DependencyProperty.Register("OnlyBar", typeof(bool), typeof(StatsBar), new PropertyMetadata(false, OnOnlyBarChanged));
private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var model = (StatsBar)d;
model.CoerceValue(ForegroundWidthProperty);
model.CoerceValue(TextProperty);
}
private static void OnOnlyBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var model = (StatsBar)d;
model.CoerceValue(TextProperty);
}
private static void OnFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var model = (StatsBar)d;
model.CoerceValue(TextProperty);
}
private static object OnCoerceForegroundWidth(DependencyObject d, object baseValue)
{
var model = (StatsBar)d;
var actualWidth = (double)model.GetValue(ActualWidthProperty);
return actualWidth / 100 * model.GetPercent();
}
private static object OnCoerceText(DependencyObject d, object baseValue)
{
var model = (StatsBar)d;
return model.OnlyBar ? "" : string.Format(model.Format, model.Current, model.Total, model.GetPercent());
}
public StatsBar()
{
InitializeComponent();
}
public override void OnApplyTemplate()
{
SizeChanged += StatsBar_SizeChanged;
}
private void StatsBar_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.WidthChanged)
{
var model = (StatsBar)sender;
model.CoerceValue(ForegroundWidthProperty);
}
}
private double GetPercent()
{
var percent = Current;
if (Total > 0)
{
percent = Current / Total * 100;
}
return percent;
}
public ImageSource BackgroundSource
{
get { return (ImageSource)GetValue(BackgroundSourceProperty); }
set { SetValue(BackgroundSourceProperty, value); }
}
public ImageSource ForegroundSource
{
get { return (ImageSource)GetValue(ForegroundSourceProperty); }
set { SetValue(ForegroundSourceProperty, value); }
}
public double ForegroundWidth
{
get { return (double)GetValue(ForegroundWidthProperty); }
set { SetValue(ForegroundWidthProperty, value); }
}
public double Current
{
get { return (double)GetValue(CurrentProperty); }
set { SetValue(CurrentProperty, value); }
}
public double Total
{
get { return (double)GetValue(TotalProperty); }
set { SetValue(TotalProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string Format
{
get { return (string)GetValue(FormatProperty); }
set { SetValue(FormatProperty, value); }
}
public bool OnlyBar
{
get { return (bool)GetValue(OnlyBarProperty); }
set { SetValue(OnlyBarProperty, value); }
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace Client.Application.Services
{
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var isInverted = parameter == null ? false : true;
var preparedValue = value == null ? false : (bool)value;
preparedValue = isInverted ? !preparedValue : preparedValue;
return innerConverter.Convert(preparedValue, targetType, parameter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
System.Windows.Controls.BooleanToVisibilityConverter innerConverter = new System.Windows.Controls.BooleanToVisibilityConverter();
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace Client.Application.Services
{
public class EqualityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
{
return false;
}
var firstValue = values.First();
for (var i = 1; i < values.Length; i++)
{
if (!firstValue.Equals(values[i]))
{
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace Client.Application.Services
{
public class NullToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var isCollapsedWhenNull = parameter == null ? false : true;
var nullVisibility = isCollapsedWhenNull ? Visibility.Collapsed : Visibility.Hidden;
return value == null ? nullVisibility : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace Client.Application.Services
{
public static class SizeObserver
{
public static readonly DependencyProperty ObserveProperty = DependencyProperty.RegisterAttached(
"Observe",
typeof(bool),
typeof(SizeObserver),
new FrameworkPropertyMetadata(OnObserveChanged));
public static readonly DependencyProperty ObservedWidthProperty = DependencyProperty.RegisterAttached(
"ObservedWidth",
typeof(double),
typeof(SizeObserver));
public static readonly DependencyProperty ObservedHeightProperty = DependencyProperty.RegisterAttached(
"ObservedHeight",
typeof(double),
typeof(SizeObserver));
public static bool GetObserve(FrameworkElement frameworkElement)
{
return (bool)frameworkElement.GetValue(ObserveProperty);
}
public static void SetObserve(FrameworkElement frameworkElement, bool observe)
{
frameworkElement.SetValue(ObserveProperty, observe);
}
public static double GetObservedWidth(FrameworkElement frameworkElement)
{
return (double)frameworkElement.GetValue(ObservedWidthProperty);
}
public static void SetObservedWidth(FrameworkElement frameworkElement, double observedWidth)
{
frameworkElement.SetValue(ObservedWidthProperty, observedWidth);
}
public static double GetObservedHeight(FrameworkElement frameworkElement)
{
return (double)frameworkElement.GetValue(ObservedHeightProperty);
}
public static void SetObservedHeight(FrameworkElement frameworkElement, double observedHeight)
{
frameworkElement.SetValue(ObservedHeightProperty, observedHeight);
}
private static void OnObserveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var frameworkElement = (FrameworkElement)dependencyObject;
if ((bool)e.NewValue)
{
frameworkElement.Loaded += OnFrameworkElementLoaded;
}
else
{
frameworkElement.SizeChanged -= OnFrameworkElementSizeChanged;
}
}
private static void OnFrameworkElementLoaded(object sender, RoutedEventArgs e)
{
var frameworkElement = (FrameworkElement)sender;
frameworkElement.SizeChanged += OnFrameworkElementSizeChanged;
frameworkElement.Loaded -= OnFrameworkElementLoaded;
UpdateObservedSizesForFrameworkElement(frameworkElement);
}
private static void OnFrameworkElementSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateObservedSizesForFrameworkElement((FrameworkElement)sender);
}
private static void UpdateObservedSizesForFrameworkElement(FrameworkElement frameworkElement)
{
frameworkElement.SetCurrentValue(ObservedWidthProperty, frameworkElement.ActualWidth);
frameworkElement.SetCurrentValue(ObservedHeightProperty, frameworkElement.ActualHeight);
}
}
}

View File

@@ -0,0 +1,89 @@
using Client.Domain.AI.Combat;
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Client.Application.ViewModels
{
public class AICombatZoneMapViewModel : ObservableObject
{
public void MapUpdated(float scale, float viewportWidth, float viewportHeight)
{
Scale = scale;
VieportSize = new Vector3(viewportWidth, viewportHeight, 0);
}
public AICombatZoneMapViewModel(CombatZone combatZone, Hero hero)
{
this.combatZone = combatZone;
this.hero = hero;
hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged;
combatZone.PropertyChanged += CombatZone_PropertyChanged;
combatZone.Center.PropertyChanged += CombatZoneCenter_PropertyChanged;
}
private void CombatZoneCenter_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Center");
}
private void CombatZone_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Radius")
{
OnPropertyChanged("Radius");
}
}
public Vector3 Center => new Vector3(
(combatZone.Center.X - hero.Transform.Position.X) / scale + (VieportSize.X / 2),
(combatZone.Center.Y - hero.Transform.Position.Y) / scale + (VieportSize.Y / 2),
0
);
public float Radius => combatZone.Radius / scale;
public float Scale
{
get => scale;
set
{
if (scale != value)
{
scale = value;
OnPropertyChanged("Center");
OnPropertyChanged("Radius");
}
}
}
public Vector3 VieportSize
{
get => vieportSize;
set
{
if (vieportSize != value)
{
vieportSize = value;
OnPropertyChanged("Center");
}
}
}
private void HeroPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Center");
}
private readonly CombatZone combatZone;
private readonly Hero hero;
private float scale = 1;
private Vector3 vieportSize = new Vector3(0, 0, 0);
}
}

View File

@@ -0,0 +1,344 @@
using Client.Application.Commands;
using Client.Application.Components;
using Client.Application.Views;
using Client.Domain.AI;
using Client.Domain.AI.IO;
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.Events;
using Client.Domain.Helpers;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
namespace Client.Application.ViewModels
{
public class AIConfigViewModel :
ObservableObject,
EventHandlerInterface<HeroCreatedEvent>,
EventHandlerInterface<HeroDeletedEvent>
{
public void Handle(HeroCreatedEvent @event)
{
hero = @event.Hero;
}
public void Handle(HeroDeletedEvent @event)
{
hero = null;
}
public class SkillCondition : ObservableObject
{
private uint id;
private byte maxTargetPercentHp = 100;
private byte minPlayerPercentMp = 0;
private byte maxPlayerPercentHp = 100;
public uint Id { get => id; set { if (value != id) { id = value; OnPropertyChanged(); } } }
public byte MaxTargetPercentHp { get => maxTargetPercentHp; set { if (value != maxTargetPercentHp) { maxTargetPercentHp = value; OnPropertyChanged(); } } }
public byte MinPlayerPercentMp { get => minPlayerPercentMp; set { if (value != minPlayerPercentMp) { minPlayerPercentMp = value; OnPropertyChanged(); } } }
public byte MaxPlayerPercentHp { get => maxPlayerPercentHp; set { if (value != maxPlayerPercentHp) { maxPlayerPercentHp = value; OnPropertyChanged(); } } }
}
public class CombatZone : ObservableObject
{
private float x = 0;
private float y = 0;
private float radius = 0;
public float X { get => x; set { if (value != x) { x = value; OnPropertyChanged(); } } }
public float Y { get => y; set { if (value != y) { y = value; OnPropertyChanged(); } } }
public float Radius { get => radius; set { if (value != radius) { radius = value; OnPropertyChanged(); } } }
}
public AIConfigViewModel(NpcInfoHelperInterface npcInfoHelper, ItemInfoHelperInterface itemInfoHelper, SkillInfoHelperInterface skillInfoHelper, Config config, ConfigSerializerInterface configSerializer, ConfigDeserializerInterface configDeserializer)
{
this.npcInfoHelper = npcInfoHelper;
this.itemInfoHelper = itemInfoHelper;
this.skillInfoHelper = skillInfoHelper;
this.config = config;
this.configSerializer = configSerializer;
this.configDeserializer = configDeserializer;
SaveDialogCommand = new RelayCommand(OnSaveDialog);
OpenDialogCommand = new RelayCommand(OnOpenDialog);
SaveCommand = new RelayCommand(OnSave);
ResetCommand = new RelayCommand(OnReset);
GetHeroPosition = new RelayCommand(OnGetHeroPosition);
Skills = new ObservableCollection<ObjectInfo>(skillInfoHelper.GetAllSkills().Select(x => x.Value).Where(x => x.IsActive).ToList());
}
public ICommand SaveDialogCommand { get; }
public ICommand OpenDialogCommand { get; }
public ICommand SaveCommand { get; }
public ICommand ResetCommand { get; }
public ICommand GetHeroPosition { get; }
public Action? Close { get; set; }
public Action<string>? OpenSaveDialog { get; set; }
public Func<string?>? OpenOpenDialog { get; set; }
public ObservableCollection<ObjectInfo> Skills { get; set; }
public uint MobsMaxDeltaZ { get => mobsMaxDeltaZ; set { if (value != mobsMaxDeltaZ) { mobsMaxDeltaZ = value; OnPropertyChanged(); } } }
public ObservableCollection<ObjectInfo> ExcludedMobs { get; set; } = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> SelectedExcludedMobs { get; set; } = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> IncludedMobs { get; set; } = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> SelectedIncludedMobs { get; set; } = new ObservableCollection<ObjectInfo>();
public byte? MobLevelLowerLimit { get => mobLevelLowerLimit; set { if (value != mobLevelLowerLimit) { mobLevelLowerLimit = value; OnPropertyChanged(); } } }
public byte? MobLevelUpperLimit { get => mobLevelUpperLimit; set { if (value != mobLevelUpperLimit) { mobLevelUpperLimit = value; OnPropertyChanged(); } } }
public ObservableCollection<ObjectInfo> ExcludedSpoilMobs { get; set; } = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> SelectedExcludedSpoilMobs { get; set; } = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> IncludedSpoilMobs { get; set; } = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> SelectedIncludedSpoilMobs { get; set; } = new ObservableCollection<ObjectInfo>();
public bool SpoilIfPossible { get => spoilIfPossible; set { if (value != spoilIfPossible) { spoilIfPossible = value; OnPropertyChanged(); } } }
public bool SpoilIsPriority { get => spoilIsPriority; set { if (value != spoilIsPriority) { spoilIsPriority = value; OnPropertyChanged(); } } }
public uint SpoilSkillId { get => spoilSkillId; set { if (value != spoilSkillId) { spoilSkillId = value; OnPropertyChanged(); } } }
public uint SweeperSkillId { get => sweeperSkillId; set { if (value != sweeperSkillId) { sweeperSkillId = value; OnPropertyChanged(); } } }
public byte SweepAttemptsCount { get => sweepAttemptsCount; set { if (value != sweepAttemptsCount) { sweepAttemptsCount = value; OnPropertyChanged(); } } }
public ObservableCollection<ObjectInfo> ExcludedItems { get; set; } = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> SelectedExcludedItems { get; set; } = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> IncludedItems { get; set; } = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> SelectedIncludedItems { get; set; } = new ObservableCollection<ObjectInfo>();
public bool PickupIfPossible { get => pickupIfPossible; set { if (value != pickupIfPossible) { pickupIfPossible = value; OnPropertyChanged(); } } }
public uint PickupMaxDeltaZ { get => pickupMaxDeltaZ; set { if (value != pickupMaxDeltaZ) { pickupMaxDeltaZ = value; OnPropertyChanged(); } } }
public byte PickupAttemptsCount { get => pickupAttemptsCount; set { if (value != pickupAttemptsCount) { pickupAttemptsCount = value; OnPropertyChanged(); } } }
public byte RestStartPercentHp { get => restStartPercentHp; set { if (value != restStartPercentHp) { restStartPercentHp = value; OnPropertyChanged(); } } }
public byte RestEndPercentHp { get => restEndPercentHp; set { if (value != restEndPercentHp) { restEndPercentHp = value; OnPropertyChanged(); } } }
public byte RestStartPercentMp { get => restStartPercentMp; set { if (value != restStartPercentMp) { restStartPercentMp = value; OnPropertyChanged(); } } }
public byte RestEndPercentMp { get => restEndPercentMp; set { if (value != restEndPercentMp) { restEndPercentMp = value; OnPropertyChanged(); } } }
public ObservableCollection<SkillCondition> CombatSkills { get; set; } = new ObservableCollection<SkillCondition>();
public bool AutoUseShots { get => autoUseShots; set { if (value != autoUseShots) { autoUseShots = value; OnPropertyChanged(); } } }
public uint AttackDistanceMili { get => attackDistanceMili; set { if (value != attackDistanceMili) { attackDistanceMili = value; OnPropertyChanged(); } } }
public uint AttackDistanceBow { get => attackDistanceBow; set { if (value != attackDistanceBow) { attackDistanceBow = value; OnPropertyChanged(); } } }
public bool UseOnlySkills { get => useOnlySkills; set { if (value != useOnlySkills) { useOnlySkills = value; OnPropertyChanged(); } } }
public CombatZone Zone { get => combatZone; set { if (value != combatZone) { combatZone = value; OnPropertyChanged(); } } }
public byte DelevelingTargetLevel { get => delevelingTargetLevel; set { if (value != delevelingTargetLevel) { delevelingTargetLevel = value; OnPropertyChanged(); } } }
public uint DelevelingAttackDistance { get => delevelingAttackDistance; set { if (value != delevelingAttackDistance) { delevelingAttackDistance = value; OnPropertyChanged(); } } }
public uint DelevelingSkillId { get => delevelingSkillId; set { if (value != delevelingSkillId) { delevelingSkillId = value; OnPropertyChanged(); } } }
public void LoadConfig()
{
LoadConfigFrom(config);
}
private void LoadConfigFrom(Config config)
{
LoadCollections(config);
MobsMaxDeltaZ = config.Combat.MobsMaxDeltaZ;
MobLevelLowerLimit = config.Combat.MobLevelLowerLimit;
MobLevelUpperLimit = config.Combat.MobLevelUpperLimit;
SpoilIfPossible = config.Combat.SpoilIfPossible;
SpoilIsPriority = config.Combat.SpoilIsPriority;
SpoilSkillId = config.Combat.SpoilSkillId;
SweeperSkillId = config.Combat.SweeperSkillId;
SweepAttemptsCount = config.Combat.SweepAttemptsCount;
PickupIfPossible = config.Combat.PickupIfPossible;
PickupMaxDeltaZ = config.Combat.PickupMaxDeltaZ;
PickupAttemptsCount = config.Combat.PickupAttemptsCount;
RestStartPercentHp = config.Combat.RestStartPercentHp;
RestEndPercentHp = config.Combat.RestEndPecentHp;
RestStartPercentMp = config.Combat.RestStartPecentMp;
RestEndPercentMp = config.Combat.RestEndPecentMp;
AutoUseShots = config.Combat.AutoUseShots;
AttackDistanceMili = config.Combat.AttackDistanceMili;
AttackDistanceBow = config.Combat.AttackDistanceBow;
UseOnlySkills = config.Combat.UseOnlySkills;
Zone.X = config.Combat.Zone.Center.X;
Zone.Y = config.Combat.Zone.Center.Y;
Zone.Radius = config.Combat.Zone.Radius;
DelevelingTargetLevel = config.Deleveling.TargetLevel;
DelevelingAttackDistance = config.Deleveling.AttackDistance;
DelevelingSkillId = config.Deleveling.SkillId;
}
private void SaveConfig()
{
config.Combat.MobsMaxDeltaZ = MobsMaxDeltaZ;
config.Combat.MobLevelLowerLimit = MobLevelLowerLimit;
config.Combat.MobLevelUpperLimit = MobLevelUpperLimit;
config.Combat.SpoilIfPossible = SpoilIfPossible;
config.Combat.SpoilIsPriority = SpoilIsPriority;
config.Combat.SpoilSkillId = SpoilSkillId;
config.Combat.SweeperSkillId = SweeperSkillId;
config.Combat.SweepAttemptsCount = SweepAttemptsCount;
config.Combat.PickupIfPossible = PickupIfPossible;
config.Combat.PickupMaxDeltaZ = PickupMaxDeltaZ;
config.Combat.PickupAttemptsCount = PickupAttemptsCount;
config.Combat.RestStartPercentHp = RestStartPercentHp;
config.Combat.RestEndPecentHp = RestEndPercentHp;
config.Combat.RestStartPecentMp = RestStartPercentMp;
config.Combat.RestEndPecentMp = RestEndPercentMp;
config.Combat.AutoUseShots = AutoUseShots;
config.Combat.AttackDistanceMili = AttackDistanceMili;
config.Combat.AttackDistanceBow = AttackDistanceBow;
config.Combat.UseOnlySkills = UseOnlySkills;
config.Combat.Zone.Center.X = Zone.X;
config.Combat.Zone.Center.Y = Zone.Y;
config.Combat.Zone.Center.Z = 0;
config.Combat.Zone.Radius = Zone.Radius;
config.Deleveling.TargetLevel = DelevelingTargetLevel;
config.Deleveling.AttackDistance = DelevelingAttackDistance;
config.Deleveling.SkillId = DelevelingSkillId;
SaveCollections();
}
private void LoadCollections(Config config)
{
ExcludedMobs.Clear();
npcInfoHelper.GetAllNpc().ForEach(n => ExcludedMobs.Add(n));
SelectedExcludedMobs.Clear();
IncludedMobs.Clear();
npcInfoHelper.GetAllNpc().ForEach(n => IncludedMobs.Add(n));
SelectedIncludedMobs.Clear();
ExcludedSpoilMobs.Clear();
npcInfoHelper.GetAllNpc().ForEach(n => ExcludedSpoilMobs.Add(n));
SelectedExcludedSpoilMobs.Clear();
IncludedSpoilMobs.Clear();
npcInfoHelper.GetAllNpc().ForEach(n => IncludedSpoilMobs.Add(n));
SelectedIncludedSpoilMobs.Clear();
ExcludedItems.Clear();
itemInfoHelper.GetAllItems().ForEach(n => ExcludedItems.Add(n));
SelectedExcludedItems.Clear();
IncludedItems.Clear();
itemInfoHelper.GetAllItems().ForEach(n => IncludedItems.Add(n));
SelectedIncludedItems.Clear();
var loadCollection = (ObservableCollection<ObjectInfo> items, ObservableCollection<ObjectInfo> selectedItems, Dictionary<uint, bool> configItems) =>
{
for (var i = items.Count - 1; i >= 0; i--)
{
var mob = items[i];
if (configItems.ContainsKey(mob.Id))
{
items.Remove(mob);
selectedItems.Add(mob);
}
}
};
loadCollection(ExcludedMobs, SelectedExcludedMobs, config.Combat.ExcludedMobIds);
loadCollection(IncludedMobs, SelectedIncludedMobs, config.Combat.IncludedMobIds);
loadCollection(ExcludedSpoilMobs, SelectedExcludedSpoilMobs, config.Combat.ExcludedSpoilMobIds);
loadCollection(IncludedSpoilMobs, SelectedIncludedSpoilMobs, config.Combat.IncludedSpoilMobIds);
loadCollection(ExcludedItems, SelectedExcludedItems, config.Combat.ExcludedItemIdsToPickup);
loadCollection(IncludedItems, SelectedIncludedItems, config.Combat.IncludedItemIdsToPickup);
CombatSkills.RemoveAll();
config.Combat.SkillConditions.ForEach(x =>
{
CombatSkills.Add(new SkillCondition()
{
Id = x.Id,
MaxTargetPercentHp = x.MaxTargetPercentHp,
MinPlayerPercentMp = x.MinPlayerPercentMp,
MaxPlayerPercentHp = x.MaxPlayerPercentHp
});
});
}
private void SaveCollections()
{
config.Combat.ExcludedMobIds = SelectedExcludedMobs.ToDictionary(x => x.Id, x => true);
config.Combat.IncludedMobIds = SelectedIncludedMobs.ToDictionary(x => x.Id, x => true);
config.Combat.ExcludedSpoilMobIds = SelectedExcludedSpoilMobs.ToDictionary(x => x.Id, x => true);
config.Combat.IncludedSpoilMobIds = SelectedIncludedSpoilMobs.ToDictionary(x => x.Id, x => true);
config.Combat.ExcludedItemIdsToPickup = SelectedExcludedItems.ToDictionary(x => x.Id, x => true);
config.Combat.IncludedItemIdsToPickup = SelectedIncludedItems.ToDictionary(x => x.Id, x => true);
config.Combat.SkillConditions = CombatSkills.Select(x => new Config.SkillCondition()
{
Id = x.Id,
MaxTargetPercentHp = x.MaxTargetPercentHp,
MinPlayerPercentMp = x.MinPlayerPercentMp,
MaxPlayerPercentHp = x.MaxPlayerPercentHp
}).ToList();
}
private void OnSaveDialog(object? sender)
{
SaveConfig();
if (OpenSaveDialog != null)
{
OpenSaveDialog(configSerializer.Serialize(config));
}
if (Close != null)
{
Close();
}
}
private void OnOpenDialog(object? sender)
{
if (OpenOpenDialog != null)
{
var data = OpenOpenDialog();
if (data != null)
{
var config = configDeserializer.Deserialize(data);
if (config != null)
{
LoadConfigFrom(config);
}
}
}
}
private void OnSave(object? sender)
{
SaveConfig();
if (Close != null)
{
Close();
}
}
private void OnReset(object? sender)
{
LoadConfigFrom(config);
}
private void OnGetHeroPosition(object? sender)
{
if (hero != null)
{
Zone.X = hero.Transform.Position.X;
Zone.Y = hero.Transform.Position.Y;
}
}
private readonly NpcInfoHelperInterface npcInfoHelper;
private readonly ItemInfoHelperInterface itemInfoHelper;
private readonly SkillInfoHelperInterface skillInfoHelper;
private readonly Config config;
private readonly ConfigSerializerInterface configSerializer;
private readonly ConfigDeserializerInterface configDeserializer;
private uint mobsMaxDeltaZ = 0;
private byte? mobLevelLowerLimit = null;
private byte? mobLevelUpperLimit = null;
private bool spoilIfPossible = false;
private bool spoilIsPriority = false;
private uint spoilSkillId = 0;
private uint sweeperSkillId = 0;
private byte sweepAttemptsCount = 0;
private bool pickupIfPossible = false;
private uint pickupMaxDeltaZ = 0;
private byte pickupAttemptsCount = 0;
private byte restStartPercentHp = 0;
private byte restEndPercentHp = 0;
private byte restStartPercentMp = 0;
private byte restEndPercentMp = 0;
private bool autoUseShots = false;
private uint attackDistanceMili = 0;
private uint attackDistanceBow = 0;
private bool useOnlySkills = false;
private CombatZone combatZone = new CombatZone();
private Hero? hero;
private byte delevelingTargetLevel = 0;
private uint delevelingAttackDistance = 0;
private uint delevelingSkillId = 0;
}
}

View File

@@ -0,0 +1,55 @@
using Client.Domain.Enums;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace Client.Application.ViewModels
{
public class ChatMessageViewModel
{
public string Text
{
get
{
return (message.Name != "" ? message.Name + ": " : "") + message.Text;
}
}
public SolidColorBrush Color
{
get
{
SolidColorBrush? color;
if (!colors.TryGetValue(message.Channel, out color))
{
color = defaultColor;
}
return color;
}
}
public ChatMessageViewModel(ChatMessage message)
{
this.message = message;
colors = new Dictionary<ChatChannelEnum, SolidColorBrush>
{
{ ChatChannelEnum.Shout, Brushes.OrangeRed },
{ ChatChannelEnum.Tell, Brushes.Magenta },
{ ChatChannelEnum.Party, Brushes.LimeGreen },
{ ChatChannelEnum.Clan, Brushes.Violet },
{ ChatChannelEnum.Gm, Brushes.Red },
{ ChatChannelEnum.Trade, Brushes.HotPink }
};
}
private readonly ChatMessage message;
private readonly Dictionary<ChatChannelEnum, SolidColorBrush> colors;
private readonly SolidColorBrush defaultColor = Brushes.Black;
}
}

View File

@@ -0,0 +1,106 @@
using Client.Application.Commands;
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Client.Application.ViewModels
{
public class CreatureListViewModel : ObservableObject
{
public uint Id => creature.Id;
public string Name => creature.Name;
public string BriefInfo => creature.BriefInfo;
public float Distance => creature.Distance(hero);
public float DeltaZ => creature.DeltaZ(hero);
public bool IsTarget => Id == hero.TargetId;
public bool IsAttacker => hero.AttackerIds.Contains(creature.Id);
public ICommand MouseLeftClickCommand { get; }
public ICommand MouseLeftDoubleClickCommand { get; }
public ICommand MouseRightClickCommand { get; }
private void OnMouseLeftClick(object? obj)
{
worldHandler.RequestAcquireTarget(Id);
}
private void OnMouseLeftDoubleClick(object? obj)
{
worldHandler.RequestAttackOrFollow(Id);
}
private async Task OnMouseRightClick(object? obj)
{
await pathMover.MoveUntilReachedAsync(creature.Transform.Position);
}
public CreatureListViewModel(WorldHandler worldHandler, AsyncPathMoverInterface pathMover, CreatureInterface creature, Hero hero)
{
creature.PropertyChanged += Creature_PropertyChanged;
creature.Transform.Position.PropertyChanged += Position_PropertyChanged;
hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged;
hero.PropertyChanged += Hero_PropertyChanged;
MouseLeftClickCommand = new RelayCommand(OnMouseLeftClick);
MouseLeftDoubleClickCommand = new RelayCommand(OnMouseLeftDoubleClick);
MouseRightClickCommand = new RelayCommand(async (o) => await OnMouseRightClick(o));
this.creature = creature;
this.hero = hero;
this.worldHandler = worldHandler;
this.pathMover = pathMover;
}
private void HeroPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Distance");
OnPropertyChanged("DeltaZ");
}
private void Position_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Distance");
OnPropertyChanged("DeltaZ");
}
private void Creature_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
{
OnPropertyChanged("Name");
}
if (e.PropertyName == "BriefInfo")
{
OnPropertyChanged("BriefInfo");
}
}
private void Hero_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "TargetId")
{
OnPropertyChanged("IsTarget");
}
if (e.PropertyName == "AttackerIds")
{
OnPropertyChanged("IsAttacker");
}
}
private readonly CreatureInterface creature;
private readonly Hero hero;
private readonly WorldHandler worldHandler;
private readonly AsyncPathMoverInterface pathMover;
}
}

View File

@@ -0,0 +1,178 @@
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.Enums;
using Client.Domain.Service;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows;
using Client.Application.Commands;
using System.Net.Http;
namespace Client.Application.ViewModels
{
public class CreatureMapViewModel : ObservableObject
{
public void MapUpdated(float scale, float viewportWidth, float viewportHeight)
{
Scale = scale;
VieportSize = new Vector3(viewportWidth, viewportHeight, 0);
}
public uint Id => creature.Id;
public string Name => creature.Name;
public Vector3 Position => new Vector3(
(creature.Transform.Position.X - hero.Transform.Position.X) / scale + (VieportSize.X / 2),
(creature.Transform.Position.Y - hero.Transform.Position.Y) / scale + (VieportSize.Y / 2),
0
);
public Vector3 Direction => new Vector3(
creature.Transform.Direction.X * Radius * 2f,
creature.Transform.Direction.Y * Radius * 2f,
0
);
public VitalStats VitalStats => creature.VitalStats;
public float Radius => MAX_RADIUS - (1/MapViewModel.MIN_SCALE - 1/scale) / (1/MapViewModel.MIN_SCALE - 1/MapViewModel.MAX_SCALE) * (MAX_RADIUS - MIN_RADIUS);
public float Scale
{
get => scale;
set
{
if (scale != value)
{
scale = value;
OnPropertyChanged("Position");
OnPropertyChanged("Direction");
OnPropertyChanged("Radius");
OnPropertyChanged("AggroRadius");
}
}
}
public Vector3 VieportSize
{
get => vieportSize;
set
{
if (vieportSize != value)
{
vieportSize = value;
OnPropertyChanged("Position");
OnPropertyChanged("Direction");
}
}
}
public CreatureTypeEnum Type => creature.Type;
public bool IsTarget => Id == hero.TargetId;
public bool IsAggressive => creature.AggroRadius > 0 && !creature.VitalStats.IsDead && creature.IsHostile;
public float AggroRadius => creature.AggroRadius / scale;
public bool IsAttacker => hero.AttackerIds.Contains(creature.Id);
public bool IsDead => creature.VitalStats.IsDead;
public bool IsHostile => creature.IsHostile;
public bool IsSweepable => creature is NPC && ((NPC) creature).SpoilState == SpoilStateEnum.Sweepable;
public ICommand MouseLeftClickCommand { get; }
public ICommand MouseLeftDoubleClickCommand { get; }
public ICommand MouseRightClickCommand { get; }
private void OnMouseLeftClick(object? obj)
{
worldHandler.RequestAcquireTarget(Id);
}
private void OnMouseLeftDoubleClick(object? obj)
{
worldHandler.RequestAttackOrFollow(Id);
}
private async Task OnMouseRightClick(object? obj)
{
await pathMover.MoveUntilReachedAsync(creature.Transform.Position);
}
public CreatureMapViewModel(WorldHandler worldHandler, AsyncPathMoverInterface pathMover, CreatureInterface creature, Hero hero)
{
this.creature = creature;
this.hero = hero;
this.worldHandler = worldHandler;
this.pathMover = pathMover;
creature.PropertyChanged += Creature_PropertyChanged;
creature.Transform.PropertyChanged += Transform_PropertyChanged;
creature.Transform.Position.PropertyChanged += Position_PropertyChanged;
creature.VitalStats.PropertyChanged += VitalStats_PropertyChanged;
hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged;
hero.PropertyChanged += Hero_PropertyChanged;
MouseLeftClickCommand = new RelayCommand(OnMouseLeftClick);
MouseLeftDoubleClickCommand = new RelayCommand(OnMouseLeftDoubleClick);
MouseRightClickCommand = new RelayCommand(async (o) => await OnMouseRightClick(o));
}
private void VitalStats_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Hp" || e.PropertyName == "MaxHp")
{
OnPropertyChanged("VitalStats");
}
if (e.PropertyName == "IsDead")
{
OnPropertyChanged("IsAggressive");
OnPropertyChanged("IsDead");
}
}
private void Hero_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "TargetId")
{
OnPropertyChanged("IsTarget");
}
if (e.PropertyName == "AttackerIds")
{
OnPropertyChanged("IsAttacker");
}
}
private void HeroPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Position");
OnPropertyChanged("Direction");
}
private void Position_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Position");
OnPropertyChanged("Direction");
}
private void Transform_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Direction")
{
OnPropertyChanged("Direction");
}
}
private void Creature_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
{
OnPropertyChanged("Name");
}
if (e.PropertyName == "SpoilState")
{
OnPropertyChanged("IsSweepable");
}
}
private readonly CreatureInterface creature;
private readonly Hero hero;
private readonly WorldHandler worldHandler;
private readonly AsyncPathMoverInterface pathMover;
private float scale = 1;
private static readonly float MAX_RADIUS = 10;
private static readonly float MIN_RADIUS = 2;
private Vector3 vieportSize = new Vector3(0, 0, 0);
}
}

View File

@@ -0,0 +1,132 @@
using Client.Application.Commands;
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Client.Application.ViewModels
{
public class DropListViewModel : ObservableObject
{
public uint Id
{
get
{
return drop.Id;
}
}
public string Name
{
get
{
return drop.Name;
}
}
public uint Amount
{
get
{
return drop.Amount;
}
}
public uint ItemId
{
get
{
return drop.ItemId;
}
}
public float Distance
{
get
{
return drop.Transform.Position.HorizontalDistance(hero.Transform.Position);
}
}
public float DeltaZ
{
get
{
return (drop.Transform.Position.Z - hero.Transform.Position.Z);
}
}
public string IconName
{
get
{
return "/Assets/icons/" + drop.IconName + ".png";
}
}
public ICommand MouseLeftClickCommand { get; }
public ICommand MouseRightClickCommand { get; }
private void OnMouseLeftClick(object? obj)
{
worldHandler.RequestPickUp(Id);
}
private async Task OnMouseRightClick(object? obj)
{
await pathMover.MoveUntilReachedAsync(drop.Transform.Position);
}
public DropListViewModel(WorldHandler worldHandler, AsyncPathMoverInterface pathMover, Drop drop, Hero hero)
{
this.drop = drop;
this.hero = hero;
this.worldHandler = worldHandler;
this.pathMover = pathMover;
drop.PropertyChanged += Drop_PropertyChanged;
drop.Transform.Position.PropertyChanged += DropPosition_PropertyChanged;
hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged;
MouseLeftClickCommand = new RelayCommand(OnMouseLeftClick);
MouseRightClickCommand = new RelayCommand(async (o) => await OnMouseRightClick(o));
}
private void HeroPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Distance");
OnPropertyChanged("DeltaZ");
}
private void DropPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Distance");
OnPropertyChanged("DeltaZ");
}
private void Drop_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
{
OnPropertyChanged("Name");
}
if (e.PropertyName == "Amount")
{
OnPropertyChanged("Amount");
}
if (e.PropertyName == "ItemId")
{
OnPropertyChanged("ItemId");
}
if (e.PropertyName == "IconName")
{
OnPropertyChanged("IconName");
}
}
private readonly Drop drop;
private readonly Hero hero;
private readonly WorldHandler worldHandler;
private readonly AsyncPathMoverInterface pathMover;
}
}

View File

@@ -0,0 +1,110 @@
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.Enums;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using Client.Domain.Service;
using System.Windows.Input;
using Client.Application.Commands;
namespace Client.Application.ViewModels
{
public class DropMapViewModel : ObservableObject
{
public void MapUpdated(float scale, float viewportWidth, float viewportHeight)
{
Scale = scale;
VieportSize = new Vector3(viewportWidth, viewportHeight, 0);
}
public uint Id => drop.Id;
public string Name => drop.Name + " (" + drop.Amount + ")";
public Vector3 Position => new Vector3(
(drop.Transform.Position.X - hero.Transform.Position.X) / scale + (VieportSize.X / 2),
(drop.Transform.Position.Y - hero.Transform.Position.Y) / scale + (VieportSize.Y / 2),
0
);
public float Radius => MathF.Max(MAX_RADIUS / scale, MIN_RADIUS);
public float Scale
{
get => scale;
set
{
if (scale != value)
{
scale = value;
OnPropertyChanged("Position");
OnPropertyChanged("Radius");
}
}
}
public Vector3 VieportSize
{
get => vieportSize;
set
{
if (vieportSize != value)
{
vieportSize = value;
OnPropertyChanged("Position");
}
}
}
public ICommand MouseLeftClickCommand { get; }
public ICommand MouseRightClickCommand { get; }
private void OnMouseLeftClick(object? obj)
{
worldHandler.RequestPickUp(Id);
}
private async Task OnMouseRightClick(object? obj)
{
await pathMover.MoveUntilReachedAsync(drop.Transform.Position);
}
public DropMapViewModel(WorldHandler worldHandler, AsyncPathMoverInterface pathMover, Drop drop, Hero hero)
{
this.drop = drop;
this.hero = hero;
this.worldHandler = worldHandler;
this.pathMover = pathMover;
drop.PropertyChanged += Creature_PropertyChanged;
drop.Transform.Position.PropertyChanged += Position_PropertyChanged;
hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged;
MouseLeftClickCommand = new RelayCommand(OnMouseLeftClick);
MouseRightClickCommand = new RelayCommand(async (o) => await OnMouseRightClick(o));
}
private void HeroPosition_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("Position");
}
private void Position_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("Position");
}
private void Creature_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name" || e.PropertyName == "Amount")
{
OnPropertyChanged("Name");
}
}
private readonly Drop drop;
private readonly Hero hero;
private readonly WorldHandler worldHandler;
private readonly AsyncPathMoverInterface pathMover;
private float scale = 1;
private static readonly float MAX_RADIUS = 8;
private static readonly float MIN_RADIUS = 2;
private Vector3 vieportSize = new Vector3(0, 0, 0);
}
}

View File

@@ -0,0 +1,151 @@
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Client.Application.ViewModels
{
public class HeroSummaryInfoViewModel : ObservableObject
{
public bool IsVisible {
get
{
return true;
}
}
public FullName Fullname
{
get
{
return hero.FullName;
}
}
public Phenotype Phenotype
{
get
{
return hero.Phenotype;
}
}
public Transform Transform
{
get
{
return hero.Transform;
}
}
public ExperienceInfo Experience
{
get
{
return hero.ExperienceInfo;
}
}
public VitalStats VitalStats
{
get
{
return hero.VitalStats;
}
}
public InventoryInfo InventoryInfo
{
get
{
return hero.InventoryInfo;
}
}
public ulong Money
{
get
{
return 0;
}
}
public TargetSummaryInfoViewModel? Target
{
get
{
return target;
}
}
public List<uint> Attackers
{
get
{
return hero.AttackerIds.ToList();
}
}
public HeroSummaryInfoViewModel(Hero hero)
{
this.hero = hero;
hero.FullName.PropertyChanged += FullName_PropertyChanged;
hero.Phenotype.PropertyChanged += Phenotype_PropertyChanged;
hero.ExperienceInfo.PropertyChanged += ExperienceInfo_PropertyChanged;
hero.Transform.PropertyChanged += Transform_PropertyChanged;
hero.VitalStats.PropertyChanged += VitalStats_PropertyChanged;
hero.InventoryInfo.PropertyChanged += InventoryInfo_PropertyChanged;
hero.PropertyChanged += Hero_PropertyChanged;
}
private void Hero_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Target")
{
if (target == null && hero.Target != null)
{
target = new TargetSummaryInfoViewModel(hero.Target, hero);
OnPropertyChanged("Target");
}
else if (target != null && hero.Target == null)
{
target = null;
OnPropertyChanged("Target");
}
}
else if (e.PropertyName == "AttackerIds")
{
OnPropertyChanged("Attackers");
}
}
private void InventoryInfo_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("InventoryInfo");
}
private void VitalStats_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("VitalStats");
}
private void Transform_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Transform");
}
private void Phenotype_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Phenotype");
}
private void ExperienceInfo_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Experience");
}
private void FullName_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Fullname");
}
private readonly Hero hero;
private TargetSummaryInfoViewModel? target;
}
}

View File

@@ -0,0 +1,46 @@
using Client.Application.Commands;
using Client.Application.Components;
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Client.Application.ViewModels
{
public class ItemListViewModel : ObservableObject
{
public uint Id => item.Id;
public string Name => item.Name;
public string Description => item.FullDescription;
public string IconName => "/Assets/icons/" + item.IconName + ".png";
public ICommand MouseLeftClickCommand { get; }
public ICommand MouseRightClickCommand { get; }
private void OnLeftMouseClick(object? obj)
{
worldHandler.RequestUseItem(Id);
}
private void OnRightMouseClick(object? obj)
{
worldHandler.RequestToggleAutouseSoulshot(Id);
}
public ItemListViewModel(WorldHandler worldHandler, ItemInterface item)
{
this.worldHandler = worldHandler;
this.item = item;
//skill.PropertyChanged += Skill_PropertyChanged;
MouseLeftClickCommand = new RelayCommand(OnLeftMouseClick);
MouseRightClickCommand = new RelayCommand(OnRightMouseClick);
}
private readonly WorldHandler worldHandler;
private readonly ItemInterface item;
}
}

View File

@@ -0,0 +1,210 @@
using Client.Application.Commands;
using Client.Application.Components;
using Client.Domain.AI;
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.Events;
using Client.Domain.Service;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace Client.Application.ViewModels
{
public class MainViewModel :
ObservableObject,
EventHandlerInterface<HeroCreatedEvent>,
EventHandlerInterface<HeroDeletedEvent>,
EventHandlerInterface<CreatureCreatedEvent>,
EventHandlerInterface<CreatureDeletedEvent>,
EventHandlerInterface<DropCreatedEvent>,
EventHandlerInterface<DropDeletedEvent>,
EventHandlerInterface<ChatMessageCreatedEvent>,
EventHandlerInterface<SkillCreatedEvent>,
EventHandlerInterface<SkillDeletedEvent>,
EventHandlerInterface<ItemCreatedEvent>,
EventHandlerInterface<ItemDeletedEvent>
{
public void Handle(HeroCreatedEvent @event)
{
Hero = new HeroSummaryInfoViewModel(@event.Hero);
hero = @event.Hero;
Map.Hero = hero;
Map.CombatZone = new AICombatZoneMapViewModel(aiConfig.Combat.Zone, hero);
AddCreature(hero);
OnPropertyChanged("Hero");
OnPropertyChanged("Map");
}
public void Handle(HeroDeletedEvent @event)
{
if (hero != null)
{
RemoveCreature(hero.Id);
}
Hero = null;
hero = null;
Map.Hero = null;
Map.CombatZone = null;
OnPropertyChanged("Hero");
OnPropertyChanged("Map");
}
public void Handle(CreatureCreatedEvent @event)
{
if (hero != null)
{
Creatures.Add(new CreatureListViewModel(worldHandler, pathMover, @event.Creature, hero));
AddCreature(@event.Creature);
}
}
public void Handle(CreatureDeletedEvent @event)
{
Creatures.RemoveAll(x => x.Id == @event.Id);
RemoveCreature(@event.Id);
}
public void Handle(DropCreatedEvent @event)
{
if (hero != null)
{
Drops.Add(new DropListViewModel(worldHandler, pathMover, @event.Drop, hero));
Map.Drops.Add(new DropMapViewModel(worldHandler, pathMover, @event.Drop, hero));
}
}
public void Handle(DropDeletedEvent @event)
{
Drops.RemoveAll(x => x.Id == @event.Id);
Map.Drops.RemoveAll(x => x.Id == @event.Id);
}
public void Handle(ChatMessageCreatedEvent @event)
{
ChatMessages.Add(new ChatMessageViewModel(@event.Message));
}
public void Handle(SkillCreatedEvent @event)
{
if (hero != null)
{
if (@event.Skill.IsActive)
{
ActiveSkills.Add(new SkillListViewModel(worldHandler, @event.Skill));
}
else
{
PassiveSkills.Add(new SkillListViewModel(worldHandler, @event.Skill));
}
}
}
public void Handle(SkillDeletedEvent @event)
{
ActiveSkills.RemoveAll(x => x.Id == @event.Id);
PassiveSkills.RemoveAll(x => x.Id == @event.Id);
}
public void Handle(ItemCreatedEvent @event)
{
if (hero != null)
{
if (@event.Item is EtcItem && ((EtcItem) @event.Item).IsQuest)
{
QuestItems.Add(new ItemListViewModel(worldHandler, @event.Item));
}
else
{
Items.Add(new ItemListViewModel(worldHandler, @event.Item));
}
}
}
public void Handle(ItemDeletedEvent @event)
{
Items.RemoveAll(x => x.Id == @event.Id);
QuestItems.RemoveAll(x => x.Id == @event.Id);
}
public Dictionary<TypeEnum, string> AITypes
{
get
{
return Enum.GetValues(typeof(TypeEnum)).Cast<TypeEnum>().ToDictionary(x => x, x => x.ToString());
}
}
private void AddCreature(CreatureInterface creature)
{
if (hero != null)
{
Map.Creatures.Add(new CreatureMapViewModel(worldHandler, pathMover, creature, hero));
}
}
private void RemoveCreature(uint id)
{
Map.Creatures.RemoveAll(x => x.Id == id);
}
private void OnToggleAI(object? sender)
{
ai.Toggle();
OnPropertyChanged("AIStatus");
}
private void OnChangeAIType(object? param)
{
if (!(param is TypeEnum))
{
return;
}
ai.Type = (TypeEnum) param;
OnPropertyChanged("AIType");
}
public MainViewModel(WorldHandler worldHandler, AsyncPathMoverInterface pathMover, AIInterface ai, Config aiConfig)
{
this.worldHandler = worldHandler;
this.pathMover = pathMover;
this.ai = ai;
this.aiConfig = aiConfig;
Map = new MapViewModel(pathMover);
ToggleAICommand = new RelayCommand(OnToggleAI);
ChangeAITypeCommand = new RelayCommand(OnChangeAIType);
}
public ICommand ToggleAICommand { get; set; }
public ICommand ChangeAITypeCommand { get; set; }
public ObservableCollection<ChatMessageViewModel> ChatMessages { get; } = new ObservableCollection<ChatMessageViewModel>();
public ObservableCollection<CreatureListViewModel> Creatures { get; } = new ObservableCollection<CreatureListViewModel>();
public ObservableCollection<DropListViewModel> Drops { get; } = new ObservableCollection<DropListViewModel>();
public ObservableCollection<SkillListViewModel> ActiveSkills { get; } = new ObservableCollection<SkillListViewModel>();
public ObservableCollection<SkillListViewModel> PassiveSkills { get; } = new ObservableCollection<SkillListViewModel>();
public ObservableCollection<ItemListViewModel> Items { get; } = new ObservableCollection<ItemListViewModel>();
public ObservableCollection<ItemListViewModel> QuestItems { get; } = new ObservableCollection<ItemListViewModel>();
public HeroSummaryInfoViewModel? Hero { get; private set; }
public MapViewModel Map { get; private set; }
public bool AIStatus => ai.IsEnabled;
public TypeEnum AIType => ai.Type;
public Hero? hero;
private readonly WorldHandler worldHandler;
private readonly AsyncPathMoverInterface pathMover;
private readonly AIInterface ai;
private readonly Config aiConfig;
}
}

View File

@@ -0,0 +1,58 @@
using Client.Domain.Common;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace Client.Application.ViewModels
{
public class MapBlockViewModel : ObservableObject
{
public string ImageSource => "/Assets/maps/" + mapBlock.BlockX + "_" + mapBlock.BlockY + ".jpg";
public float DeltaX => mapBlock.DeltaX;
public float DeltaY => mapBlock.DeltaY;
public float Size => mapBlock.Size;
public bool Visible
{
get => visible;
set
{
if (visible != value)
{
visible = value;
OnPropertyChanged();
}
}
}
public MapBlock MapBlock => mapBlock;
public MapBlockViewModel(MapBlock mapBlock)
{
this.mapBlock = mapBlock;
mapBlock.PropertyChanged += MapBlock_PropertyChanged;
}
private void MapBlock_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "DeltaX")
{
OnPropertyChanged("DeltaX");
}
if (e.PropertyName == "DeltaY")
{
OnPropertyChanged("DeltaY");
}
if (e.PropertyName == "Size")
{
OnPropertyChanged("Size");
}
}
private readonly MapBlock mapBlock;
private bool visible = true;
}
}

View File

@@ -0,0 +1,340 @@
using Client.Application.Commands;
using Client.Domain.Common;
using Client.Domain.DTO;
using Client.Domain.Entities;
using Client.Domain.Service;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
namespace Client.Application.ViewModels
{
public class MapViewModel : ObservableObject
{
public delegate void MapUpdatedEventHandler(float scale, float viewportWidth, float viewportHeight);
private event MapUpdatedEventHandler? MapUpdated;
public Hero? Hero
{
get => hero;
set
{
if (hero != value)
{
if (hero != null)
{
hero.Transform.Position.PropertyChanged -= HeroPosition_PropertyChanged;
}
hero = value;
if (hero != null)
{
hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged;
}
UpdateMap();
}
}
}
public double ViewportWidth
{
get => viewportWidth;
set
{
if (viewportWidth != value)
{
viewportWidth = value;
UpdateMap();
OnPropertyChanged("MousePosition");
}
}
}
public double ViewportHeight
{
get => viewportHeight;
set
{
if (viewportHeight != value)
{
viewportHeight = value;
UpdateMap();
OnPropertyChanged("MousePosition");
}
}
}
public float Scale
{
get => scale;
set
{
if (scale != value)
{
scale = value;
UpdateMap();
OnPropertyChanged();
OnPropertyChanged("MousePosition");
}
}
}
public Vector3 MousePosition
{
get => mousePosition;
set => mousePosition = value;
}
private void HeroPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
UpdateMap();
if (e.PropertyName == "X" || e.PropertyName == "Y")
{
OnPropertyChanged("MousePosition");
}
}
private void UpdateMap()
{
foreach (var block in Blocks)
{
block.Visible = false;
}
if (hero != null)
{
var blocks = selector.SelectImages((float)ViewportWidth, (float)ViewportHeight, hero.Transform.Position, Scale);
foreach (var block in blocks)
{
if (this.blocks.ContainsKey(block.Id))
{
this.blocks[block.Id].MapBlock.DeltaX = block.DeltaX;
this.blocks[block.Id].MapBlock.DeltaY = block.DeltaY;
this.blocks[block.Id].MapBlock.Size = block.Size;
}
else
{
var model = new MapBlockViewModel(block);
this.blocks.Add(block.Id, model);
Blocks.Add(model);
}
this.blocks[block.Id].Visible = true;
}
if (MapUpdated != null)
{
MapUpdated(scale, (float)ViewportWidth, (float)ViewportHeight);
}
}
}
public ICommand MouseLeftClickCommand { get; }
private async Task OnLeftMouseClick(object? obj)
{
if (obj == null)
{
return;
}
if (hero == null)
{
return;
}
Point mousePos = Mouse.GetPosition((IInputElement)obj);
var location = new Vector3(
(float)(mousePos.X - ViewportWidth / 2) * scale + hero.Transform.Position.X,
(float)(mousePos.Y - ViewportHeight / 2) * scale + hero.Transform.Position.Y,
hero.Transform.Position.Z
);
await pathMover.MoveUntilReachedAsync(location);
}
public void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
var newScale = Scale - e.Delta / Mouse.MouseWheelDeltaForOneLine;
Scale = MathF.Max(MathF.Min(newScale, MAX_SCALE), MIN_SCALE);
}
public void OnMouseLeave(object sender, MouseEventArgs e)
{
mousePosition.X = 0;
mousePosition.Y = 0;
}
public void OnMouseMove(object sender, MouseEventArgs e)
{
if (hero == null)
{
mousePosition.X = 0;
mousePosition.Y = 0;
return;
}
var el = (IInputElement)sender;
var mousePos = e.GetPosition(el);
mousePosition.X = (float)(mousePos.X - ViewportWidth / 2) * scale + hero.Transform.Position.X;
mousePosition.Y = (float)(mousePos.Y - ViewportHeight / 2) * scale + hero.Transform.Position.Y;
}
public MapViewModel(AsyncPathMoverInterface pathMover)
{
Creatures.CollectionChanged += Creatures_CollectionChanged;
Drops.CollectionChanged += Drops_CollectionChanged;
Path.CollectionChanged += Path_CollectionChanged;
MouseLeftClickCommand = new RelayCommand(async (o) => await OnLeftMouseClick(o));
mousePosition.PropertyChanged += MousePosition_PropertyChanged;
BindingOperations.EnableCollectionSynchronization(Path, pathCollectionLock);
this.pathMover = pathMover;
this.pathMover.Path.CollectionChanged += PathMover_Path_CollectionChanged;
}
private void PathMover_Path_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
lock(pathCollectionLock)
{
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
{
if (hero != null)
{
foreach (var item in e.NewItems)
{
var node = (PathSegment)item;
Path.Add(new PathNodeViewModel(node.From, node.To, hero));
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null)
{
foreach (var item in e.OldItems)
{
Path.RemoveAt(0);
}
}
else if (e.Action == NotifyCollectionChangedAction.Reset)
{
Path.RemoveAll();
}
}
}
private void Path_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
{
foreach (var item in e.NewItems)
{
var node = (PathNodeViewModel)item;
MapUpdated += node.MapUpdated;
node.MapUpdated(scale, (float)ViewportWidth, (float)ViewportHeight);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null)
{
foreach (var item in e.OldItems)
{
var node = (PathNodeViewModel)item;
MapUpdated -= node.MapUpdated;
}
}
}
private void MousePosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("MousePosition");
}
private void Drops_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
{
foreach (var item in e.NewItems)
{
var drop = (DropMapViewModel)item;
MapUpdated += drop.MapUpdated;
drop.MapUpdated(scale, (float)ViewportWidth, (float)ViewportHeight);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null)
{
foreach (var item in e.OldItems)
{
var drop = (DropMapViewModel)item;
MapUpdated -= drop.MapUpdated;
}
}
}
private void Creatures_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
{
foreach (var item in e.NewItems)
{
var creature = (CreatureMapViewModel)item;
MapUpdated += creature.MapUpdated;
creature.MapUpdated(scale, (float)ViewportWidth, (float)ViewportHeight);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null)
{
foreach (var item in e.OldItems)
{
var creature = (CreatureMapViewModel)item;
MapUpdated -= creature.MapUpdated;
}
}
}
public ObservableCollection<MapBlockViewModel> Blocks { get; } = new ObservableCollection<MapBlockViewModel>();
public ObservableCollection<CreatureMapViewModel> Creatures { get; } = new ObservableCollection<CreatureMapViewModel>();
public ObservableCollection<DropMapViewModel> Drops { get; } = new ObservableCollection<DropMapViewModel>();
public ObservableCollection<PathNodeViewModel> Path { get; } = new ObservableCollection<PathNodeViewModel>();
public AICombatZoneMapViewModel? CombatZone {
get
{
return combatZone;
}
set
{
if (value != combatZone)
{
if (value == null)
{
if (combatZone != null)
{
MapUpdated -= combatZone.MapUpdated;
}
}
else
{
MapUpdated += value.MapUpdated;
}
combatZone = value;
OnPropertyChanged();
}
}
}
public readonly static float MIN_SCALE = 1;
public readonly static float MAX_SCALE = 128;
private readonly AsyncPathMoverInterface pathMover;
private MapImageSelector selector = new MapImageSelector();
private Dictionary<uint, MapBlockViewModel> blocks = new Dictionary<uint, MapBlockViewModel>();
private Hero? hero;
private float scale = 8;
private double viewportWidth = 0;
private double viewportHeight = 0;
private Vector3 mousePosition = new Vector3(0, 0, 0);
private object pathCollectionLock = new object();
private AICombatZoneMapViewModel? combatZone = null;
}
}

View File

@@ -0,0 +1,86 @@
using Client.Application.Commands;
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.Service;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Client.Application.ViewModels
{
public class PathNodeViewModel : ObservableObject
{
public void MapUpdated(float scale, float viewportWidth, float viewportHeight)
{
Scale = scale;
VieportSize = new Vector3(viewportWidth, viewportHeight, 0);
}
public Vector3 From => new Vector3(
(from.X - hero.Transform.Position.X) / scale + (VieportSize.X / 2),
(from.Y - hero.Transform.Position.Y) / scale + (VieportSize.Y / 2),
0
);
public Vector3 To => new Vector3(
(to.X - hero.Transform.Position.X) / scale + (VieportSize.X / 2),
(to.Y - hero.Transform.Position.Y) / scale + (VieportSize.Y / 2),
0
);
public float Radius => MAX_RADIUS - (1 / MapViewModel.MIN_SCALE - 1 / scale) / (1 / MapViewModel.MIN_SCALE - 1 / MapViewModel.MAX_SCALE) * (MAX_RADIUS - MIN_RADIUS);
public float Scale
{
get => scale;
set
{
if (scale != value)
{
scale = value;
OnPropertyChanged("From");
OnPropertyChanged("To");
OnPropertyChanged("Radius");
}
}
}
public Vector3 VieportSize
{
get => vieportSize;
set
{
if (vieportSize != value)
{
vieportSize = value;
OnPropertyChanged("From");
OnPropertyChanged("To");
}
}
}
public PathNodeViewModel(Vector3 from, Vector3 to, Hero hero)
{
this.from = from;
this.to = to;
this.hero = hero;
hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged;
}
private void HeroPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("From");
OnPropertyChanged("To");
}
private readonly Vector3 from;
private readonly Vector3 to;
private readonly Hero hero;
private float scale = 1;
private static readonly float MAX_RADIUS = 10;
private static readonly float MIN_RADIUS = 2;
private Vector3 vieportSize = new Vector3(0, 0, 0);
}
}

View File

@@ -0,0 +1,78 @@
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.Service;
using System.Windows.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Client.Application.Commands;
namespace Client.Application.ViewModels
{
public class SkillListViewModel : ObservableObject
{
public uint Id => skill.Id;
public string Name => skill.Name;
public uint Level => skill.Level;
public string Description=> skill.Description;
public string IconName => "/Assets/icons/" + skill.IconName + ".png";
public bool IsActive => skill.IsActive;
public bool IsReadyToUse => skill.IsReadyToUse;
public bool IsBusy => !skill.IsReadyToUse;
public uint? Cost => skill.Cost == 0 ? null : skill.Cost;
public int? Range => skill.Range <= 0 ? null : skill.Range;
public ICommand MouseLeftClickCommand { get; }
private void OnLeftMouseClick(object? obj)
{
worldHandler.RequestUseSkill(Id, false, false);
}
public SkillListViewModel(WorldHandler worldHandler, Skill skill)
{
this.worldHandler = worldHandler;
this.skill = skill;
skill.PropertyChanged += Skill_PropertyChanged;
MouseLeftClickCommand = new RelayCommand(OnLeftMouseClick);
}
private void Skill_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Description")
{
OnPropertyChanged("Description");
}
if (e.PropertyName == "IsReadyToUse")
{
OnPropertyChanged("IsBusy");
OnPropertyChanged("IsReadyToUse");
}
if (e.PropertyName == "Level")
{
OnPropertyChanged("Level");
}
if (e.PropertyName == "Cost")
{
OnPropertyChanged("Cost");
}
if (e.PropertyName == "Range")
{
OnPropertyChanged("Range");
}
if (e.PropertyName == "Name")
{
OnPropertyChanged("Name");
}
if (e.PropertyName == "IconName")
{
OnPropertyChanged("IconName");
}
}
private readonly WorldHandler worldHandler;
private readonly Skill skill;
}
}

View File

@@ -0,0 +1,81 @@
using Client.Domain.Common;
using Client.Domain.Entities;
using Client.Domain.ValueObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Client.Application.ViewModels
{
public class TargetSummaryInfoViewModel : ObservableObject
{
public uint Id => creature.Id;
public string Name => creature.Name;
public Vector3 Position => creature.Transform.Position;
public string BriefInfo => creature.BriefInfo;
public float Distance => creature.Distance(hero);
public float DeltaZ => creature.DeltaZ(hero);
public uint Hp => creature.VitalStats.Hp;
public uint MaxHp => creature.VitalStats.MaxHp;
public TargetSummaryInfoViewModel(CreatureInterface creature, Hero hero)
{
creature.PropertyChanged += Creature_PropertyChanged;
creature.Transform.Position.PropertyChanged += Position_PropertyChanged;
creature.VitalStats.PropertyChanged += VitalStats_PropertyChanged;
hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged;
this.creature = creature;
this.hero = hero;
}
private void VitalStats_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Hp")
{
OnPropertyChanged("Hp");
}
if (e.PropertyName == "MaxHp")
{
OnPropertyChanged("MaxHp");
}
}
private void HeroPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Distance");
OnPropertyChanged("DeltaZ");
}
private void Position_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged("Distance");
OnPropertyChanged("DeltaZ");
OnPropertyChanged("Transform");
}
private void Creature_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
{
OnPropertyChanged("Name");
}
if (e.PropertyName == "BriefInfo")
{
OnPropertyChanged("BriefInfo");
}
}
private readonly CreatureInterface creature;
private readonly Hero hero;
}
}

View File

@@ -0,0 +1,373 @@
<Window x:Class="Client.Application.Views.AIConfig"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client.Application.Views"
xmlns:components="clr-namespace:Client.Application.Components"
mc:Ignorable="d"
IsVisibleChanged="Window_IsVisibleChanged"
Closing="Window_Closing"
SizeToContent="Height"
ResizeMode="NoResize"
Title="Combat AI Config" Height="600" Width="800"
x:Name="root">
<Grid Margin="0,0,0,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="File">
<MenuItem Header="Open" Command="{Binding OpenDialogCommand}"/>
<MenuItem Header="Save" Command="{Binding SaveDialogCommand}"/>
</MenuItem>
</Menu>
<TabControl Grid.Row="1" TabStripPlacement="Left">
<TabItem>
<TabItem.Header>Combat AI</TabItem.Header>
<TabItem.Content>
<TabControl>
<TabItem>
<TabItem.Header>Combat</TabItem.Header>
<TabItem.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" IsChecked="{Binding AutoUseShots}">Auto use soul and spiritshots</CheckBox>
<CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{Binding UseOnlySkills}">Use only skills</CheckBox>
<StackPanel Grid.Row="2" Grid.Column="0">
<Label>Attack distance for mili weapon:</Label>
<TextBox Width="100" HorizontalAlignment="Left">
<TextBox.Text>
<Binding Path="AttackDistanceMili" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
<StackPanel Grid.Row="3" Grid.Column="0">
<Label>Attack distance for bows:</Label>
<TextBox Width="100" HorizontalAlignment="Left">
<TextBox.Text>
<Binding Path="AttackDistanceBow" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
<StackPanel Grid.Row="4" Grid.Column="0">
<Label>Skill conditions:</Label>
<DataGrid
AutoGenerateColumns="False"
Height="150"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding CombatSkills}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Skill" Width="300">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Skills}"
SelectedValuePath="Id"
SelectedValue="{Binding Id}"
DisplayMemberPath="Name">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Max target % HP" Width="100" Binding="{Binding MaxTargetPercentHp}" />
<DataGridTextColumn Header="Min player % MP" Width="100" Binding="{Binding MinPlayerPercentMp}" />
<DataGridTextColumn Header="Max player % HP" Width="100" Binding="{Binding MaxPlayerPercentHp}" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<StackPanel Grid.Row="5" Grid.Column="0">
<Label>Combat zone:</Label>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="120"></ColumnDefinition>
<ColumnDefinition Width="80"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0">X:</Label>
<TextBox Width="100" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="1">
<TextBox.Text>
<Binding Path="Zone.X" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<Button Grid.Row="0" Grid.Column="2" Command="{Binding GetHeroPosition}">Current</Button>
<Label Grid.Row="1" Grid.Column="0">Y:</Label>
<TextBox Width="100" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="1">
<TextBox.Text>
<Binding Path="Zone.Y" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<Label Grid.Row="2" Grid.Column="0">Radius:</Label>
<TextBox Width="100" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="1">
<TextBox.Text>
<Binding Path="Zone.Radius" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</Grid>
</StackPanel>
</Grid>
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Mobs</TabItem.Header>
<TabItem.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<Label>Max delta z:</Label>
<TextBox Width="100" HorizontalAlignment="Left">
<TextBox.Text>
<Binding Path="MobsMaxDeltaZ" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
<components:MultipleObjectSelector
Grid.Row="1"
Grid.Column="0"
Source="{Binding ExcludedMobs}"
Target="{Binding SelectedExcludedMobs}"
Header="Excluded:"/>
<components:MultipleObjectSelector
Grid.Row="1"
Grid.Column="1"
Source="{Binding IncludedMobs}"
Target="{Binding SelectedIncludedMobs}"
Header="Included:"/>
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<Label>Mobs level range:</Label>
<StackPanel Orientation="Horizontal">
<TextBox Width="100">
<TextBox.Text>
<Binding Path="MobLevelLowerLimit" UpdateSourceTrigger="PropertyChanged" TargetNullValue=""/>
</TextBox.Text>
</TextBox>
<TextBlock>&lt;= Player level &lt;=</TextBlock>
<TextBox Width="100">
<TextBox.Text>
<Binding Path="MobLevelUpperLimit" UpdateSourceTrigger="PropertyChanged" TargetNullValue=""/>
</TextBox.Text>
</TextBox>
</StackPanel>
</StackPanel>
</Grid>
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Drop</TabItem.Header>
<TabItem.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" IsChecked="{Binding PickupIfPossible}">Pickup if possible</CheckBox>
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<Label>Max delta z:</Label>
<TextBox Width="100" HorizontalAlignment="Left">
<TextBox.Text>
<Binding Path="PickupMaxDeltaZ" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<Label>Pickup attempts count:</Label>
<TextBox Width="100" HorizontalAlignment="Left">
<TextBox.Text>
<Binding Path="PickupAttemptsCount" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
<components:MultipleObjectSelector
Grid.Row="3"
Grid.Column="0"
Source="{Binding ExcludedItems}"
Target="{Binding SelectedExcludedItems}"
Header="Excluded:"/>
<components:MultipleObjectSelector
Grid.Row="3"
Grid.Column="1"
Source="{Binding IncludedItems}"
Target="{Binding SelectedIncludedItems}"
Header="Included:"/>
</Grid>
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Spoil</TabItem.Header>
<TabItem.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" IsChecked="{Binding SpoilIfPossible}">Spoil if possible</CheckBox>
<CheckBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" IsChecked="{Binding SpoilIsPriority}">Spoil is priority</CheckBox>
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<Label>Sweep attempts count:</Label>
<TextBox Width="100" HorizontalAlignment="Left">
<TextBox.Text>
<Binding Path="SweepAttemptsCount" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
<components:MultipleObjectSelector
Grid.Row="3"
Grid.Column="0"
Source="{Binding ExcludedSpoilMobs}"
Target="{Binding SelectedExcludedSpoilMobs}"
Header="Excluded:"/>
<components:MultipleObjectSelector
Grid.Row="3"
Grid.Column="1"
Source="{Binding IncludedSpoilMobs}"
Target="{Binding SelectedIncludedSpoilMobs}"
Header="Included:"/>
<StackPanel Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2">
<components:ObjectSelector
Source="{Binding Skills}"
SelectedValue="{Binding SpoilSkillId, Mode=TwoWay}"
Header="Spoil skill:"/>
</StackPanel>
<StackPanel Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2">
<components:ObjectSelector
Source="{Binding Skills}"
SelectedValue="{Binding SweeperSkillId, Mode=TwoWay}"
Header="Sweeper skill:"/>
</StackPanel>
</Grid>
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Rest</TabItem.Header>
<TabItem.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Label>% HP range:</Label>
<StackPanel Orientation="Horizontal">
<TextBox Width="100">
<TextBox.Text>
<Binding Path="RestStartPercentHp" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<TextBlock>-</TextBlock>
<TextBox Width="100">
<TextBox.Text>
<Binding Path="RestEndPercentHp" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
</StackPanel>
<StackPanel Grid.Row="1">
<Label>% MP range:</Label>
<StackPanel Orientation="Horizontal">
<TextBox Width="100">
<TextBox.Text>
<Binding Path="RestStartPercentMp" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<TextBlock>-</TextBlock>
<TextBox Width="100">
<TextBox.Text>
<Binding Path="RestEndPercentMp" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
</StackPanel>
</Grid>
</TabItem.Content>
</TabItem>
</TabControl>
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Deleveling AI</TabItem.Header>
<TabItem.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0">
<Label>Target level:</Label>
<TextBox Width="100" HorizontalAlignment="Left">
<TextBox.Text>
<Binding Path="DelevelingTargetLevel" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0">
<Label>Attack distance:</Label>
<TextBox Width="100" HorizontalAlignment="Left">
<TextBox.Text>
<Binding Path="DelevelingAttackDistance" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="0">
<components:ObjectSelector
Source="{Binding Skills}"
SelectedValue="{Binding DelevelingSkillId, Mode=TwoWay}"
Header="Attacking skill:"/>
</StackPanel>
</Grid>
</TabItem.Content>
</TabItem>
</TabControl>
<StackPanel Orientation="Horizontal" Grid.Row="2" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0,10,10,0">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="8,0,0,0"/>
</Style>
</StackPanel.Resources>
<Button Command="{Binding SaveCommand}">Save</Button>
<Button Command="{Binding ResetCommand}">Reset</Button>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,83 @@
using Client.Application.Commands;
using Client.Application.ViewModels;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Client.Application.Views
{
/// <summary>
/// Interaction logic for CombatAIConfigView.xaml
/// </summary>
public partial class AIConfig : Window
{
private AIConfigViewModel viewModel;
public AIConfig(AIConfigViewModel viewModel)
{
InitializeComponent();
this.viewModel = viewModel;
DataContext = viewModel;
if (viewModel.Close == null)
{
viewModel.Close = () => Close();
}
if (viewModel.OpenSaveDialog == null)
{
viewModel.OpenSaveDialog = (string data) =>
{
var dialog = new SaveFileDialog()
{
Filter = "Json files(*.json)|*.json"
};
if (dialog.ShowDialog() == true)
{
File.WriteAllText(dialog.FileName, data);
}
};
}
if (viewModel.OpenOpenDialog == null)
{
viewModel.OpenOpenDialog = () =>
{
var dialog = new OpenFileDialog()
{
Filter = "Json files(*.json)|*.json"
};
if (dialog.ShowDialog() == true)
{
return File.ReadAllText(dialog.FileName);
}
return null;
};
}
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
Hide();
}
private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool) e.NewValue)
{
viewModel.LoadConfig();
}
}
}
}

View File

@@ -0,0 +1,23 @@
<ItemsControl x:Class="Client.Application.Views.ItemPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client.Application.Views"
xmlns:components="clr-namespace:Client.Application.Components"
mc:Ignorable="d">
<ItemsControl.ItemTemplate>
<DataTemplate>
<components:Item
ImageSource="{Binding Path=IconName,Mode=OneWay}"
ItemName="{Binding Path=Name,Mode=OneWay}"
Description="{Binding Path=Description,Mode=OneWay}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Client.Application.Views
{
/// <summary>
/// Interaction logic for ItemPanel.xaml
/// </summary>
public partial class ItemPanel : ItemsControl
{
public ItemPanel()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,303 @@
<Window x:Class="Client.Application.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:services="clr-namespace:Client.Application.Services"
xmlns:components="clr-namespace:Client.Application.Components"
xmlns:views="clr-namespace:Client.Application.Views"
mc:Ignorable="d"
Title="L2Bot 2.0 Client" Height="600" Width="1024">
<Window.Resources>
<CollectionViewSource x:Key="SortedCreatures" Source="{Binding Creatures, Mode=OneWay}" IsLiveSortingRequested="True">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Distance" Direction="Ascending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<CollectionViewSource x:Key="SortedDrops" Source="{Binding Drops, Mode=OneWay}" IsLiveSortingRequested="True">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Distance" Direction="Ascending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<services:NullToVisibilityConverter x:Key="NullToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="444"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="22"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="240"></RowDefinition>
</Grid.RowDefinitions>
<Menu Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<MenuItem Header="AI options">
<MenuItem Command="{Binding ToggleAICommand}">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="Start"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding AIStatus, Mode=OneWay}" Value="True">
<Setter Property="Header" Value="Stop"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
<MenuItem ItemsSource="{Binding AITypes}" Header="AI type">
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding Value}"/>
<Setter Property="MenuItem.Command" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ChangeAITypeCommand}"/>
<Setter Property="MenuItem.CommandParameter" Value="{Binding Key}"/>
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding>
<MultiBinding.Converter>
<services:EqualityConverter />
</MultiBinding.Converter>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" Path="DataContext.AIType" />
<Binding Path="Key" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="MenuItem.IsChecked" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="Config" Click="AIConfig_Click"/>
</MenuItem>
</Menu>
<ListBox x:Name="listBox" Grid.Row="2" Grid.Column="0" ItemsSource="{Binding ChatMessages, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Text, Mode=OneWay}" Foreground="{Binding Path=Color, Mode=OneWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<components:Map Grid.Column="0" Grid.Row="1" DataContext="{Binding Map}" />
<TabControl Grid.Row="1" Grid.Column="1">
<TabItem>
<TabItem.Header>Environment</TabItem.Header>
<TabItem.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition Height="1*"></RowDefinition>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Source={StaticResource SortedCreatures}, Mode=OneWay}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="Item">
<StackPanel Margin="5">
<StackPanel.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding MouseLeftClickCommand}" />
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding MouseLeftDoubleClickCommand}" />
<MouseBinding Gesture="RightClick" Command="{Binding MouseRightClickCommand}" />
</StackPanel.InputBindings>
<TextBlock FontSize="16" Text="{Binding Path=Name,Mode=OneWay}" />
<TextBlock FontSize="11">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}; distance: {1:F0}; delta z: {2:F0}">
<Binding Path="BriefInfo" Mode="OneWay"/>
<Binding Path="Distance" Mode="OneWay"/>
<Binding Path="DeltaZ" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsTarget,Mode=OneWay}" Value="True">
<Setter TargetName="Item" Property="Background" Value="#330000ff" />
</DataTrigger>
<DataTrigger Binding="{Binding IsAttacker,Mode=OneWay}" Value="True">
<Setter TargetName="Item" Property="Background" Value="#33ff0000" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox ItemsSource="{Binding Source={StaticResource SortedDrops}, Mode=OneWay}" Grid.Row="1" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<DockPanel.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding MouseLeftClickCommand}" />
<MouseBinding Gesture="RightClick" Command="{Binding MouseRightClickCommand}" />
</DockPanel.InputBindings>
<Image DockPanel.Dock="Left" Source="{Binding Path=IconName, FallbackValue=./Assets/icons/_fallback.png, Mode=OneWay}" Height="32" Width="32" />
<StackPanel Margin="5">
<TextBlock FontSize="16">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}[{1}]">
<Binding Path="Name" Mode="OneWay"/>
<Binding Path="ItemId" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock FontSize="11">
<TextBlock.Text>
<MultiBinding StringFormat="{}Count {0}; distance: {1:F0}; delta z: {2:F0}">
<Binding Path="Amount" Mode="OneWay"/>
<Binding Path="Distance" Mode="OneWay"/>
<Binding Path="DeltaZ" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Skills</TabItem.Header>
<TabItem.Content>
<TabControl>
<TabItem>
<TabItem.Header>Active</TabItem.Header>
<TabItem.Content>
<views:SkillPanel ItemsSource="{Binding ActiveSkills,Mode=OneWay}" />
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Passive</TabItem.Header>
<TabItem.Content>
<views:SkillPanel ItemsSource="{Binding PassiveSkills,Mode=OneWay}" />
</TabItem.Content>
</TabItem>
</TabControl>
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Inventory</TabItem.Header>
<TabItem.Content>
<TabControl>
<TabItem>
<TabItem.Header>Items</TabItem.Header>
<TabItem.Content>
<views:ItemPanel ItemsSource="{Binding Items,Mode=OneWay}" />
</TabItem.Content>
</TabItem>
<TabItem>
<TabItem.Header>Quest Items</TabItem.Header>
<TabItem.Content>
<views:ItemPanel ItemsSource="{Binding QuestItems,Mode=OneWay}" />
</TabItem.Content>
</TabItem>
</TabControl>
</TabItem.Content>
</TabItem>
</TabControl>
<Grid Grid.Row="2" Grid.Column="1" DataContext="{Binding Hero, Mode=OneWay}" Margin="4" Visibility="{Binding Path=.,Converter={StaticResource NullToVisibilityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Margin="4">
<TextBlock FontSize="16" Text="{Binding Path=Fullname.Nickname, Mode=OneWay}"></TextBlock>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1} {2}lvl">
<Binding Path="Phenotype.Race" Mode="OneWay"/>
<Binding Path="Phenotype.Class" Mode="OneWay"/>
<Binding Path="Experience.Level" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
<views:StatsPanel DataContext="{Binding ., Mode=OneWay}" Margin="4" Grid.Row="1" />
<DockPanel Grid.Row="2" Margin="4">
<StackPanel DockPanel.Dock="Left" Margin="0 0 5 0">
<TextBlock Padding="0 0 0 3">Position:</TextBlock>
<TextBlock Padding="0 0 0 3">Exp:</TextBlock>
<TextBlock Padding="0 0 0 3">Weight:</TextBlock>
<TextBlock Padding="0 0 0 3">Adena:</TextBlock>
<TextBlock Padding="0 0 0 3">Inv. slots:</TextBlock>
</StackPanel>
<StackPanel>
<TextBlock Padding="0 0 0 3">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0:F0}, {1:F0}, {2:F0}">
<Binding Path="Transform.Position.X" Mode="OneWay"/>
<Binding Path="Transform.Position.Y" Mode="OneWay"/>
<Binding Path="Transform.Position.Z" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Padding="0 0 0 3">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}/{1}">
<Binding Path="Experience.Exp" Mode="OneWay"/>
<Binding Path="Experience.ExpToLevel" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Padding="0 0 0 3">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}/{1}">
<Binding Path="InventoryInfo.Weight" Mode="OneWay"/>
<Binding Path="InventoryInfo.MaxWeight" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{Binding Path=Money, Mode=OneWay}" Padding="0 0 0 3"></TextBlock>
<TextBlock Padding="0 0 0 3">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}/{1}">
<Binding Path="InventoryInfo.Slots" Mode="OneWay"/>
<Binding Path="InventoryInfo.Slots" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DockPanel>
<StackPanel Grid.Column="1" DataContext="{Binding Target, Mode=OneWay}" Visibility="{Binding Path=.,Converter={StaticResource NullToVisibilityConverter}}" Margin="4">
<TextBlock FontSize="16" Text="{Binding Path=Name, Mode=OneWay}"></TextBlock>
<TextBlock Text="{Binding Path=BriefInfo, Mode=OneWay}"></TextBlock>
</StackPanel>
<StackPanel Margin="4" Grid.Column="1" Grid.Row="1" DataContext="{Binding Target, Mode=OneWay}" Visibility="{Binding Path=.,Converter={StaticResource NullToVisibilityConverter}}">
<components:StatsBar
BackgroundSource="/Assets/icons/ps_hpbar_back.png"
ForegroundSource="/Assets/icons/ps_hpbar.png"
Current="{Binding Path=Hp,Mode=OneWay}"
Total="{Binding Path=MaxHp,Mode=OneWay}"
Height="15"
/>
</StackPanel>
<DockPanel Grid.Column="1" Grid.Row="2" Margin="4" DataContext="{Binding Target, Mode=OneWay}" Visibility="{Binding Path=.,Converter={StaticResource NullToVisibilityConverter}}">
<StackPanel DockPanel.Dock="Left" Margin="0 0 5 0">
<TextBlock Padding="0 0 0 3">Position:</TextBlock>
<TextBlock Padding="0 0 0 3">Distance:</TextBlock>
<TextBlock Padding="0 0 0 3">Delta Z:</TextBlock>
</StackPanel>
<StackPanel>
<TextBlock Padding="0 0 0 3">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0:F0}, {1:F0}, {2:F0}">
<Binding Path="Position.X" Mode="OneWay"/>
<Binding Path="Position.Y" Mode="OneWay"/>
<Binding Path="Position.Z" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Padding="0 0 0 3" Text="{Binding Distance,Mode=OneWay,StringFormat='{}{0:F0}'}"></TextBlock>
<TextBlock Padding="0 0 0 3" Text="{Binding DeltaZ,Mode=OneWay,StringFormat='{}{0:F0}'}"></TextBlock>
</StackPanel>
</DockPanel>
</Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,47 @@
using Client.Application.Components;
using Client.Application.ViewModels;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Client.Application.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly MainViewModel mainViewModel;
private readonly AIConfigViewModel aiConfigViewModel;
private AIConfig aiConfigView;
public MainWindow(MainViewModel mainViewModel, AIConfigViewModel aiConfigViewModel)
{
InitializeComponent();
DataContext = mainViewModel;
this.mainViewModel = mainViewModel;
this.aiConfigViewModel = aiConfigViewModel;
aiConfigView = new AIConfig(aiConfigViewModel);
}
private void AIConfig_Click(object sender, RoutedEventArgs e)
{
aiConfigView.Owner = this;
aiConfigView.ShowDialog();
}
}
}

View File

@@ -0,0 +1,28 @@
<ItemsControl x:Class="Client.Application.Views.SkillPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Client.Application.Views"
xmlns:components="clr-namespace:Client.Application.Components"
mc:Ignorable="d">
<ItemsControl.ItemTemplate>
<DataTemplate>
<components:Skill
ImageSource="{Binding Path=IconName,Mode=OneWay}"
IsReadyToUse="{Binding Path=IsReadyToUse,Mode=OneWay}"
IsActive="{Binding Path=IsActive,Mode=OneWay}"
SkillName="{Binding Path=Name,Mode=OneWay}"
Level="{Binding Path=Level,Mode=OneWay}"
Cost="{Binding Path=Cost,Mode=OneWay}"
Range="{Binding Path=Range,Mode=OneWay}"
Description="{Binding Path=Description,Mode=OneWay}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Client.Application.Views
{
/// <summary>
/// Interaction logic for SkillPanel.xaml
/// </summary>
public partial class SkillPanel : ItemsControl
{
public SkillPanel()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,40 @@
<StackPanel x:Class="Client.Application.Views.StatsPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:components="clr-namespace:Client.Application.Components"
mc:Ignorable="d">
<components:StatsBar
BackgroundSource="/Assets/icons/ps_cpbar_back.png"
ForegroundSource="/Assets/icons/ps_cpbar.png"
Current="{Binding VitalStats.Cp}"
Total="{Binding VitalStats.MaxCp}"
Height="15"
Margin="0 0 0 2"
/>
<components:StatsBar
BackgroundSource="/Assets/icons/ps_hpbar_back.png"
ForegroundSource="/Assets/icons/ps_hpbar.png"
Current="{Binding VitalStats.Hp}"
Total="{Binding VitalStats.MaxHp}"
Height="15"
Margin="0 0 0 2"
/>
<components:StatsBar
BackgroundSource="/Assets/icons/ps_mpbar_back.png"
ForegroundSource="/Assets/icons/ps_mpbar.png"
Current="{Binding VitalStats.Mp}"
Total="{Binding VitalStats.MaxMp}"
Height="15"
Margin="0 0 0 2"
/>
<components:StatsBar
BackgroundSource="/Assets/icons/ps_expbar_back.png"
ForegroundSource="/Assets/icons/ps_expbar.png"
Current="{Binding Experience.ExpPercent}"
Format="{}{0:F2}%"
Height="15"
Margin="0 0 0 2"
/>
</StackPanel>

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Client.Application.Views
{
/// <summary>
/// Interaction logic for StatsPanel.xaml
/// </summary>
public partial class StatsPanel : StackPanel
{
public StatsPanel()
{
InitializeComponent();
}
}
}

10
Client/AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@@ -0,0 +1,352 @@
{
"experience": [
{
"level": "1",
"tolevel": "0"
},
{
"level": "2",
"tolevel": "68"
},
{
"level": "3",
"tolevel": "363"
},
{
"level": "4",
"tolevel": "1168"
},
{
"level": "5",
"tolevel": "2884"
},
{
"level": "6",
"tolevel": "6038"
},
{
"level": "7",
"tolevel": "11287"
},
{
"level": "8",
"tolevel": "19423"
},
{
"level": "9",
"tolevel": "31378"
},
{
"level": "10",
"tolevel": "48229"
},
{
"level": "11",
"tolevel": "71201"
},
{
"level": "12",
"tolevel": "101676"
},
{
"level": "13",
"tolevel": "141192"
},
{
"level": "14",
"tolevel": "191452"
},
{
"level": "15",
"tolevel": "254327"
},
{
"level": "16",
"tolevel": "331864"
},
{
"level": "17",
"tolevel": "426284"
},
{
"level": "18",
"tolevel": "539995"
},
{
"level": "19",
"tolevel": "675590"
},
{
"level": "20",
"tolevel": "835854"
},
{
"level": "21",
"tolevel": "1023775"
},
{
"level": "22",
"tolevel": "1242536"
},
{
"level": "23",
"tolevel": "1495531"
},
{
"level": "24",
"tolevel": "1786365"
},
{
"level": "25",
"tolevel": "2118860"
},
{
"level": "26",
"tolevel": "2497059"
},
{
"level": "27",
"tolevel": "2925229"
},
{
"level": "28",
"tolevel": "3407873"
},
{
"level": "29",
"tolevel": "3949727"
},
{
"level": "30",
"tolevel": "4555766"
},
{
"level": "31",
"tolevel": "5231213"
},
{
"level": "32",
"tolevel": "5981539"
},
{
"level": "33",
"tolevel": "6812472"
},
{
"level": "34",
"tolevel": "7729999"
},
{
"level": "35",
"tolevel": "8740372"
},
{
"level": "36",
"tolevel": "9850111"
},
{
"level": "37",
"tolevel": "11066012"
},
{
"level": "38",
"tolevel": "12395149"
},
{
"level": "39",
"tolevel": "13844879"
},
{
"level": "40",
"tolevel": "15422851"
},
{
"level": "41",
"tolevel": "17137002"
},
{
"level": "42",
"tolevel": "18995573"
},
{
"level": "43",
"tolevel": "21007103"
},
{
"level": "44",
"tolevel": "23180442"
},
{
"level": "45",
"tolevel": "25524751"
},
{
"level": "46",
"tolevel": "28049509"
},
{
"level": "47",
"tolevel": "30764519"
},
{
"level": "48",
"tolevel": "33679907"
},
{
"level": "49",
"tolevel": "36806133"
},
{
"level": "50",
"tolevel": "40153995"
},
{
"level": "51",
"tolevel": "45524865"
},
{
"level": "52",
"tolevel": "51262204"
},
{
"level": "53",
"tolevel": "57383682"
},
{
"level": "54",
"tolevel": "63907585"
},
{
"level": "55",
"tolevel": "70852742"
},
{
"level": "56",
"tolevel": "80700339"
},
{
"level": "57",
"tolevel": "91162131"
},
{
"level": "58",
"tolevel": "102265326"
},
{
"level": "59",
"tolevel": "114038008"
},
{
"level": "60",
"tolevel": "126509030"
},
{
"level": "61",
"tolevel": "146307211"
},
{
"level": "62",
"tolevel": "167243291"
},
{
"level": "63",
"tolevel": "189363788"
},
{
"level": "64",
"tolevel": "212716741"
},
{
"level": "65",
"tolevel": "237351413"
},
{
"level": "66",
"tolevel": "271973532"
},
{
"level": "67",
"tolevel": "308441375"
},
{
"level": "68",
"tolevel": "346825235"
},
{
"level": "69",
"tolevel": "387197529"
},
{
"level": "70",
"tolevel": "429632402"
},
{
"level": "71",
"tolevel": "474205751"
},
{
"level": "72",
"tolevel": "532692055"
},
{
"level": "73",
"tolevel": "606319094"
},
{
"level": "74",
"tolevel": "696376867"
},
{
"level": "75",
"tolevel": "804219972"
},
{
"level": "76",
"tolevel": "931275828"
},
{
"level": "77",
"tolevel": "1151275834"
},
{
"level": "78",
"tolevel": "1511275834"
},
{
"level": "79",
"tolevel": "2099275834"
},
{
"level": "80",
"tolevel": "4200000000"
},
{
"level": "81",
"tolevel": "6300000000"
},
{
"level": "82",
"tolevel": "8820000000"
},
{
"level": "83",
"tolevel": "11844000000"
},
{
"level": "84",
"tolevel": "15472800000"
},
{
"level": "85",
"tolevel": "19827360000"
},
{
"level": "86",
"tolevel": "25314105600"
},
{
"level": "87",
"tolevel": "32211728640"
}
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 986 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 986 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 B

Some files were not shown because too many files have changed in this diff Show More