baseline: OpenMU upstream b5a0961 (fresh source)
This commit is contained in:
155
src/GameLogic/MuHelper/IMuHelperSettings.cs
Normal file
155
src/GameLogic/MuHelper/IMuHelperSettings.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
// <copyright file="IMuHelperSettings.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.MuHelper;
|
||||
|
||||
/// <summary>
|
||||
/// The Mu Helper player settings.
|
||||
/// </summary>
|
||||
public interface IMuHelperSettings
|
||||
{
|
||||
/// <summary>Gets the always-active basic attack skill ID (0 = no skill, use normal attack).</summary>
|
||||
int BasicSkillId { get; }
|
||||
|
||||
/// <summary>Gets the first conditional skill ID.</summary>
|
||||
int ActivationSkill1Id { get; }
|
||||
|
||||
/// <summary>Gets the second conditional skill ID.</summary>
|
||||
int ActivationSkill2Id { get; }
|
||||
|
||||
/// <summary>Gets the timer interval (seconds) for ActivationSkill1 when Skill1Delay is set.</summary>
|
||||
int DelayMinSkill1 { get; }
|
||||
|
||||
/// <summary>Gets the timer interval (seconds) for ActivationSkill2 when Skill2Delay is set.</summary>
|
||||
int DelayMinSkill2 { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use timer for the skill 1.</summary>
|
||||
bool Skill1UseTimer { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use condition for the skill 1.</summary>
|
||||
bool Skill1UseCondition { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use the precondition for Skill1 (false = nearby, true = attacking).</summary>
|
||||
bool Skill1ConditionAttacking { get; }
|
||||
|
||||
/// <summary>Gets the mob count threshold for Skill1 condition: 0=2+, 1=3+, 2=4+, 3=5+.</summary>
|
||||
int Skill1SubCondition { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use timer for the skill 2.</summary>
|
||||
bool Skill2UseTimer { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use condition for the skill 2.</summary>
|
||||
bool Skill2UseCondition { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use the precondition for Skill2 (false = nearby, true = attacking).</summary>
|
||||
bool Skill2ConditionAttacking { get; }
|
||||
|
||||
/// <summary>Gets the mob count threshold for Skill2 condition.</summary>
|
||||
int Skill2SubCondition { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use combo mode.</summary>
|
||||
bool UseCombo { get; }
|
||||
|
||||
/// <summary>Gets the hunting range nibble (0-15); multiply to get tile distance.</summary>
|
||||
int HuntingRange { get; }
|
||||
|
||||
/// <summary>Gets the max seconds away from original position before regrouping.</summary>
|
||||
int MaxSecondsAway { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to counter-attack enemies that attack from long range.</summary>
|
||||
bool LongRangeCounterAttack { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to return to original spawn position when away too long.</summary>
|
||||
bool ReturnToOriginalPosition { get; }
|
||||
|
||||
/// <summary>Gets the Buff 0 skill id.</summary>
|
||||
int BuffSkill0Id { get; }
|
||||
|
||||
/// <summary>Gets the Buff 1 skill id.</summary>
|
||||
int BuffSkill1Id { get; }
|
||||
|
||||
/// <summary>Gets the Buff 2 skill id.</summary>
|
||||
int BuffSkill2Id { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether apply buffs based on duration (i.e. when the buff expires).</summary>
|
||||
bool BuffOnDuration { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether apply buff duration logic to party members too.</summary>
|
||||
bool BuffDurationForParty { get; }
|
||||
|
||||
/// <summary>Gets the buff cast interval in seconds (0 = disabled).</summary>
|
||||
int BuffCastIntervalSeconds { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use auto-heal.</summary>
|
||||
bool AutoHeal { get; }
|
||||
|
||||
/// <summary>Gets the self-heal threshold (% HP, e.g. 30 means heal when below 30%).</summary>
|
||||
int HealThresholdPercent { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use drain life.</summary>
|
||||
bool UseDrainLife { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use healing potion.</summary>
|
||||
bool UseHealPotion { get; }
|
||||
|
||||
/// <summary>Gets the potion use threshold (% HP).</summary>
|
||||
int PotionThresholdPercent { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to support party.</summary>
|
||||
bool SupportParty { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to auto heal party.</summary>
|
||||
bool AutoHealParty { get; }
|
||||
|
||||
/// <summary>Gets the party member HP threshold (%) below which healing is applied.</summary>
|
||||
int HealPartyThresholdPercent { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use dark raven.</summary>
|
||||
bool UseDarkRaven { get; }
|
||||
|
||||
/// <summary>Gets the dark raven mode 0 = cease, 1 = auto-attack, 2 = attack with owner.</summary>
|
||||
int DarkRavenMode { get; }
|
||||
|
||||
/// <summary>Gets the obtain range.</summary>
|
||||
int ObtainRange { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether pickup all items.</summary>
|
||||
bool PickAllItems { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether pickup selected items.</summary>
|
||||
bool PickSelectItems { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether pickup jewels.</summary>
|
||||
bool PickJewel { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether pickup zen.</summary>
|
||||
bool PickZen { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether pickup ancient items.</summary>
|
||||
bool PickAncient { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether pickup excellent items.</summary>
|
||||
bool PickExcellent { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether pickup extra items.</summary>
|
||||
bool PickExtraItems { get; }
|
||||
|
||||
/// <summary>Gets the extra item names. Up to 12 item name substrings; pick any dropped item whose name contains one of these.</summary>
|
||||
IReadOnlyList<string> ExtraItemNames { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to repair items.</summary>
|
||||
bool RepairItem { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to automatically defend against players attacking the character.</summary>
|
||||
bool UseSelfDefense { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to automatically accept requests from friends.</summary>
|
||||
bool AutoAcceptFriend { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to automatically accept requests from guild.</summary>
|
||||
bool AutoAcceptGuild { get; }
|
||||
|
||||
/// <summary>Gets a value indicating whether to use basic attack as fallback when the configured skill cannot be used.</summary>
|
||||
bool FallbackBasicAttack { get; }
|
||||
}
|
||||
180
src/GameLogic/MuHelper/MuHelper.cs
Normal file
180
src/GameLogic/MuHelper/MuHelper.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
// <copyright file="MuHelper.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.MuHelper;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.PlugIns.ChatCommands;
|
||||
using MUnique.OpenMU.GameLogic.Views.MuHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the logic of the 'MU Helper'.
|
||||
/// </summary>
|
||||
public class MuHelper : AsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IElement"/> which is added to the players <see cref="IAttributeSystem"/>
|
||||
/// when the MU Helper is active.
|
||||
/// </summary>
|
||||
private static readonly IElement ActiveElement = new ConstantElement(1);
|
||||
|
||||
/// <summary>
|
||||
/// Associated player.
|
||||
/// </summary>
|
||||
private readonly Player _player;
|
||||
|
||||
/// <summary>
|
||||
/// The current configuration.
|
||||
/// </summary>
|
||||
private readonly MuHelperConfiguration _configuration;
|
||||
|
||||
private CancellationTokenSource? _stopCts;
|
||||
private Task? _runTask;
|
||||
private DateTime _startTimestamp;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MuHelper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">current player.</param>
|
||||
public MuHelper(Player player)
|
||||
{
|
||||
this._player = player;
|
||||
this._configuration = this._player.GameContext.FeaturePlugIns.GetPlugIn<MuHelperFeaturePlugIn>()?.Configuration ?? new MuHelperConfiguration();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start Mu Helper.
|
||||
/// </summary>
|
||||
public async ValueTask<bool> TryStartAsync()
|
||||
{
|
||||
if (this._runTask is not null)
|
||||
{
|
||||
await this._player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.MuHelperAlreadyRunning)).ConfigureAwait(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._player.Level < this._configuration.MinLevel)
|
||||
{
|
||||
await this._player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.MuHelperMinimumLevel), this._configuration.MinLevel).ConfigureAwait(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._player.Level > this._configuration.MaxLevel)
|
||||
{
|
||||
await this._player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.MuHelperMaximumLevel), this._configuration.MaxLevel).ConfigureAwait(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
this._startTimestamp = DateTime.UtcNow;
|
||||
var requiredMoney = this.CalculateRequiredMoney();
|
||||
|
||||
if (!this._player.TryRemoveMoney(requiredMoney))
|
||||
{
|
||||
await this._player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.MuHelperRequiresMoney), requiredMoney).ConfigureAwait(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
await this._player.InvokeViewPlugInAsync<IMuHelperStatusUpdatePlugIn>(p => p.StartAsync()).ConfigureAwait(false);
|
||||
await this._player.InvokeViewPlugInAsync<IMuHelperStatusUpdatePlugIn>(p => p.ConsumeMoneyAsync((uint)requiredMoney)).ConfigureAwait(false);
|
||||
|
||||
this._player.Attributes?.AddElement(ActiveElement, Stats.IsMuHelperActive);
|
||||
this._stopCts?.Dispose();
|
||||
this._stopCts = new CancellationTokenSource();
|
||||
var cts = this._stopCts.Token;
|
||||
this._runTask = this.RunLoopAsync(cts);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the MU Helper.
|
||||
/// </summary>
|
||||
public async ValueTask StopAsync()
|
||||
{
|
||||
if (this._runTask is not { } runTask
|
||||
|| this._stopCts is not { } stopCts)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this._player.Attributes?.RemoveElement(ActiveElement, Stats.IsMuHelperActive);
|
||||
|
||||
await stopCts.CancelAsync().ConfigureAwait(false);
|
||||
|
||||
// Skip awaiting the loop task if we are currently executing inside it
|
||||
// (i.e. CollectAsync triggered StopAsync), to avoid a self-deadlock.
|
||||
if (runTask.Id != Task.CurrentId)
|
||||
{
|
||||
await runTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
this._runTask = null;
|
||||
stopCts.Dispose();
|
||||
this._stopCts = null;
|
||||
|
||||
await this._player.InvokeViewPlugInAsync<IMuHelperStatusUpdatePlugIn>(p => p.StopAsync()).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this._player.Logger.LogWarning(ex, "Exception during stopping the mu helper for {CharacterName}: {Error}", this._player.Name, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask DisposeAsyncCore()
|
||||
{
|
||||
await this.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private int CalculateRequiredMoney() =>
|
||||
MuHelperZenCostCalculator.Calculate(this._player, this._configuration, this._startTimestamp);
|
||||
|
||||
private async Task RunLoopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this._configuration.PayInterval <= TimeSpan.Zero)
|
||||
{
|
||||
this._player.Logger.LogDebug("MU Helper PayInterval is {PayInterval}. Stopping for {CharacterName}.", this._configuration.PayInterval, this._player.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
using var timer = new PeriodicTimer(this._configuration.PayInterval);
|
||||
while (true)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await timer.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false);
|
||||
await this.CollectAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// expected when StopAsync cancels the token.
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Fail(ex.Message, ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the money collection.
|
||||
/// </summary>
|
||||
private async ValueTask CollectAsync()
|
||||
{
|
||||
var amount = this.CalculateRequiredMoney();
|
||||
if (amount > 0 && this._player.TryRemoveMoney(amount))
|
||||
{
|
||||
await this._player.InvokeViewPlugInAsync<IMuHelperStatusUpdatePlugIn>(p => p.ConsumeMoneyAsync((uint)amount)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await this.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/GameLogic/MuHelper/MuHelperConfiguration.cs
Normal file
47
src/GameLogic/MuHelper/MuHelperConfiguration.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
// <copyright file="MuHelperConfiguration.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.MuHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for the <see cref="MuHelper"/>.
|
||||
/// </summary>
|
||||
public class MuHelperConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the cost of the helper per stage. This value is applied per
|
||||
/// <see cref="PayInterval"/>, and multiplied with the total character level.
|
||||
/// </summary>
|
||||
[Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.MuHelperConfiguration_CostPerStage_Name), Description = nameof(PlugInResources.MuHelperConfiguration_CostPerStage_Description))]
|
||||
public IList<int> CostPerStage { get; set; } = new List<int>
|
||||
{
|
||||
20, 50, 80, 100, 120,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pay interval.
|
||||
/// </summary>
|
||||
[Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.MuHelperConfiguration_PayInterval_Name))]
|
||||
public TimeSpan PayInterval { get; set; } = TimeSpan.FromMinutes(5);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stage interval.
|
||||
/// After each interval, the stage gets increased to the next level with
|
||||
/// usually increasing costs.
|
||||
/// </summary>
|
||||
[Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.MuHelperConfiguration_StageInterval_Name), Description = nameof(PlugInResources.MuHelperConfiguration_StageInterval_Description))]
|
||||
public TimeSpan StageInterval { get; set; } = TimeSpan.FromMinutes(200);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum character level.
|
||||
/// </summary>
|
||||
[Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.MuHelperConfiguration_MinLevel_Name))]
|
||||
public int MinLevel { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum character level.
|
||||
/// </summary>
|
||||
[Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.MuHelperConfiguration_MaxLevel_Name))]
|
||||
public int MaxLevel { get; set; } = 400;
|
||||
}
|
||||
23
src/GameLogic/MuHelper/MuHelperFeaturePlugIn.cs
Normal file
23
src/GameLogic/MuHelper/MuHelperFeaturePlugIn.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
// <copyright file="MuHelperFeaturePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.MuHelper;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Feature plugin which provides the configuration for the reset feature.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.MuHelperFeaturePlugIn_Name), Description = nameof(PlugInResources.MuHelperFeaturePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("E90A72C3-0459-4323-B6D3-171F88D35542")]
|
||||
public class MuHelperFeaturePlugIn : IFeaturePlugIn, ISupportCustomConfiguration<MuHelperConfiguration>, ISupportDefaultCustomConfiguration
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public MuHelperConfiguration? Configuration { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object CreateDefaultConfig() => new MuHelperConfiguration();
|
||||
}
|
||||
21
src/GameLogic/MuHelper/MuHelperStatus.cs
Normal file
21
src/GameLogic/MuHelper/MuHelperStatus.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
// <copyright file="MuHelperStatus.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.MuHelper;
|
||||
|
||||
/// <summary>
|
||||
/// MuBot Status Map.
|
||||
/// </summary>
|
||||
public enum MuHelperStatus : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Enabled.
|
||||
/// </summary>
|
||||
Enabled = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Disabled.
|
||||
/// </summary>
|
||||
Disabled = 1,
|
||||
}
|
||||
38
src/GameLogic/MuHelper/MuHelperZenCostCalculator.cs
Normal file
38
src/GameLogic/MuHelper/MuHelperZenCostCalculator.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
// <copyright file="MuHelperZenCostCalculator.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.MuHelper;
|
||||
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the Zen cost for the MU Helper and offline player based on the
|
||||
/// player's total level and the elapsed stage derived from the server configuration.
|
||||
/// </summary>
|
||||
public static class MuHelperZenCostCalculator
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates the Zen amount to charge for the current pay interval.
|
||||
/// </summary>
|
||||
/// <param name="player">The player being charged.</param>
|
||||
/// <param name="configuration">The MU helper server configuration.</param>
|
||||
/// <param name="startTimestamp">The timestamp when the session started, used to determine the current cost stage.</param>
|
||||
/// <returns>The Zen amount to deduct; 0 if the configuration has no cost entries.</returns>
|
||||
public static int Calculate(Player player, MuHelperConfiguration configuration, DateTime startTimestamp)
|
||||
{
|
||||
if (configuration.CostPerStage.Count == 0 || configuration.StageInterval <= TimeSpan.Zero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var elapsed = DateTime.UtcNow - startTimestamp;
|
||||
var currentStage = (int)(elapsed / configuration.StageInterval);
|
||||
currentStage = Math.Clamp(currentStage, 0, configuration.CostPerStage.Count - 1);
|
||||
|
||||
var costMultiplier = configuration.CostPerStage[currentStage];
|
||||
var totalLevel = player.Level + (int)(player.Attributes?[Stats.MasterLevel] ?? 0);
|
||||
|
||||
return costMultiplier * totalLevel;
|
||||
}
|
||||
}
|
||||
100
src/GameLogic/MuHelper/PartyRequestHandler.cs
Normal file
100
src/GameLogic/MuHelper/PartyRequestHandler.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
// <copyright file="PartyRequestHandler.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameLogic.MuHelper;
|
||||
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Handles auto-accepting incoming party requests from friends and guild members
|
||||
/// based on the player's MU Helper settings flags.
|
||||
/// </summary>
|
||||
public static class PartyRequestHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Automatically accepts a party request if the receiver has the relevant flag enabled.
|
||||
/// </summary>
|
||||
/// <param name="receiver">The player receiving the party request.</param>
|
||||
/// <param name="requester">The player who sent the party request.</param>
|
||||
/// <returns>True if the criteria matched (auto-accept was attempted, regardless of success); false if no criteria matched.</returns>
|
||||
public static async ValueTask<bool> TryAutoAcceptPartyRequestAsync(Player receiver, Player requester)
|
||||
{
|
||||
var settings = receiver.MuHelperSettings;
|
||||
if (settings is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.AutoAcceptGuild && AreGuildMembers(receiver, requester))
|
||||
{
|
||||
await AcceptPartyRequestAsync(receiver, requester).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (settings.AutoAcceptFriend && await AreFriendsAsync(receiver, requester).ConfigureAwait(false))
|
||||
{
|
||||
await AcceptPartyRequestAsync(receiver, requester).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool AreGuildMembers(Player receiver, Player requester)
|
||||
{
|
||||
return receiver.GuildStatus?.GuildId != null
|
||||
&& receiver.GuildStatus.GuildId == requester.GuildStatus?.GuildId;
|
||||
}
|
||||
|
||||
private static async ValueTask<bool> AreFriendsAsync(Player receiver, Player requester)
|
||||
{
|
||||
if (receiver.SelectedCharacter is null || requester.SelectedCharacter is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var friendServer = (receiver.GameContext as IGameServerContext)?.FriendServer;
|
||||
if (friendServer is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await friendServer.IsFriendAsync(receiver.SelectedCharacter.Name, requester.SelectedCharacter.Name).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async ValueTask<bool> AcceptPartyRequestAsync(Player receiver, Player requester)
|
||||
{
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
if (receiver.Party != null)
|
||||
{
|
||||
if (requester.Party == null)
|
||||
{
|
||||
// Receiver is the offline party master; add the solo requester to the existing party.
|
||||
success = await receiver.Party.AddAsync(requester).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else if (requester.Party != null)
|
||||
{
|
||||
// Requester already has a party; add the offline receiver to it.
|
||||
success = await requester.Party.AddAsync(receiver).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Neither side has a party; create a new one.
|
||||
var party = receiver.GameContext.PartyManager.CreateParty();
|
||||
success = await party.AddAsync(requester).ConfigureAwait(false)
|
||||
&& await party.AddAsync(receiver).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await receiver.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false);
|
||||
receiver.LastPartyRequester = null;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user