baseline: OpenMU upstream b5a0961 (fresh source)
This commit is contained in:
30
src/GameServer/RemoteView/Quest/CharacterClassExtensions.cs
Normal file
30
src/GameServer/RemoteView/Quest/CharacterClassExtensions.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// <copyright file="CharacterClassExtensions.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="CharacterClass"/>.
|
||||
/// </summary>
|
||||
internal static class CharacterClassExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the base character class.
|
||||
/// </summary>
|
||||
/// <param name="characterClass">The character class.</param>
|
||||
/// <param name="gameConfiguration">The game configuration.</param>
|
||||
/// <returns>The base character class.</returns>
|
||||
internal static CharacterClass GetBaseClass(this CharacterClass characterClass, GameConfiguration gameConfiguration)
|
||||
{
|
||||
var previousClass = gameConfiguration.CharacterClasses.FirstOrDefault(c => c.NextGenerationClass == characterClass);
|
||||
if (previousClass is null)
|
||||
{
|
||||
return characterClass;
|
||||
}
|
||||
|
||||
return previousClass.GetBaseClass(gameConfiguration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// <copyright file="CurrentlyActiveQuestsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.GameServer.MessageHandler.Quests;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="ICurrentlyActiveQuestsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.CurrentlyActiveQuestsPlugIn_Name), Description = nameof(PlugInResources.CurrentlyActiveQuestsPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("9851157D-97CA-42F3-840C-8448D02B49A4")]
|
||||
[MinimumClient(5, 0, ClientLanguage.Invariant)]
|
||||
public class CurrentlyActiveQuestsPlugIn : ICurrentlyActiveQuestsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CurrentlyActiveQuestsPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public CurrentlyActiveQuestsPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowActiveQuestsAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.SelectedCharacter is not { } character)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
const int maxQuestsPerPacket = 62;
|
||||
var activeQuests = character.QuestStates
|
||||
.Where(state => state.Group != QuestConstants.LegacyQuestGroup && state.ActiveQuest != null)
|
||||
.Select(s => s.ActiveQuest!)
|
||||
.Take(maxQuestsPerPacket)
|
||||
.ToList();
|
||||
var size = QuestStateListRef.GetRequiredSize(activeQuests.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var message = new QuestStateListRef(span);
|
||||
byte i = 0;
|
||||
foreach (var activeQuest in activeQuests)
|
||||
{
|
||||
var questIdentification = message[i];
|
||||
questIdentification.Number = (ushort)activeQuest.Number;
|
||||
questIdentification.Group = (ushort)activeQuest.Group;
|
||||
i++;
|
||||
}
|
||||
|
||||
message.QuestCount = i;
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
32
src/GameServer/RemoteView/Quest/EnumExtensions.cs
Normal file
32
src/GameServer/RemoteView/Quest/EnumExtensions.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
// <copyright file="EnumExtensions.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions to convert enum values.
|
||||
/// </summary>
|
||||
public static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the specified quest reward type.
|
||||
/// </summary>
|
||||
/// <param name="questRewardType">Type of the quest reward.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static RewardType Convert(this QuestRewardType questRewardType)
|
||||
{
|
||||
return questRewardType switch
|
||||
{
|
||||
QuestRewardType.Item => RewardType.Item,
|
||||
QuestRewardType.Experience => RewardType.Experience,
|
||||
QuestRewardType.Money => RewardType.Money,
|
||||
QuestRewardType.GensAttribution => RewardType.GensContribution,
|
||||
QuestRewardType.Undefined => RewardType.None,
|
||||
_ => throw new ArgumentException($"Unknown reward type {questRewardType}."),
|
||||
};
|
||||
}
|
||||
}
|
||||
73
src/GameServer/RemoteView/Quest/LegacyQuestRewardPlugIn.cs
Normal file
73
src/GameServer/RemoteView/Quest/LegacyQuestRewardPlugIn.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
// <copyright file="LegacyQuestRewardPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.PlayerActions.Quests;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.GameServer.MessageHandler.Quests;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="ILegacyQuestRewardPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.LegacyQuestRewardPlugIn_Name), Description = nameof(PlugInResources.LegacyQuestRewardPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("53BEDA2A-9FE6-406D-AE6D-8E4DDDA3D73D")]
|
||||
public class LegacyQuestRewardPlugIn : ILegacyQuestRewardPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LegacyQuestRewardPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public LegacyQuestRewardPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowAsync(Player receiver, QuestRewardType reward, int value, AttributeDefinition? attributeReward)
|
||||
{
|
||||
var receiverId = receiver.GetId(this._player);
|
||||
switch (reward)
|
||||
{
|
||||
case QuestRewardType.LevelUpPoints:
|
||||
await this._player.Connection.SendLegacyQuestRewardAsync(receiverId, LegacyQuestReward.QuestRewardType.LevelUpPoints, (byte)value).ConfigureAwait(false);
|
||||
break;
|
||||
case QuestRewardType.CharacterEvolutionFirstToSecond:
|
||||
await this._player.Connection.SendLegacyQuestRewardAsync(receiverId, LegacyQuestReward.QuestRewardType.CharacterEvolutionFirstToSecond, (byte)(this._player.SelectedCharacter!.CharacterClass!.Number << 3)).ConfigureAwait(false);
|
||||
break;
|
||||
case QuestRewardType.CharacterEvolutionSecondToThird:
|
||||
await this._player.Connection.SendLegacyQuestRewardAsync(receiverId, LegacyQuestReward.QuestRewardType.CharacterEvolutionSecondToThird, (byte)(this._player.SelectedCharacter!.CharacterClass!.Number << 3)).ConfigureAwait(false);
|
||||
break;
|
||||
case QuestRewardType.Attribute:
|
||||
if (attributeReward == Stats.IsSkillComboAvailable)
|
||||
{
|
||||
await this._player.Connection.SendLegacyQuestRewardAsync(receiverId, LegacyQuestReward.QuestRewardType.ComboSkill, 0).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (attributeReward == Stats.PointsPerLevelUp)
|
||||
{
|
||||
var questLevel = this._player.GetQuestState(QuestConstants.LegacyQuestGroup)?.ActiveQuest?.MinimumCharacterLevel ?? 220;
|
||||
var points = (this._player.Level - questLevel) * value;
|
||||
await this._player.Connection.SendLegacyQuestRewardAsync(receiverId, LegacyQuestReward.QuestRewardType.LevelUpPointsPerLevelIncrease, (byte)points).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
this._player.Logger.LogDebug("Unknown quest reward: {0}", reward);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// <copyright file="LegacyQuestStateDialogPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.GameServer.MessageHandler.Quests;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="ILegacyQuestStateDialogPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.LegacyQuestStateDialogPlugIn_Name), Description = nameof(PlugInResources.LegacyQuestStateDialogPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("F43DA7A9-1ACC-4FBB-9E15-8BE977F4CAF9")]
|
||||
public class LegacyQuestStateDialogPlugIn : ILegacyQuestStateDialogPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LegacyQuestStateDialogPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public LegacyQuestStateDialogPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowAsync()
|
||||
{
|
||||
if (this._player.Connection is not { } connection || this._player.SelectedCharacter is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var questState = this._player.SelectedCharacter.QuestStates.FirstOrDefault(s => s.Group == QuestConstants.LegacyQuestGroup);
|
||||
var quest = questState?.ActiveQuest ?? this._player.GetNextLegacyQuest();
|
||||
await connection.SendLegacyQuestStateDialogAsync((byte)(quest?.Number ?? 0), this._player.GetLegacyQuestStateByte()).ConfigureAwait(false);
|
||||
|
||||
if (quest?.RequiredMonsterKills.Any() ?? false)
|
||||
{
|
||||
int Write()
|
||||
{
|
||||
var size = LegacyQuestMonsterKillInfoRef.Length;
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new LegacyQuestMonsterKillInfoRef(span);
|
||||
packet.QuestIndex = (byte)quest.Number;
|
||||
int i = 0;
|
||||
foreach (var requirement in quest.RequiredMonsterKills)
|
||||
{
|
||||
var monsterState = packet[i];
|
||||
monsterState.MonsterNumber = (uint)requirement.Monster!.Number;
|
||||
monsterState.KillCount = (uint)(questState?.RequirementStates.FirstOrDefault(r => r.Requirement == requirement)?.KillCount ?? 0);
|
||||
i++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/GameServer/RemoteView/Quest/QuestCancelledPlugIn.cs
Normal file
44
src/GameServer/RemoteView/Quest/QuestCancelledPlugIn.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// <copyright file="QuestCancelledPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.GameServer.MessageHandler.Quests;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IQuestCancelledPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.QuestCancelledPlugIn_Name), Description = nameof(PlugInResources.QuestCancelledPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("733C8E1A-A663-4804-96E3-1EA955438970")]
|
||||
public class QuestCancelledPlugIn : IQuestCancelledPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestCancelledPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public QuestCancelledPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask QuestCancelledAsync(QuestDefinition quest)
|
||||
{
|
||||
if (quest.Group == QuestConstants.LegacyQuestGroup)
|
||||
{
|
||||
this._player.SelectedCharacter?.QuestStates.FirstOrDefault(q => q.Group == QuestConstants.LegacyQuestGroup)?.SendLegacyQuestStateAsync(this._player);
|
||||
return;
|
||||
}
|
||||
|
||||
await this._player.Connection.SendQuestCancelledAsync((ushort)quest.Number, (ushort)quest.Group).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// <copyright file="QuestCompletionResponsePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.GameServer.MessageHandler.Quests;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <seealso cref="IQuestCompletionResponsePlugIn" /> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.QuestCompletionResponsePlugIn_Name), Description = nameof(PlugInResources.QuestCompletionResponsePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("4BCA9C67-A695-4244-9F5A-3B7CAC049DB4")]
|
||||
public class QuestCompletionResponsePlugIn : IQuestCompletionResponsePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestCompletionResponsePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public QuestCompletionResponsePlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask QuestCompletedAsync(QuestDefinition quest)
|
||||
{
|
||||
if (quest.Group == QuestConstants.LegacyQuestGroup)
|
||||
{
|
||||
await this._player.Connection.SendLegacySetQuestStateResponseAsync((byte)quest.Number, 0, this._player.GetLegacyQuestStateByte()).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await this._player.Connection.SendQuestCompletionResponseAsync((ushort)quest.Number, (ushort)quest.Group, true).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/GameServer/RemoteView/Quest/QuestEventResponsePlugIn.cs
Normal file
59
src/GameServer/RemoteView/Quest/QuestEventResponsePlugIn.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
// <copyright file="QuestEventResponsePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IQuestEventResponsePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.QuestEventResponsePlugIn_Name), Description = nameof(PlugInResources.QuestEventResponsePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("5EAE1634-9589-4DEB-AEBA-93D0AC8AC5DF")]
|
||||
public class QuestEventResponsePlugIn : IQuestEventResponsePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestEventResponsePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public QuestEventResponsePlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowActiveEventQuestsAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Hints: * This is only sent for new characters (level <= 1, no MG or DL).
|
||||
// * I also found a struct which contains the following fields (each 2 bytes: NpcNumber (0), Count (1), QuestGroup (1), QuestNumber (0)).
|
||||
// * Always: C1 0C F6 03 00 00 01 00 00 00 01 00
|
||||
int Write()
|
||||
{
|
||||
var size = QuestEventResponseRef.Length;
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new QuestEventResponseRef(span);
|
||||
var vanertGroup = packet[0];
|
||||
vanertGroup.Number = 1;
|
||||
|
||||
var duprianGroup = packet[1];
|
||||
duprianGroup.Number = 1;
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// <copyright file="QuestProgressExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IQuestProgressPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.QuestProgressExtendedPlugIn_Name), Description = nameof(PlugInResources.QuestProgressExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("30446944-29C6-48EE-A62E-3B724E7E444D")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class QuestProgressExtendedPlugIn : IQuestProgressPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestProgressExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public QuestProgressExtendedPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowQuestProgressAsync(QuestDefinition quest, bool wasProgressionRequested)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = QuestProgressExtendedRef.Length;
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
_ = new QuestProgressExtendedRef(span)
|
||||
{
|
||||
QuestGroup = (ushort)quest.Group,
|
||||
};
|
||||
|
||||
var questState = this._player.SelectedCharacter?.QuestStates.FirstOrDefault(q => q.Group == quest.Group);
|
||||
|
||||
// to write the quest state into the message, we can use the same logic as for the QuestState. The messages are equal in their content.
|
||||
QuestStateExtendedRef progress = span;
|
||||
progress.AssignActiveQuestData(questState, this._player);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
62
src/GameServer/RemoteView/Quest/QuestProgressPlugIn.cs
Normal file
62
src/GameServer/RemoteView/Quest/QuestProgressPlugIn.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
// <copyright file="QuestProgressPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IQuestProgressPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.QuestProgressPlugIn_Name), Description = nameof(PlugInResources.QuestProgressPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("5D2B7F90-FEFA-4889-B339-D64512471613")]
|
||||
public class QuestProgressPlugIn : IQuestProgressPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestProgressPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public QuestProgressPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowQuestProgressAsync(QuestDefinition quest, bool wasProgressionRequested)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = QuestProgressRef.Length;
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new QuestProgressRef(span)
|
||||
{
|
||||
QuestGroup = (ushort)quest.Group,
|
||||
};
|
||||
|
||||
var questState = this._player.SelectedCharacter?.QuestStates.FirstOrDefault(q => q.Group == quest.Group);
|
||||
|
||||
// to write the quest state into the message, we can use the same logic as for the QuestState. The messages are equal in their content.
|
||||
QuestStateRef progress = span;
|
||||
progress.AssignActiveQuestData(questState, this._player);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
57
src/GameServer/RemoteView/Quest/QuestStartedPlugIn.cs
Normal file
57
src/GameServer/RemoteView/Quest/QuestStartedPlugIn.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
// <copyright file="QuestStartedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.PlayerActions.Quests;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.GameServer.MessageHandler.Quests;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IQuestStartedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.QuestStartedPlugIn_Name), Description = nameof(PlugInResources.QuestStartedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("2D813203-8266-4BB7-A267-E476AD11AC4B")]
|
||||
public class QuestStartedPlugIn : IQuestStartedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestStartedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public QuestStartedPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask QuestStartedAsync(QuestDefinition quest)
|
||||
{
|
||||
if (this._player.Connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (quest.Group == QuestConstants.LegacyQuestGroup)
|
||||
{
|
||||
await this._player.Connection.SendLegacySetQuestStateResponseAsync((byte)quest.Number, 0, this._player.GetLegacyQuestStateByte()).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var state = this._player.GetQuestState(quest.Group);
|
||||
if (state?.ActiveQuest == quest)
|
||||
{
|
||||
await this._player.Connection.SendQuestStepInfoAsync((ushort)quest.Number, (ushort)quest.Group).ConfigureAwait(false);
|
||||
await this._player.InvokeViewPlugInAsync<IQuestProgressPlugIn>(p => p.ShowQuestProgressAsync(quest, true)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
src/GameServer/RemoteView/Quest/QuestStateExtensions.cs
Normal file
92
src/GameServer/RemoteView/Quest/QuestStateExtensions.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
// <copyright file="QuestStateExtensions.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.PlayerActions.Quests;
|
||||
using MUnique.OpenMU.GameServer.MessageHandler.Quests;
|
||||
using MUnique.OpenMU.Network.Packets;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions regarding the quest state of a player.
|
||||
/// </summary>
|
||||
internal static class QuestStateExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the quest state byte for the current quest state of the legacy quest group.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
/// <returns>The quest state byte for the current quest state of the legacy quest group.</returns>
|
||||
/// <remarks>
|
||||
/// Coming to this solution was pure brainfuck!.
|
||||
/// </remarks>
|
||||
public static byte GetLegacyQuestStateByte(this Player player)
|
||||
{
|
||||
var legacyQuestState = player.GetQuestState(QuestConstants.LegacyQuestGroup);
|
||||
|
||||
// The first case is the easiest:
|
||||
if (legacyQuestState is null)
|
||||
{
|
||||
// In this case, all Quests are inactive, including the starting one
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
// The next one is obvious, too:
|
||||
if (legacyQuestState.LastFinishedQuest is null && legacyQuestState.ActiveQuest is null)
|
||||
{
|
||||
// In this case, it depends if we have an active quest
|
||||
return (byte)(0b1111_1100 | (legacyQuestState.ActiveQuest is { }
|
||||
? (byte)LegacyQuestState.Active
|
||||
: (byte)LegacyQuestState.Inactive));
|
||||
}
|
||||
|
||||
var quest = legacyQuestState.ActiveQuest ?? player.GetNextLegacyQuest() ?? legacyQuestState.LastFinishedQuest;
|
||||
if (quest is null)
|
||||
{
|
||||
// Should never happen.
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
var startOffset = (quest.Number / 4) * 4; // 4 quests per byte
|
||||
var result = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
var shift = i * 2;
|
||||
if ((legacyQuestState.LastFinishedQuest?.Number >= i + startOffset)
|
||||
|| (legacyQuestState.ActiveQuest?.Number > i + startOffset))
|
||||
{
|
||||
// This case is a bit simplified. There may be previous quests which are not meant for the character class.
|
||||
// For the sake of simplicity, we leave it like that, for now.
|
||||
result |= (byte)LegacyQuestState.Complete << shift;
|
||||
}
|
||||
else if (legacyQuestState.ActiveQuest == quest && quest.Number == i + startOffset)
|
||||
{
|
||||
result |= (byte)LegacyQuestState.Active << shift;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is also simplified. This quest may not exist,
|
||||
// then we wouldn't OR a new state, and just leave these bits as 0.
|
||||
result |= (byte)LegacyQuestState.Inactive << shift;
|
||||
}
|
||||
}
|
||||
|
||||
return (byte)result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next legacy quest of the currently opened NPC.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
/// <returns>The next legacy quest of the currently opened NPC.</returns>
|
||||
public static QuestDefinition? GetNextLegacyQuest(this Player player)
|
||||
{
|
||||
var legacyQuestState = player.GetQuestState(QuestConstants.LegacyQuestGroup);
|
||||
return player.GetAvailableQuestsOfOpenedNpc()
|
||||
.OrderBy(quest => quest.Number)
|
||||
.FirstOrDefault(quest => quest.Number > (legacyQuestState?.LastFinishedQuest?.Number ?? -1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// <copyright file="QuestStateResponseExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IQuestStateResponsePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.QuestStateResponseExtendedPlugIn_Name), Description = nameof(PlugInResources.QuestStateResponseExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("5EA4F6F0-BF20-48AD-B491-11E707052E95")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class QuestStateResponseExtendedPlugIn : QuestStateResponsePlugIn, IQuestStateResponsePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestStateResponseExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public QuestStateResponseExtendedPlugIn(RemotePlayer player)
|
||||
: base(player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ShowNewQuestStateAsync(CharacterQuestState questState)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = QuestStateExtendedRef.Length;
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new QuestStateExtendedRef(span)
|
||||
{
|
||||
QuestGroup = (ushort)questState.Group,
|
||||
};
|
||||
|
||||
packet.AssignActiveQuestData(questState, this._player);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
82
src/GameServer/RemoteView/Quest/QuestStateResponsePlugIn.cs
Normal file
82
src/GameServer/RemoteView/Quest/QuestStateResponsePlugIn.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
// <copyright file="QuestStateResponsePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.GameServer.MessageHandler.Quests;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IQuestStateResponsePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.QuestStateResponsePlugIn_Name), Description = nameof(PlugInResources.QuestStateResponsePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("BBE63528-67DC-4D5F-8C9D-D09AB488CC55")]
|
||||
[MinimumClient(0, 90, ClientLanguage.Invariant)]
|
||||
public class QuestStateResponsePlugIn : IQuestStateResponsePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestStateResponsePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public QuestStateResponsePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowQuestStateAsync(CharacterQuestState? questState)
|
||||
{
|
||||
if (questState is null || questState.Group == QuestConstants.LegacyQuestGroup)
|
||||
{
|
||||
await this.ShowLegacyQuestStateAsync(this._player.SelectedCharacter?.QuestStates.FirstOrDefault(state => state.Group == QuestConstants.LegacyQuestGroup)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.ShowNewQuestStateAsync(questState).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the legacy quest state.
|
||||
/// </summary>
|
||||
/// <param name="questState">State of the quest.</param>
|
||||
protected virtual async ValueTask ShowLegacyQuestStateAsync(CharacterQuestState? questState)
|
||||
{
|
||||
await questState.SendLegacyQuestStateAsync(this._player).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the quest state of the new quest system.
|
||||
/// </summary>
|
||||
/// <param name="questState">State of the quest.</param>
|
||||
protected virtual async ValueTask ShowNewQuestStateAsync(CharacterQuestState questState)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = QuestStateRef.Length;
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new QuestStateRef(span)
|
||||
{
|
||||
QuestGroup = (ushort)questState.Group,
|
||||
};
|
||||
|
||||
packet.AssignActiveQuestData(questState, this._player);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
36
src/GameServer/RemoteView/Quest/QuestStepInfoPlugIn.cs
Normal file
36
src/GameServer/RemoteView/Quest/QuestStepInfoPlugIn.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// <copyright file="QuestStepInfoPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IQuestStepInfoPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.QuestStepInfoPlugIn_Name), Description = nameof(PlugInResources.QuestStepInfoPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("099AACF3-2933-486C-83DE-01A340DACAA6")]
|
||||
public class QuestStepInfoPlugIn : IQuestStepInfoPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuestStepInfoPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public QuestStepInfoPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowQuestStepInfoAsync(short questGroup, short stepNumber)
|
||||
{
|
||||
await this._player.Connection.SendQuestStepInfoAsync((ushort)stepNumber, (ushort)questGroup).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
237
src/GameServer/RemoteView/Quest/QuestStructExtensions.cs
Normal file
237
src/GameServer/RemoteView/Quest/QuestStructExtensions.cs
Normal file
@@ -0,0 +1,237 @@
|
||||
// <copyright file="QuestStructExtensions.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Quests;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using QuestReward = MUnique.OpenMU.DataModel.Configuration.Quests.QuestReward;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for quest message structs.
|
||||
/// </summary>
|
||||
public static class QuestStructExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends the quest states of the legacy quest system.
|
||||
/// </summary>
|
||||
/// <param name="questState">State of the quest.</param>
|
||||
/// <param name="player">The player.</param>
|
||||
internal static async ValueTask SendLegacyQuestStateAsync(this CharacterQuestState? questState, RemotePlayer player)
|
||||
{
|
||||
var connection = player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var questCount = 7;
|
||||
int Write()
|
||||
{
|
||||
var size = LegacyQuestStateListRef.GetRequiredSize(questCount);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var message = new LegacyQuestStateListRef(span)
|
||||
{
|
||||
QuestCount = (byte)questCount,
|
||||
};
|
||||
|
||||
if (questState?.LastFinishedQuest != null)
|
||||
{
|
||||
for (int i = 0; i <= questState.LastFinishedQuest.Number; i++)
|
||||
{
|
||||
message[i] = LegacyQuestState.Complete;
|
||||
}
|
||||
}
|
||||
|
||||
if (questState?.ActiveQuest != null)
|
||||
{
|
||||
message[questState.ActiveQuest.Number] = LegacyQuestState.Active;
|
||||
}
|
||||
|
||||
if (player.SelectedCharacter?.CharacterClass?.GetBaseClass(player.GameContext.Configuration).Number != (byte)CharacterClassNumber.DarkKnight)
|
||||
{
|
||||
message.SecretOfDarkStoneState = LegacyQuestState.Undefined;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns the <paramref name="questState"/> to this <paramref name="message"/>.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="questState">State of the quest.</param>
|
||||
/// <param name="player">The player.</param>
|
||||
internal static void AssignActiveQuestData(this QuestStateRef message, CharacterQuestState? questState, RemotePlayer player)
|
||||
{
|
||||
var activeQuest = questState?.ActiveQuest;
|
||||
if (activeQuest is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
message.QuestNumber = (ushort)activeQuest.Number;
|
||||
|
||||
var itemSerializer = player.ItemSerializer;
|
||||
if (activeQuest.RequiredItems.Any())
|
||||
{
|
||||
message.ConditionCount = (byte)activeQuest.RequiredItems.Count;
|
||||
int i = 0;
|
||||
foreach (var requiredItem in activeQuest.RequiredItems)
|
||||
{
|
||||
requiredItem.AssignTo(message.GetQuestCondition(i), player);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (activeQuest.RequiredMonsterKills.Any())
|
||||
{
|
||||
message.ConditionCount = (byte)activeQuest.RequiredMonsterKills.Count;
|
||||
int i = 0;
|
||||
foreach (var requiredKill in activeQuest.RequiredMonsterKills)
|
||||
{
|
||||
requiredKill.AssignTo(message.GetQuestCondition(i), questState!);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no requirement states available
|
||||
}
|
||||
|
||||
int r = 0;
|
||||
foreach (var reward in activeQuest.Rewards)
|
||||
{
|
||||
reward.AssignTo(message.GetQuestReward(r), itemSerializer);
|
||||
r++;
|
||||
}
|
||||
|
||||
message.RewardCount = (byte)r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns the <paramref name="questState"/> to this <paramref name="message"/>.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="questState">State of the quest.</param>
|
||||
/// <param name="player">The player.</param>
|
||||
internal static void AssignActiveQuestData(this QuestStateExtendedRef message, CharacterQuestState? questState, RemotePlayer player)
|
||||
{
|
||||
var activeQuest = questState?.ActiveQuest;
|
||||
if (activeQuest is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
message.QuestNumber = (ushort)activeQuest.Number;
|
||||
|
||||
var itemSerializer = player.ItemSerializer;
|
||||
if (activeQuest.RequiredItems.Any())
|
||||
{
|
||||
message.ConditionCount = (byte)activeQuest.RequiredItems.Count;
|
||||
int i = 0;
|
||||
foreach (var requiredItem in activeQuest.RequiredItems)
|
||||
{
|
||||
requiredItem.AssignTo(message.GetQuestConditionExtended(i), player);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (activeQuest.RequiredMonsterKills.Any())
|
||||
{
|
||||
message.ConditionCount = (byte)activeQuest.RequiredMonsterKills.Count;
|
||||
int i = 0;
|
||||
foreach (var requiredKill in activeQuest.RequiredMonsterKills)
|
||||
{
|
||||
requiredKill.AssignTo(message.GetQuestConditionExtended(i), questState!);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no requirement states available
|
||||
}
|
||||
|
||||
int r = 0;
|
||||
foreach (var reward in activeQuest.Rewards)
|
||||
{
|
||||
reward.AssignTo(message.GetQuestRewardExtended(r), itemSerializer);
|
||||
r++;
|
||||
}
|
||||
|
||||
message.RewardCount = (byte)r;
|
||||
}
|
||||
|
||||
private static void AssignTo(this QuestItemRequirement itemRequirement, QuestConditionRef condition, RemotePlayer player)
|
||||
{
|
||||
condition.Type = ConditionType.Item;
|
||||
condition.RequiredCount = (uint)itemRequirement.MinimumNumber;
|
||||
condition.CurrentCount = (uint)(player.Inventory?.Items.Count(item => Equals(item.Definition, itemRequirement.Item)) ?? 0);
|
||||
condition.RequirementId = itemRequirement.Item!.GetItemType();
|
||||
var temporaryItem = new TemporaryItem { Definition = itemRequirement.Item };
|
||||
temporaryItem.Durability = temporaryItem.GetMaximumDurabilityOfOnePiece();
|
||||
player.ItemSerializer.SerializeItem(condition.RequiredItemData, temporaryItem);
|
||||
}
|
||||
|
||||
private static void AssignTo(this QuestItemRequirement itemRequirement, QuestConditionExtendedRef condition, RemotePlayer player)
|
||||
{
|
||||
condition.Type = ConditionType.Item;
|
||||
condition.RequiredCount = (uint)itemRequirement.MinimumNumber;
|
||||
condition.CurrentCount = (uint)(player.Inventory?.Items.Count(item => Equals(item.Definition, itemRequirement.Item)) ?? 0);
|
||||
condition.RequirementId = itemRequirement.Item!.GetItemType();
|
||||
var temporaryItem = new TemporaryItem { Definition = itemRequirement.Item };
|
||||
temporaryItem.Durability = temporaryItem.GetMaximumDurabilityOfOnePiece();
|
||||
player.ItemSerializer.SerializeItem(condition.RequiredItemData, temporaryItem);
|
||||
}
|
||||
|
||||
private static void AssignTo(this QuestMonsterKillRequirement killRequirement, QuestConditionRef condition, CharacterQuestState questState)
|
||||
{
|
||||
condition.Type = ConditionType.MonsterKills;
|
||||
condition.RequiredCount = (uint)killRequirement.MinimumNumber;
|
||||
condition.RequirementId = (ushort)killRequirement.Monster!.Number;
|
||||
condition.CurrentCount = (uint)(questState.RequirementStates.FirstOrDefault(s => s.Requirement != null && s.Requirement.Equals(killRequirement))?.KillCount ?? 0);
|
||||
}
|
||||
|
||||
private static void AssignTo(this QuestMonsterKillRequirement killRequirement, QuestConditionExtendedRef condition, CharacterQuestState questState)
|
||||
{
|
||||
condition.Type = ConditionType.MonsterKills;
|
||||
condition.RequiredCount = (uint)killRequirement.MinimumNumber;
|
||||
condition.RequirementId = (ushort)killRequirement.Monster!.Number;
|
||||
condition.CurrentCount = (uint)(questState.RequirementStates.FirstOrDefault(s => s.Requirement != null && s.Requirement.Equals(killRequirement))?.KillCount ?? 0);
|
||||
}
|
||||
|
||||
private static void AssignTo(this QuestReward questReward, Network.Packets.ServerToClient.QuestRewardRef rewardStruct, IItemSerializer itemSerializer)
|
||||
{
|
||||
rewardStruct.Type = questReward.RewardType.Convert();
|
||||
rewardStruct.RewardCount = (uint)questReward.Value;
|
||||
if (questReward.RewardType == QuestRewardType.Item && questReward.ItemReward is { } itemReward)
|
||||
{
|
||||
rewardStruct.RewardId = itemReward.Definition!.GetItemType();
|
||||
itemSerializer.SerializeItem(rewardStruct.RewardedItemData, itemReward);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssignTo(this QuestReward questReward, Network.Packets.ServerToClient.QuestRewardExtendedRef rewardStruct, IItemSerializer itemSerializer)
|
||||
{
|
||||
rewardStruct.Type = questReward.RewardType.Convert();
|
||||
rewardStruct.RewardCount = (uint)questReward.Value;
|
||||
if (questReward.RewardType == QuestRewardType.Item && questReward.ItemReward is { } itemReward)
|
||||
{
|
||||
rewardStruct.RewardId = itemReward.Definition!.GetItemType();
|
||||
itemSerializer.SerializeItem(rewardStruct.RewardedItemData, itemReward);
|
||||
}
|
||||
}
|
||||
|
||||
private static ushort GetItemType(this ItemDefinition item)
|
||||
{
|
||||
// since the original game server/client supports up to 512 items per group, we have to shift the group by 9 bytes instead of 8.
|
||||
return (ushort)((ushort)(item.Group << 9) | (ushort)item.Number);
|
||||
}
|
||||
}
|
||||
67
src/GameServer/RemoteView/Quest/ShowAvailableQuestsPlugIn.cs
Normal file
67
src/GameServer/RemoteView/Quest/ShowAvailableQuestsPlugIn.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
// <copyright file="ShowAvailableQuestsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Quest;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.PlayerActions.Quests;
|
||||
using MUnique.OpenMU.GameLogic.Views.Quest;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowAvailableQuestsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowAvailableQuestsPlugIn_Name), Description = nameof(PlugInResources.ShowAvailableQuestsPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("63C7B3E2-EF66-49BB-A9F8-EFBD2389588F")]
|
||||
public class ShowAvailableQuestsPlugIn : IShowAvailableQuestsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowAvailableQuestsPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowAvailableQuestsPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowAvailableQuestsAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.OpenedNpc is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var totalQuests = this._player.GetAvailableQuestsOfOpenedNpc().ToList();
|
||||
var questCount = totalQuests.Count;
|
||||
int Write()
|
||||
{
|
||||
var size = AvailableQuestsRef.GetRequiredSize(questCount);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new AvailableQuestsRef(span)
|
||||
{
|
||||
QuestCount = (ushort)questCount,
|
||||
QuestNpcNumber = (ushort)this._player.OpenedNpc.Definition.Number,
|
||||
};
|
||||
|
||||
for (int i = 0; i < questCount; i++)
|
||||
{
|
||||
var block = packet[i];
|
||||
var quest = totalQuests[i];
|
||||
block.Number = (ushort)quest.StartingNumber;
|
||||
block.Group = (ushort)quest.Group;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user