Skip to content

Commit

Permalink
Merge branch 'master' into dev/#517-fix-elf-bow-requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
sven-n authored Nov 12, 2024
2 parents 1cb3736 + 3c0565f commit 82aa8cb
Show file tree
Hide file tree
Showing 20 changed files with 278 additions and 46 deletions.
24 changes: 24 additions & 0 deletions src/DataModel/ItemExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,30 @@ public static bool IsTrainablePet(this ItemDefinition itemDefinition)
return itemDefinition.PetExperienceFormula is not null;
}

/// <summary>
/// Determines whether the specified item is a dark raven.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>
/// <c>true</c> if the specified item is raven; otherwise, <c>false</c>.
/// </returns>
public static bool IsDarkRaven(this Item item)
{
return item.Definition is { Group: 13, Number: 5 };
}

/// <summary>
/// Gets the dark raven leadership requirement, based on pet level.
/// TODO: Make somehow configurable?
/// </summary>
/// <param name="item">The item.</param>
/// <param name="petLevel">The pet level.</param>
/// <returns>The required leadership.</returns>
public static int GetDarkRavenLeadershipRequirement(this Item item, int petLevel)
{
return petLevel * 15 + 185;
}

/// <summary>
/// Determines whether this item can have a skill.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/GameLogic/IGameContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public interface IGameContext
DuelRoomManager DuelRoomManager { get; }

/// <summary>
/// Gets the state of the active self defenses.
/// Gets the state of the active self defenses. The datetime holds the timestamp when self-defense ends.
/// </summary>
ConcurrentDictionary<(Player Attacker, Player Defender), DateTime> SelfDefenseState { get; }

Expand Down
5 changes: 3 additions & 2 deletions src/GameLogic/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ public bool IsAnySelfDefenseActive()

var hitInfo = await attacker.CalculateDamageAsync(this, skill, isCombo, damageFactor).ConfigureAwait(false);

