diff --git a/Client/App.xaml.cs b/Client/App.xaml.cs index 77d75ba..e003cfb 100644 --- a/Client/App.xaml.cs +++ b/Client/App.xaml.cs @@ -78,6 +78,7 @@ namespace Client ) .AddSingleton(typeof(EntityHandlerFactoryInterface), typeof(EntityHandlerFactory)) .AddSingleton(typeof(MessageParserInterface), typeof(JsonMessageParser)) + .AddSingleton(typeof(OutgoingMessageBuilderInterface), typeof(JsonOutgoingMessageBuilder)) .AddSingleton( typeof(TransportInterface), x => new NamedPipeTransport( @@ -103,6 +104,7 @@ namespace Client .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton(); } diff --git a/Client/Application/Commands/RelayCommand.cs b/Client/Application/Commands/RelayCommand.cs new file mode 100644 index 0000000..1c90020 --- /dev/null +++ b/Client/Application/Commands/RelayCommand.cs @@ -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 execute; + private Func? canExecute; + + public event EventHandler? CanExecuteChanged + { + add { CommandManager.RequerySuggested += value; } + remove { CommandManager.RequerySuggested -= value; } + } + + public RelayCommand(Action execute, Func? 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); + } + } +} diff --git a/Client/Application/Components/Map.xaml b/Client/Application/Components/Map.xaml index 9ebf28c..042ea32 100644 --- a/Client/Application/Components/Map.xaml +++ b/Client/Application/Components/Map.xaml @@ -3,7 +3,6 @@ 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" xmlns:components="clr-namespace:Client.Application.Components" mc:Ignorable="d" @@ -11,7 +10,10 @@ services:SizeObserver.ObservedWidth="{Binding ViewportWidth, Mode=OneWayToSource}" services:SizeObserver.ObservedHeight="{Binding ViewportHeight, Mode=OneWayToSource}" > - + + + + @@ -55,21 +57,46 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -125,9 +152,9 @@ - - - + + + @@ -141,17 +168,52 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Client/Application/Components/Skill.xaml b/Client/Application/Components/Skill.xaml index ea1aa77..be0ee38 100644 --- a/Client/Application/Components/Skill.xaml +++ b/Client/Application/Components/Skill.xaml @@ -8,7 +8,8 @@ mc:Ignorable="d" IsEnabled="{Binding Path=IsButtonActive,ElementName=root,Mode=OneWay}" ToolTipService.ShowOnDisabled="True" - x:Name="root"> + x:Name="root" + Command="{Binding MouseLeftClickCommand}"> diff --git a/Client/Application/ViewModels/CreatureListViewModel.cs b/Client/Application/ViewModels/CreatureListViewModel.cs index 0872eb7..589d603 100644 --- a/Client/Application/ViewModels/CreatureListViewModel.cs +++ b/Client/Application/ViewModels/CreatureListViewModel.cs @@ -1,11 +1,14 @@ -using Client.Domain.Common; +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 { @@ -21,14 +24,36 @@ namespace Client.Application.ViewModels public float DeltaZ => creature.DeltaZ(hero); - public CreatureListViewModel(CreatureInterface creature, Hero hero) + 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 void OnMouseRightClick(object? obj) + { + worldHandler.RequestMoveToEntity(Id); + } + + public CreatureListViewModel(CreatureInterface creature, Hero hero, WorldHandler worldHandler) { creature.PropertyChanged += Creature_PropertyChanged; creature.Transform.Position.PropertyChanged += Position_PropertyChanged; hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged; + MouseLeftClickCommand = new RelayCommand(OnMouseLeftClick); + MouseLeftDoubleClickCommand = new RelayCommand(OnMouseLeftDoubleClick); + MouseRightClickCommand = new RelayCommand(OnMouseRightClick); this.creature = creature; this.hero = hero; + this.worldHandler = worldHandler; } private void HeroPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) @@ -57,5 +82,6 @@ namespace Client.Application.ViewModels private readonly CreatureInterface creature; private readonly Hero hero; + private readonly WorldHandler worldHandler; } } diff --git a/Client/Application/ViewModels/CreatureMapViewModel.cs b/Client/Application/ViewModels/CreatureMapViewModel.cs index 37e34b2..39323b4 100644 --- a/Client/Application/ViewModels/CreatureMapViewModel.cs +++ b/Client/Application/ViewModels/CreatureMapViewModel.cs @@ -1,12 +1,17 @@ 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 { @@ -56,20 +61,41 @@ namespace Client.Application.ViewModels } public CreatureTypeEnum Type => creature.Type; public bool IsTarget => Id == hero.TargetId; - public bool IsAggressive => creature.AggroRadius > 0; + public bool IsAggressive => creature.AggroRadius > 0 && !creature.VitalStats.IsDead && creature.IsHostile; public float AggroRadius => creature.AggroRadius / scale; + public ICommand MouseLeftClickCommand { get; } + public ICommand MouseLeftDoubleClickCommand { get; } + public ICommand MouseRightClickCommand { get; } + private void OnMouseLeftClick(object? obj) + { + worldHandler.RequestAcquireTarget(Id); + } - public CreatureMapViewModel(CreatureInterface creature, Hero hero) + private void OnMouseLeftDoubleClick(object? obj) + { + worldHandler.RequestAttackOrFollow(Id); + } + + private void OnMouseRightClick(object? obj) + { + worldHandler.RequestMoveToEntity(Id); + } + + public CreatureMapViewModel(CreatureInterface creature, Hero hero, WorldHandler worldHandler) { this.creature = creature; this.hero = hero; + this.worldHandler = worldHandler; 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(OnMouseRightClick); } private void VitalStats_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) @@ -78,6 +104,10 @@ namespace Client.Application.ViewModels { OnPropertyChanged("VitalStats"); } + if (e.PropertyName == "IsDead") + { + OnPropertyChanged("IsAggressive"); + } } private void Hero_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) @@ -118,6 +148,7 @@ namespace Client.Application.ViewModels private readonly CreatureInterface creature; private readonly Hero hero; + private readonly WorldHandler worldHandler; private float scale = 1; private static readonly float MAX_RADIUS = 10; private static readonly float MIN_RADIUS = 4; diff --git a/Client/Application/ViewModels/DropListViewModel.cs b/Client/Application/ViewModels/DropListViewModel.cs index 26a8293..972a5b0 100644 --- a/Client/Application/ViewModels/DropListViewModel.cs +++ b/Client/Application/ViewModels/DropListViewModel.cs @@ -1,10 +1,13 @@ -using Client.Domain.Common; +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 { @@ -46,14 +49,14 @@ namespace Client.Application.ViewModels { get { - return drop.Transform.Position.HorizontalDistance(hero.Transform.Position) / 100; + return drop.Transform.Position.HorizontalDistance(hero.Transform.Position); } } public float DeltaZ { get { - return (drop.Transform.Position.Z - hero.Transform.Position.Z) / 100; + return (drop.Transform.Position.Z - hero.Transform.Position.Z); } } @@ -65,14 +68,27 @@ namespace Client.Application.ViewModels } } - public DropListViewModel(Drop drop, Hero hero) + public ICommand MouseLeftClickCommand { get; } + public ICommand MouseRightClickCommand { get; } + private void OnMouseLeftClick(object? obj) + { + worldHandler.RequestPickUp(Id); + } + private void OnMouseRightClick(object? obj) + { + worldHandler.RequestMoveToEntity(Id); + } + + public DropListViewModel(Drop drop, Hero hero, WorldHandler worldHandler) { this.drop = drop; this.hero = hero; - + this.worldHandler = worldHandler; drop.PropertyChanged += Drop_PropertyChanged; drop.Transform.Position.PropertyChanged += DropPosition_PropertyChanged; hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged; + MouseLeftClickCommand = new RelayCommand(OnMouseLeftClick); + MouseRightClickCommand = new RelayCommand(OnMouseRightClick); } private void HeroPosition_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) @@ -109,5 +125,6 @@ namespace Client.Application.ViewModels private readonly Drop drop; private readonly Hero hero; + private readonly WorldHandler worldHandler; } } diff --git a/Client/Application/ViewModels/DropMapViewModel.cs b/Client/Application/ViewModels/DropMapViewModel.cs index 16a06b4..d70426f 100644 --- a/Client/Application/ViewModels/DropMapViewModel.cs +++ b/Client/Application/ViewModels/DropMapViewModel.cs @@ -8,13 +8,16 @@ 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 uint Id => drop.Id; - public string Name => drop.Name; + 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), @@ -47,13 +50,27 @@ namespace Client.Application.ViewModels } } - public DropMapViewModel(Drop drop, Hero hero) + public ICommand MouseLeftClickCommand { get; } + public ICommand MouseRightClickCommand { get; } + private void OnMouseLeftClick(object? obj) + { + worldHandler.RequestPickUp(Id); + } + private void OnMouseRightClick(object? obj) + { + worldHandler.RequestMoveToEntity(Id); + } + + public DropMapViewModel(Drop drop, Hero hero, WorldHandler worldHandler) { this.drop = drop; this.hero = hero; + this.worldHandler = worldHandler; drop.PropertyChanged += Creature_PropertyChanged; drop.Transform.Position.PropertyChanged += Position_PropertyChanged; hero.Transform.Position.PropertyChanged += HeroPosition_PropertyChanged; + MouseLeftClickCommand = new RelayCommand(OnMouseLeftClick); + MouseRightClickCommand = new RelayCommand(OnMouseRightClick); } private void HeroPosition_PropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -68,7 +85,7 @@ namespace Client.Application.ViewModels private void Creature_PropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == "Name") + if (e.PropertyName == "Name" || e.PropertyName == "Amount") { OnPropertyChanged("Name"); } @@ -76,6 +93,7 @@ namespace Client.Application.ViewModels private readonly Drop drop; private readonly Hero hero; + private readonly WorldHandler worldHandler; private float scale = 1; private static readonly float MAX_RADIUS = 8; private static readonly float MIN_RADIUS = 2; diff --git a/Client/Application/ViewModels/MainViewModel.cs b/Client/Application/ViewModels/MainViewModel.cs index 7f14ff0..7ee0ee9 100644 --- a/Client/Application/ViewModels/MainViewModel.cs +++ b/Client/Application/ViewModels/MainViewModel.cs @@ -1,6 +1,8 @@ -using Client.Domain.Common; +using Client.Application.Components; +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; @@ -57,7 +59,7 @@ namespace Client.Application.ViewModels { if (hero != null) { - Creatures.Add(new CreatureListViewModel(@event.Creature, hero)); + Creatures.Add(new CreatureListViewModel(@event.Creature, hero, worldHandler)); AddCreature(@event.Creature); } } @@ -72,8 +74,8 @@ namespace Client.Application.ViewModels { if (hero != null) { - Drops.Add(new DropListViewModel(@event.Drop, hero)); - Map.Drops.Add(new DropMapViewModel(@event.Drop, hero)); + Drops.Add(new DropListViewModel(@event.Drop, hero, worldHandler)); + Map.Drops.Add(new DropMapViewModel(@event.Drop, hero, worldHandler)); } } @@ -94,11 +96,11 @@ namespace Client.Application.ViewModels { if (@event.Skill.IsActive) { - ActiveSkills.Add(new SkillListViewModel(@event.Skill)); + ActiveSkills.Add(new SkillListViewModel(worldHandler, @event.Skill)); } else { - PassiveSkills.Add(new SkillListViewModel(@event.Skill)); + PassiveSkills.Add(new SkillListViewModel(worldHandler, @event.Skill)); } } } @@ -126,7 +128,7 @@ namespace Client.Application.ViewModels { if (hero != null) { - Map.Creatures.Add(new CreatureMapViewModel(creature, hero)); + Map.Creatures.Add(new CreatureMapViewModel(creature, hero, worldHandler)); } } @@ -135,6 +137,12 @@ namespace Client.Application.ViewModels Map.Creatures.RemoveAll(x => x.Id == id); } + public MainViewModel(WorldHandler worldHandler) + { + this.worldHandler = worldHandler; + Map = new MapViewModel(worldHandler); + } + public ObservableCollection ChatMessages { get; } = new ObservableCollection(); public ObservableCollection Creatures { get; } = new ObservableCollection(); public ObservableCollection Drops { get; } = new ObservableCollection(); @@ -142,7 +150,8 @@ namespace Client.Application.ViewModels public ObservableCollection PassiveSkills { get; } = new ObservableCollection(); public ObservableCollection Items { get; } = new ObservableCollection(); public HeroSummaryInfoViewModel? Hero { get; private set; } - public MapViewModel Map { get; private set; } = new MapViewModel(); + public MapViewModel Map { get; private set; } public Hero? hero; + private readonly WorldHandler worldHandler; } } diff --git a/Client/Application/ViewModels/MapViewModel.cs b/Client/Application/ViewModels/MapViewModel.cs index 0296f88..fe9c153 100644 --- a/Client/Application/ViewModels/MapViewModel.cs +++ b/Client/Application/ViewModels/MapViewModel.cs @@ -11,6 +11,9 @@ using System.Threading.Tasks; using System.Windows.Input; using System.Windows.Media.Animation; using System.Collections.Specialized; +using Client.Application.Commands; +using System.Reflection.Metadata; +using System.Windows; namespace Client.Application.ViewModels { @@ -125,10 +128,33 @@ namespace Client.Application.ViewModels } } - public MapViewModel() + public ICommand MouseLeftClickCommand { get; } + private void 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 + ); + worldHandler.RequestMoveToLocation(location); + } + + public MapViewModel(WorldHandler worldHandler) { Creatures.CollectionChanged += Creatures_CollectionChanged; Drops.CollectionChanged += Drops_CollectionChanged; + this.worldHandler = worldHandler; + MouseLeftClickCommand = new RelayCommand(OnLeftMouseClick); } private void Drops_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) @@ -167,5 +193,6 @@ namespace Client.Application.ViewModels private float scale = 8; private double viewportWidth = 0; private double viewportHeight = 0; + private readonly WorldHandler worldHandler; } } diff --git a/Client/Application/ViewModels/SkillListViewModel.cs b/Client/Application/ViewModels/SkillListViewModel.cs index 4186fa6..7a07b11 100644 --- a/Client/Application/ViewModels/SkillListViewModel.cs +++ b/Client/Application/ViewModels/SkillListViewModel.cs @@ -1,10 +1,13 @@ 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 { @@ -21,11 +24,19 @@ namespace Client.Application.ViewModels public uint? Cost => skill.Cost == 0 ? null : skill.Cost; public int? Range => skill.Range <= 0 ? null : skill.Range; - public SkillListViewModel(Skill skill) + 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) @@ -61,6 +72,7 @@ namespace Client.Application.ViewModels } } + private readonly WorldHandler worldHandler; private readonly Skill skill; } } diff --git a/Client/Application/Views/MainWindow.xaml b/Client/Application/Views/MainWindow.xaml index 00ffe42..d292bff 100644 --- a/Client/Application/Views/MainWindow.xaml +++ b/Client/Application/Views/MainWindow.xaml @@ -53,10 +53,15 @@ + + + + + - + @@ -71,6 +76,10 @@ + + + + @@ -83,7 +92,7 @@ - + @@ -146,7 +155,8 @@ - + + @@ -270,8 +280,8 @@ - - + + diff --git a/Client/Bot.cs b/Client/Bot.cs index e478a54..0d81d05 100644 --- a/Client/Bot.cs +++ b/Client/Bot.cs @@ -78,6 +78,18 @@ namespace Client eventBus.Subscrbe((EventHandlerInterface)viewModel); eventBus.Subscrbe((EventHandlerInterface)viewModel); + var worldHandler = serviceProvider.GetRequiredService(); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)worldHandler); + eventBus.Subscrbe((EventHandlerInterface)serviceProvider.GetRequiredService()); eventBus.Subscrbe((EventHandlerInterface)serviceProvider.GetRequiredService()); eventBus.Subscrbe((EventHandlerInterface)serviceProvider.GetRequiredService()); diff --git a/Client/Domain/Common/CreatureExtensions.cs b/Client/Domain/Common/CreatureExtensions.cs index 0720448..5243481 100644 --- a/Client/Domain/Common/CreatureExtensions.cs +++ b/Client/Domain/Common/CreatureExtensions.cs @@ -12,12 +12,12 @@ namespace Client.Domain.Common { public static float Distance(this CreatureInterface creature, CreatureInterface other) { - return creature.Transform.Position.HorizontalDistance(other.Transform.Position) / 100; + return creature.Transform.Position.HorizontalDistance(other.Transform.Position); } public static float DeltaZ(this CreatureInterface creature, CreatureInterface other) { - return (creature.Transform.Position.Z - other.Transform.Position.Z) / 100; + return (creature.Transform.Position.Z - other.Transform.Position.Z); } } } diff --git a/Client/Domain/DTO/OutgoingMessage.cs b/Client/Domain/DTO/OutgoingMessage.cs new file mode 100644 index 0000000..51271bd --- /dev/null +++ b/Client/Domain/DTO/OutgoingMessage.cs @@ -0,0 +1,28 @@ +using Client.Domain.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Client.Domain.DTO +{ + public class OutgoingMessage + { + public readonly OutgoingMessageTypeEnum Type; + public readonly T? Content; + + public OutgoingMessage(OutgoingMessageTypeEnum type, T? content = default) + { + Type = type; + Content = content; + } + } + + public class EmptyOutgoingMessage : OutgoingMessage + { + public EmptyOutgoingMessage(OutgoingMessageTypeEnum type) : base(type) + { + } + } +} diff --git a/Client/Domain/Entities/CreatureInterface.cs b/Client/Domain/Entities/CreatureInterface.cs index 8160b0d..098861f 100644 --- a/Client/Domain/Entities/CreatureInterface.cs +++ b/Client/Domain/Entities/CreatureInterface.cs @@ -18,6 +18,6 @@ namespace Client.Domain.Entities string BriefInfo { get; } CreatureTypeEnum Type { get; } uint AggroRadius { get; set; } - + bool IsHostile { get; set; } } } diff --git a/Client/Domain/Entities/Hero.cs b/Client/Domain/Entities/Hero.cs index 3dba512..a51df3b 100644 --- a/Client/Domain/Entities/Hero.cs +++ b/Client/Domain/Entities/Hero.cs @@ -62,6 +62,7 @@ namespace Client.Domain.Entities } } public uint AggroRadius { get; set; } = 0; + public bool IsHostile { get; set; } = false; public Hero(uint id, Transform transform, FullName fullName, VitalStats vitalStats, Phenotype phenotype, ExperienceInfo experienceInfo, PermanentStats permanentStats, VariableStats variableStats, Reputation reputation, InventoryInfo inventoryInfo, uint targetId, bool isStanding) { diff --git a/Client/Domain/Entities/NPC.cs b/Client/Domain/Entities/NPC.cs index 4514b33..a70cc03 100644 --- a/Client/Domain/Entities/NPC.cs +++ b/Client/Domain/Entities/NPC.cs @@ -74,7 +74,7 @@ namespace Client.Domain.Entities { string result = FullName.Nickname; - if (IsDead()) + if (VitalStats.IsDead) { result += " (dead)"; } @@ -124,17 +124,12 @@ namespace Client.Domain.Entities private void VitalStats_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (e.PropertyName == "Hp" || e.PropertyName == "MaxHp") + if (e.PropertyName == "IsDead") { OnPropertyChanged("Name"); } } - public bool IsDead() - { - return VitalStats.MaxHp > 0 && VitalStats.Hp <= 0; - } - private uint level; private uint aggroRadius; private VitalStats vitalStats; diff --git a/Client/Domain/Entities/Player.cs b/Client/Domain/Entities/Player.cs index 76114a0..f282c46 100644 --- a/Client/Domain/Entities/Player.cs +++ b/Client/Domain/Entities/Player.cs @@ -62,6 +62,7 @@ namespace Client.Domain.Entities public VitalStats VitalStats { get => vitalStats; set => vitalStats = value; } public CreatureTypeEnum Type { get => CreatureTypeEnum.Player; } public uint AggroRadius { get; set; } = 0; + public bool IsHostile { get; set; } = false; public Player(uint id, Transform transform, FullName fullName, Phenotype phenotype) { diff --git a/Client/Domain/Enums/OutgoingMessageTypeEnum.cs b/Client/Domain/Enums/OutgoingMessageTypeEnum.cs new file mode 100644 index 0000000..2692dc9 --- /dev/null +++ b/Client/Domain/Enums/OutgoingMessageTypeEnum.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Client.Domain.Enums +{ + public enum OutgoingMessageTypeEnum + { + Invalidate, + Move, + AcquireTarget, + Attack, + Pickup, + UseSkill, + UseItem, + ToggleSoulshot, + Sit, + Stand + } +} diff --git a/Client/Domain/Parsers/OutgoingMessageBuilderInterface.cs b/Client/Domain/Parsers/OutgoingMessageBuilderInterface.cs new file mode 100644 index 0000000..4078553 --- /dev/null +++ b/Client/Domain/Parsers/OutgoingMessageBuilderInterface.cs @@ -0,0 +1,14 @@ +using Client.Domain.DTO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Client.Domain.Parsers +{ + public interface OutgoingMessageBuilderInterface + { + string Build(OutgoingMessage message); + } +} diff --git a/Client/Domain/Service/WorldHandler.cs b/Client/Domain/Service/WorldHandler.cs new file mode 100644 index 0000000..2c0f637 --- /dev/null +++ b/Client/Domain/Service/WorldHandler.cs @@ -0,0 +1,289 @@ +using Client.Domain.Entities; +using Client.Domain.Events; +using Client.Domain.Parsers; +using Client.Domain.ValueObjects; +using Client.Domain.DTO; +using Client.Domain.Enums; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Collections.Concurrent; +using Client.Domain.Transports; + +namespace Client.Domain.Service +{ + public class WorldHandler : + EventHandlerInterface, + EventHandlerInterface, + EventHandlerInterface, + EventHandlerInterface, + EventHandlerInterface, + EventHandlerInterface, + EventHandlerInterface, + EventHandlerInterface, + EventHandlerInterface, + EventHandlerInterface + { + public void RequestMoveToLocation(Vector3 location) + { + if (hero == null) + { + return; + } + + SendMessage(OutgoingMessageTypeEnum.Move, location); + } + + public void RequestMoveToEntity(uint id) + { + if (hero == null) + { + return; + } + + if (!creatures.ContainsKey(id) && !drops.ContainsKey(id)) + { + Debug.WriteLine("RequestMoveToEntity: entity " + id + " not found"); + return; + } + + var position = creatures.ContainsKey(id) ? creatures[id].Transform.Position : drops[id].Transform.Position; + + RequestMoveToLocation(position); + } + + public void RequestAcquireTarget(uint id) + { + if (hero == null) + { + return; + } + + if (hero.TargetId == id) + { + Debug.WriteLine("RequestAcquireTarget: creature " + id + " is already target"); + return; + } + if (!creatures.ContainsKey(id) && hero.Id != id) + { + Debug.WriteLine("RequestAcquireTarget: creature " + id + " not found"); + return; + } + + SendMessage(OutgoingMessageTypeEnum.AcquireTarget, id); + } + + public void RequestAttackOrFollow(uint id) + { + if (hero == null) + { + return; + } + + if (!creatures.ContainsKey(id)) + { + Debug.WriteLine("RequestAttackOrFollow: creature " + id + " not found"); + return; + } + + SendMessage(OutgoingMessageTypeEnum.Attack, id); + } + + public void RequestPickUp(uint id) + { + if (hero == null) + { + return; + } + + if (!drops.ContainsKey(id)) + { + Debug.WriteLine("RequestPickUp: drop " + id + " not found"); + return; + } + + SendMessage(OutgoingMessageTypeEnum.Pickup, id); + } + + public void RequestUseSkill(uint id, bool isForced, bool isShiftPressed) + { + if (hero == null) + { + return; + } + + if (!skills.TryGetValue(id, out Skill? skill)) + { + Debug.WriteLine("RequestUseSkill: skill " + id + " not found"); + return; + } + + if (!skill.IsActive) + { + Debug.WriteLine("RequestUseSkill: skill " + id + " is passive"); + return; + } + + SendMessage(OutgoingMessageTypeEnum.UseSkill, id); + } + + public void RequestUseItem(uint id) + { + if (hero == null) + { + return; + } + + if (!items.ContainsKey(id)) + { + Debug.WriteLine("RequestUseItem: item " + id + " not found"); + return; + } + + SendMessage(OutgoingMessageTypeEnum.UseItem, id); + } + + public void RequestToggleAutouseSoulshot(uint id) + { + if (hero == null) + { + return; + } + + if (!items.ContainsKey(id)) + { + Debug.WriteLine("RequestToggleAutouseSoulshot: item " + id + " not found"); + return; + } + + SendMessage(OutgoingMessageTypeEnum.ToggleSoulshot, id); + } + + public void RequestSit() + { + if (hero == null) + { + return; + } + + if (!hero.IsStanding) + { + Debug.WriteLine("RequestSit: hero is already sitting"); + return; + } + + SendMessage(OutgoingMessageTypeEnum.Sit); + } + + public void RequestStand() + { + if (hero == null) + { + return; + } + + if (hero.IsStanding) + { + Debug.WriteLine("RequestStand: hero is already standing"); + return; + } + + SendMessage(OutgoingMessageTypeEnum.Stand); + } + + private void SendMessage(OutgoingMessageTypeEnum type, T? content = default) + { + var message = outgoingMessageBuilder.Build( + new OutgoingMessage(type, content) + ); + transport.SendAsync(message); + Debug.WriteLine(message); + } + + private void SendMessage(OutgoingMessageTypeEnum type) + { + SendMessage(type); + } + + #region Handle Entity + public void Handle(HeroCreatedEvent @event) + { + hero = @event.Hero; + } + + public void Handle(HeroDeletedEvent @event) + { + hero = null; + } + + public void Handle(CreatureCreatedEvent @event) + { + if (!creatures.ContainsKey(@event.Creature.Id)) + { + creatures.TryAdd(@event.Creature.Id, @event.Creature); + } + } + + public void Handle(CreatureDeletedEvent @event) + { + creatures.Remove(@event.Id, out CreatureInterface? value); + } + + public void Handle(DropCreatedEvent @event) + { + if (!drops.ContainsKey(@event.Drop.Id)) + { + drops.TryAdd(@event.Drop.Id, @event.Drop); + } + } + + public void Handle(DropDeletedEvent @event) + { + drops.Remove(@event.Id, out Drop? value); + } + + public void Handle(SkillCreatedEvent @event) + { + if (!skills.ContainsKey(@event.Skill.Id)) + { + skills.TryAdd(@event.Skill.Id, @event.Skill); + } + } + + public void Handle(SkillDeletedEvent @event) + { + skills.Remove(@event.Id, out Skill? value); + } + + public void Handle(ItemCreatedEvent @event) + { + if (!items.ContainsKey(@event.Item.Id)) + { + items.TryAdd(@event.Item.Id, @event.Item); + } + } + + public void Handle(ItemDeletedEvent @event) + { + items.Remove(@event.Id, out BaseItem? value); + } + #endregion + + public WorldHandler(OutgoingMessageBuilderInterface outgoingMessageBuilder, TransportInterface transport) + { + this.outgoingMessageBuilder = outgoingMessageBuilder; + this.transport = transport; + } + + private Hero? hero; + private ConcurrentDictionary creatures = new ConcurrentDictionary(); + private ConcurrentDictionary drops = new ConcurrentDictionary(); + private ConcurrentDictionary skills = new ConcurrentDictionary(); + private ConcurrentDictionary items = new ConcurrentDictionary(); + private readonly OutgoingMessageBuilderInterface outgoingMessageBuilder; + private readonly TransportInterface transport; + } +} diff --git a/Client/Domain/ValueObjects/VitalStats.cs b/Client/Domain/ValueObjects/VitalStats.cs index 26cf09a..c69a28c 100644 --- a/Client/Domain/ValueObjects/VitalStats.cs +++ b/Client/Domain/ValueObjects/VitalStats.cs @@ -12,12 +12,12 @@ namespace Client.Domain.ValueObjects private uint cp; private uint maxCp; - public uint Hp { get => hp; set { if (value != hp) { hp = value; OnPropertyChanged("Hp"); OnPropertyChanged("MaxHp"); } } } - public uint MaxHp { get => Math.Max(hp, maxHp); set { if (value != maxHp) { maxHp = value; OnPropertyChanged("MaxHp"); } } } - public uint Mp { get => mp; set { if (value != mp) { mp = value; OnPropertyChanged("Mp"); } } } - public uint MaxMp { get => maxMp; set { if (value != maxMp) { maxMp = value; OnPropertyChanged("MaxMp"); } } } - public uint Cp { get => cp; set { if (value != cp) { cp = value; OnPropertyChanged("Cp"); } } } - public uint MaxCp { get => maxCp; set { if (value != maxCp) { maxCp = value; OnPropertyChanged("MaxCp"); } } } + public uint Hp { get => hp; set { if (value != hp) { hp = value; OnPropertyChanged(); OnPropertyChanged("MaxHp"); OnPropertyChanged("IsDead"); } } } + public uint MaxHp { get => Math.Max(hp, maxHp); set { if (value != maxHp) { maxHp = value; OnPropertyChanged(); OnPropertyChanged("IsDead"); } } } + public uint Mp { get => mp; set { if (value != mp) { mp = value; OnPropertyChanged(); } } } + public uint MaxMp { get => maxMp; set { if (value != maxMp) { maxMp = value; OnPropertyChanged(); } } } + public uint Cp { get => cp; set { if (value != cp) { cp = value; OnPropertyChanged(); } } } + public uint MaxCp { get => maxCp; set { if (value != maxCp) { maxCp = value; OnPropertyChanged(); } } } public double HpPercent { @@ -41,6 +41,8 @@ namespace Client.Domain.ValueObjects } } + public bool IsDead => MaxHp > 0 && hp == 0; + public VitalStats(uint hp, uint maxHp, uint mp, uint maxMp, uint cp, uint maxCp) { this.hp = hp; diff --git a/Client/Infrastructure/Parsers/JsonOutgoingMessageBuilder.cs b/Client/Infrastructure/Parsers/JsonOutgoingMessageBuilder.cs new file mode 100644 index 0000000..bc891de --- /dev/null +++ b/Client/Infrastructure/Parsers/JsonOutgoingMessageBuilder.cs @@ -0,0 +1,31 @@ +using Client.Domain; +using Client.Domain.Enums; +using Client.Domain.Parsers; +using Client.Domain.DTO; +using Client.Infrastructure.Parsers.Objects; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Client.Infrastructure.Parsers +{ + public class JsonOutgoingMessageBuilder : OutgoingMessageBuilderInterface + { + public string Build(OutgoingMessage message) + { + return JsonConvert.SerializeObject(new OutgoingMessage + { + Type = GetStringType(message.Type), + Content = JsonConvert.SerializeObject(message.Content) + }); + } + + private string GetStringType(OutgoingMessageTypeEnum type) + { + return type.ToString(); + } + } +} diff --git a/Client/Infrastructure/Parsers/Objects/OutgoingMessage.cs b/Client/Infrastructure/Parsers/Objects/OutgoingMessage.cs new file mode 100644 index 0000000..aa8398a --- /dev/null +++ b/Client/Infrastructure/Parsers/Objects/OutgoingMessage.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Client.Infrastructure.Parsers.Converters; + +namespace Client.Infrastructure.Parsers.Objects +{ + internal class OutgoingMessage + { + public string Type { get; set; } = ""; + [JsonConverter(typeof(RawConverter))] + public string Content { get; set; } = ""; + } +}