if (hitInfo.HealthDamage == 0)
if (hitInfo is { HealthDamage: 0, ShieldDamage: 0 })
{
await this.InvokeViewPlugInAsync<IShowHitPlugIn>(p => p.ShowHitAsync(this, hitInfo)).ConfigureAwait(false);
if (attacker is IWorldObserver observer)
Expand Down Expand Up @@ -2332,7 +2332,8 @@ async ValueTask AddExpToPetAsync(Item pet, double experience)
{
pet.PetExperience += (int)experience;

while (pet.PetExperience >= pet.Definition!.GetExperienceOfPetLevel((byte)(pet.Level + 1), pet.Definition!.MaximumItemLevel))
while (pet.PetExperience >= pet.Definition!.GetExperienceOfPetLevel((byte)(pet.Level + 1), pet.Definition!.MaximumItemLevel)
&& (!pet.IsDarkRaven() || pet.GetDarkRavenLeadershipRequirement(pet.Level + 1) <= this.Attributes![Stats.TotalLeadership]))
{
pet.Level++;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public FrustumBasedTargetFilter(float startWidth, float endWidth, float distance
/// <returns><c>true</c> if the target is within hit bounds; otherwise, <c>false</c>.</returns>
public bool IsTargetWithinBounds(ILocateable attacker, ILocateable target, Point targetAreaCenter, byte rotation)
{
if (attacker.Position == target.Position)
{
return true;
}

var frustum = this.GetFrustum(attacker.Position, rotation);
return IsWithinFrustum(frustum, target.Position);
}
Expand Down
11 changes: 5 additions & 6 deletions src/GameLogic/PlugIns/SelfDefensePlugIn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ public class SelfDefensePlugIn : IPeriodicTaskPlugIn, IAttackableGotHitPlugIn, I
/// <inheritdoc />
public async ValueTask ExecuteTaskAsync(GameContext gameContext)
{
var configuration = this.Configuration ??= CreateDefaultConfiguration();
var timedOut = gameContext.SelfDefenseState.Where(s => DateTime.UtcNow.Subtract(s.Value) >= configuration.SelfDefenseTimeOut).ToList();
foreach (var (pair, lastAttack) in timedOut)
var timedOut = gameContext.SelfDefenseState.Where(s => s.Value < DateTime.UtcNow).ToList();
foreach (var (pair, _) in timedOut)
{
if (gameContext.SelfDefenseState.Remove(pair, out _))
{
Expand Down Expand Up @@ -80,16 +79,16 @@ public void AttackableGotHit(IAttackable attackable, IAttacker attacker, HitInfo
return;
}

var now = DateTime.UtcNow;
var timeout = DateTime.UtcNow.Add(this.Configuration?.SelfDefenseTimeOut ?? TimeSpan.FromMinutes(1));
var gameContext = defender.GameContext;
gameContext.SelfDefenseState.AddOrUpdate(
(attackerPlayer, defender),
tuple =>
{
_ = this.BeginSelfDefenseAsync(attackerPlayer, defender);
return now;
return timeout;
},
(_, _) => now);
(_, _) => timeout);
}

/// <inheritdoc />
Expand Down
2 changes: 1 addition & 1 deletion src/GameServer/RemoteView/Character/UpdateStatsPlugIn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ await this._player.Connection.SendCurrentHealthAndShieldAsync(

if (updatedStats.HasFlag(IUpdateStatsPlugIn.UpdatedStats.Mana))
{
await this._player.Connection.SendCurrentHealthAndShieldAsync(
await this._player.Connection.SendCurrentManaAndAbilityAsync(
(ushort)Math.Max(this._player.Attributes[Stats.CurrentMana], 0f),
(ushort)Math.Max(this._player.Attributes[Stats.CurrentAbility], 0f)).ConfigureAwait(false);
}
Expand Down
42 changes: 42 additions & 0 deletions src/Persistence/BasicModel/AttributeDefinition.Custom.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// <copyright file="AttributeDefinition.Custom.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.Persistence.BasicModel;

using MUnique.OpenMU.DataModel;

/// <summary>
/// A plain implementation of <see cref="MUnique.OpenMU.AttributeSystem.AttributeDefinition"/>.
/// </summary>
public partial class AttributeDefinition :
IAssignable,
IAssignable<MUnique.OpenMU.AttributeSystem.AttributeDefinition>,
ICloneable<MUnique.OpenMU.AttributeSystem.AttributeDefinition>
{
/// <inheritdoc />
public virtual AttributeSystem.AttributeDefinition Clone(DataModel.Configuration.GameConfiguration gameConfiguration)
{
var clone = new AttributeDefinition();
clone.AssignValuesOf(this, gameConfiguration);
return clone;
}

/// <inheritdoc />
public virtual void AssignValuesOf(object other, DataModel.Configuration.GameConfiguration gameConfiguration)
{
if (other is AttributeSystem.AttributeDefinition typedOther)
{
AssignValuesOf(typedOther, gameConfiguration);
}
}

/// <inheritdoc />
public virtual void AssignValuesOf(AttributeSystem.AttributeDefinition other, DataModel.Configuration.GameConfiguration gameConfiguration)
{
this.Id = other.Id;
this.Designation = other.Designation;
this.Description = other.Description;
this.MaximumValue = other.MaximumValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// <copyright file="AttributeDefinition.Custom.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.Persistence.EntityFramework.Model;

using MUnique.OpenMU.DataModel;

/// <summary>
/// The Entity Framework Core implementation of <see cref="MUnique.OpenMU.AttributeSystem.AttributeDefinition"/>.
/// </summary>

internal partial class AttributeDefinition :
IAssignable,
IAssignable<MUnique.OpenMU.AttributeSystem.AttributeDefinition>,
ICloneable<MUnique.OpenMU.AttributeSystem.AttributeDefinition>
{
/// <inheritdoc />
public virtual AttributeSystem.AttributeDefinition Clone(DataModel.Configuration.GameConfiguration gameConfiguration)
{
var clone = new AttributeDefinition();
clone.AssignValuesOf(this, gameConfiguration);
return clone;
}

/// <inheritdoc />
public virtual void AssignValuesOf(object other, DataModel.Configuration.GameConfiguration gameConfiguration)
{
if (other is AttributeSystem.AttributeDefinition typedOther)
{
AssignValuesOf(typedOther, gameConfiguration);
}
}

/// <inheritdoc />
public virtual void AssignValuesOf(AttributeSystem.AttributeDefinition other, DataModel.Configuration.GameConfiguration gameConfiguration)
{
this.Id = other.Id;
this.Designation = other.Designation;
this.Description = other.Description;
this.MaximumValue = other.MaximumValue;
}
}
1 change: 1 addition & 0 deletions src/Web/AdminPanel/MUnique.OpenMU.Web.AdminPanel.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Blazored.Modal" Version="6.0.1" />
<PackageReference Include="Blazored.Toast" Version="4.2.1" />
<PackageReference Include="Blazored.Typeahead" Version="4.7.0" />
<PackageReference Include="BlazorInputFile" Version="0.2.0" />
<PackageReference Include="Mapster" Version="7.4.0" />
Expand Down
18 changes: 9 additions & 9 deletions src/Web/AdminPanel/Pages/CreateConnectServerConfig.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace MUnique.OpenMU.Web.AdminPanel.Pages;

using System.ComponentModel.DataAnnotations;
using System.Threading;
using Blazored.Modal.Services;
using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Interfaces;
Expand Down Expand Up @@ -42,10 +42,10 @@ public partial class CreateConnectServerConfig : ComponentBase, IAsyncDisposable
public IDataSource<GameConfiguration> DataSource { get; set; } = null!;

/// <summary>
/// Gets or sets the modal service.
/// Gets or sets the toast service.
/// </summary>
[Inject]
public IModalService ModalService { get; set; } = null!;
public IToastService ToastService { get; set; } = null!;

/// <summary>
/// Gets or sets the navigation manager.
Expand Down Expand Up @@ -131,7 +131,6 @@ private async ValueTask<ConnectServerDefinition> CreateDefinitionByViewModelAsyn

private async Task OnSaveButtonClickAsync()
{
string text;
try
{
var gameConfiguration = await this.DataSource.GetOwnerAsync().ConfigureAwait(false);
Expand All @@ -141,13 +140,13 @@ private async Task OnSaveButtonClickAsync()
var existingServerDefinitions = (await saveContext.GetAsync<ConnectServerDefinition>().ConfigureAwait(false)).ToList();
if (existingServerDefinitions.Any(def => def.ServerId == this._viewModel?.ServerId))
{
await this.ModalService.ShowMessageAsync("Save", $"Server with Id {this._viewModel?.ServerId} already exists. Please use another value.").ConfigureAwait(true);
this.ToastService.ShowError($"Server with Id {this._viewModel?.ServerId} already exists. Please use another value.");
return;
}

if (existingServerDefinitions.Any(def => def.ClientListenerPort == this._viewModel?.NetworkPort))
{
await this.ModalService.ShowMessageAsync("Save", $"A server with tcp port {this._viewModel?.NetworkPort} already exists. Please use another tcp port.").ConfigureAwait(true);
this.ToastService.ShowError($"A server with tcp port {this._viewModel?.NetworkPort} already exists. Please use another tcp port.");
return;
}

Expand All @@ -157,24 +156,25 @@ private async Task OnSaveButtonClickAsync()
this._initState = "Saving Configuration ...";
await this.InvokeAsync(this.StateHasChanged);
var success = await saveContext.SaveChangesAsync().ConfigureAwait(true);
text = success ? "The changes have been saved." : "There were no changes to save.";

// if success, init new game server instance
if (success)
{
this.ToastService.ShowSuccess("The connection server configuration has been saved. Initializing connect server ...");
this._initState = "Initializing Connect Server ...";
await this.InvokeAsync(this.StateHasChanged);
await this.ServerInstanceManager.InitializeConnectServerAsync(connectServerDefinition.ConfigurationId);
this.NavigationManager.NavigateTo("servers");
return;
}

this.ToastService.ShowError("No changes have been saved.");
}
catch (Exception ex)
{
text = $"An unexpected error occurred: {ex.Message}.";
this.ToastService.ShowError($"An unexpected error occurred: {ex.Message}.");
}

await this.ModalService.ShowMessageAsync("Save", text).ConfigureAwait(true);
this._initState = null;
}

Expand Down
18 changes: 13 additions & 5 deletions src/Web/AdminPanel/Pages/CreateGameServerConfig.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace MUnique.OpenMU.Web.AdminPanel.Pages;
using System.ComponentModel.DataAnnotations;
using System.Threading;
using Blazored.Modal.Services;
using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Persistence;
Expand Down Expand Up @@ -48,6 +49,12 @@ public partial class CreateGameServerConfig : ComponentBase, IAsyncDisposable
[Inject]
public IModalService ModalService { get; set; } = null!;

/// <summary>
/// Gets or sets the toast service.
/// </summary>
[Inject]
public IToastService ToastService { get; set; } = null!;

/// <summary>
/// Gets or sets the navigation manager.
/// </summary>
Expand Down Expand Up @@ -152,13 +159,13 @@ private async Task OnSaveButtonClickAsync()
var existingServerDefinitions = (await saveContext.GetAsync<GameServerDefinition>().ConfigureAwait(false)).ToList();
if (existingServerDefinitions.Any(def => def.ServerID == this._viewModel?.ServerId))
{
await this.ModalService.ShowMessageAsync("Save", $"Server with Id {this._viewModel?.ServerId} already exists. Please use another value.").ConfigureAwait(true);
this.ToastService.ShowError($"Server with Id {this._viewModel?.ServerId} already exists. Please use another value.");
return;
}

if (existingServerDefinitions.Any(def => def.Endpoints.Any(endpoint => endpoint.NetworkPort == this._viewModel?.NetworkPort)))
{
await this.ModalService.ShowMessageAsync("Save", $"A server with tcp port {this._viewModel?.NetworkPort} already exists. Please use another tcp port.").ConfigureAwait(true);
this.ToastService.ShowError($"A server with tcp port {this._viewModel?.NetworkPort} already exists. Please use another tcp port.");
return;
}

Expand All @@ -168,24 +175,25 @@ private async Task OnSaveButtonClickAsync()
this._initState = "Saving Configuration ...";
await this.InvokeAsync(this.StateHasChanged);
var success = await saveContext.SaveChangesAsync().ConfigureAwait(true);
text = success ? "The changes have been saved." : "There were no changes to save.";

// if success, init new game server instance
if (success)
{
this.ToastService.ShowSuccess("The game server configuration has been saved. Initializing game server ...");
this._initState = "Initializing Game Server ...";
await this.InvokeAsync(this.StateHasChanged);
await this.ServerInstanceManager.InitializeGameServerAsync(gameServerDefinition.ServerID);
this.NavigationManager.NavigateTo("servers");
return;
}

this.ToastService.ShowError("No changes have been saved.");
}
catch (Exception ex)
{
text = $"An unexpected error occurred: {ex.Message}.";
this.ToastService.ShowError($"An unexpected error occurred: {ex.Message}.");
}

await this.ModalService.ShowMessageAsync("Save", text).ConfigureAwait(true);
this._initState = null;
}

Expand Down
Loading

0 comments on commit 82aa8cb

Please sign in to comment.