Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement NPC quest dialog and related packet handlers #170

Merged
merged 11 commits into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions EOLib/Domain/Interact/INPCInteractionNotifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using AutomaticTypeMapper;
using EOLib.IO;

namespace EOLib.Domain.Interact
{
public interface INPCInteractionNotifier
{
void NotifyInteractionFromNPC(NPCType npcType);
}

[AutoMappedType]
public class NoOpNPCInteractionNotifier : INPCInteractionNotifier
{
public void NotifyInteractionFromNPC(NPCType npcType) { }
}
}
27 changes: 23 additions & 4 deletions EOLib/Domain/Interact/MapNPCActions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using AutomaticTypeMapper;
using EOLib.Domain.NPC;
using EOLib.IO.Repositories;
using EOLib.Net;
using EOLib.Net.Communication;

Expand All @@ -8,16 +10,31 @@ namespace EOLib.Domain.Interact
public class MapNPCActions : IMapNPCActions
{
private readonly IPacketSendService _packetSendService;
private readonly IENFFileProvider _enfFileProvider;

public MapNPCActions(IPacketSendService packetSendService)
public MapNPCActions(IPacketSendService packetSendService,
IENFFileProvider enfFileProvider)
{
_packetSendService = packetSendService;
_enfFileProvider = enfFileProvider;
}

public void RequestShop(byte index)
public void RequestShop(INPC npc)
{
var packet = new PacketBuilder(PacketFamily.Shop, PacketAction.Open)
.AddShort(index)
.AddShort(npc.Index)
.Build();

_packetSendService.SendPacket(packet);
}

public void RequestQuest(INPC npc)
{
var data = _enfFileProvider.ENFFile[npc.ID];

var packet = new PacketBuilder(PacketFamily.Quest, PacketAction.Use)
.AddShort(npc.Index)
.AddShort(data.VendorID)
.Build();

_packetSendService.SendPacket(packet);
Expand All @@ -26,6 +43,8 @@ public void RequestShop(byte index)

public interface IMapNPCActions
{
void RequestShop(byte index);
void RequestShop(INPC npc);

void RequestQuest(INPC npc);
}
}
8 changes: 8 additions & 0 deletions EOLib/Domain/Interact/Quest/DialogReply.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace EOLib.Domain.Interact.Quest
{
public enum DialogReply
{
Ok = 1,
Link
}
}
15 changes: 15 additions & 0 deletions EOLib/Domain/Interact/Quest/IStatusLabelNotifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using AutomaticTypeMapper;

namespace EOLib.Domain.Interact.Quest
{
public interface IStatusLabelNotifier
{
void ShowWarning(string message);
}

[AutoMappedType]
public class NoOpStatusLabelNotifier : IStatusLabelNotifier
{
public void ShowWarning(string message) { }
}
}
44 changes: 44 additions & 0 deletions EOLib/Domain/Interact/Quest/QuestActions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using AutomaticTypeMapper;
using EOLib.Net;
using EOLib.Net.Communication;

namespace EOLib.Domain.Interact.Quest
{
[AutoMappedType]
public class QuestActions : IQuestActions
{
private readonly IPacketSendService _packetSendService;
private readonly IQuestDataProvider _questDataProvider;

public QuestActions(IPacketSendService packetSendService,
IQuestDataProvider questDataProvider)
{
_packetSendService = packetSendService;
_questDataProvider = questDataProvider;
}

public void RespondToQuestDialog(DialogReply reply, byte linkId = 0)
{
_questDataProvider.QuestDialogData.MatchSome(data =>
{
var builder = new PacketBuilder(PacketFamily.Quest, PacketAction.Accept)
.AddShort(data.SessionID) // ignored by eoserv
.AddShort(data.DialogID) // ignored by eoserv
.AddShort(data.QuestID)
.AddShort(data.VendorID) // ignored by eoserv
.AddChar((byte)reply);

if (reply == DialogReply.Link)
builder = builder.AddChar(linkId);

var packet = builder.Build();
_packetSendService.SendPacket(packet);
});
}
}

public interface IQuestActions
{
void RespondToQuestDialog(DialogReply reply, byte linkId = 0);
}
}
38 changes: 38 additions & 0 deletions EOLib/Domain/Interact/Quest/QuestDataRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using AutomaticTypeMapper;
using EOLib.Domain.NPC;
using Optional;

namespace EOLib.Domain.Interact.Quest
{
public interface IQuestDataRepository : IResettable
{
INPC RequestedNPC { get; set; }

Option<IQuestDialogData> QuestDialogData { get; set; }
}

public interface IQuestDataProvider : IResettable
{
INPC RequestedNPC { get; }

Option<IQuestDialogData> QuestDialogData { get; }
}

[AutoMappedType(IsSingleton = true)]
public class QuestDataRepository : IQuestDataProvider, IQuestDataRepository
{
public INPC RequestedNPC { get; set; }

public Option<IQuestDialogData> QuestDialogData { get; set; }

public QuestDataRepository()
{
ResetState();
}

public void ResetState()
{
QuestDialogData = Option.None<IQuestDialogData>();
}
}
}
186 changes: 186 additions & 0 deletions EOLib/Domain/Interact/Quest/QuestDialogData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
using System.Collections.Generic;
using System.Linq;

namespace EOLib.Domain.Interact.Quest
{
public class QuestDialogData : IQuestDialogData
{
public short VendorID { get; private set; }

public short QuestID { get; private set; }

public short SessionID { get; private set; }

public short DialogID { get; private set; }

public IReadOnlyDictionary<short, string> DialogTitles { get; private set; }

public IReadOnlyList<string> PageText { get; private set; }

public IReadOnlyList<(short ActionID, string DisplayText)> Actions { get; private set; }

public QuestDialogData()
{
DialogTitles = new Dictionary<short, string>();
PageText = new List<string>();
Actions = new List<(short, string)>();
}

private QuestDialogData(short vendorID,
short questID,
short sessionID,
short dialogID,
IReadOnlyDictionary<short, string> dialogTitles,
IReadOnlyList<string> pageText,
IReadOnlyList<(short, string)> actions)
{
VendorID = vendorID;
QuestID = questID;
SessionID = sessionID;
DialogID = dialogID;
DialogTitles = dialogTitles;
PageText = pageText;
Actions = actions;
}

public IQuestDialogData WithVendorID(short vendorId)
{
var copy = MakeCopy(this);
copy.VendorID = vendorId;
return copy;
}

public IQuestDialogData WithQuestID(short questId)
{
var copy = MakeCopy(this);
copy.QuestID = questId;
return copy;
}

public IQuestDialogData WithSessionID(short sessionId)
{
var copy = MakeCopy(this);
copy.SessionID = sessionId;
return copy;
}

public IQuestDialogData WithDialogID(short dialogId)
{
var copy = MakeCopy(this);
copy.DialogID = dialogId;
return copy;
}

public IQuestDialogData WithDialogTitles(IReadOnlyDictionary<short, string> dialogTitles)
{
var copy = MakeCopy(this);
copy.DialogTitles = dialogTitles;
return copy;
}

public IQuestDialogData WithPageText(IReadOnlyList<string> pageText)
{
var copy = MakeCopy(this);
copy.PageText = pageText;
return copy;
}

public IQuestDialogData WithActions(IReadOnlyList<(short, string)> actions)
{
var copy = MakeCopy(this);
copy.Actions = actions;
return copy;
}

private static QuestDialogData MakeCopy(IQuestDialogData other)
{
return new QuestDialogData(
other.VendorID,
other.QuestID,
other.SessionID,
other.DialogID,
other.DialogTitles.ToDictionary(k => k.Key, v => v.Value),
new List<string>(other.PageText),
new List<(short, string)>(other.Actions));
}

public override bool Equals(object obj)
{
var other = obj as QuestDialogData;
if (other == null) return false;

return other.VendorID == VendorID
&& other.QuestID == QuestID
&& other.SessionID == SessionID
&& other.DialogID == DialogID
&& other.DialogTitles.SequenceEqual(DialogTitles)
&& other.PageText.SequenceEqual(PageText)
&& other.Actions.SequenceEqual(Actions);
}

public override int GetHashCode()
{
int hashCode = 170256730;
hashCode = hashCode * -1521134295 + VendorID.GetHashCode();
hashCode = hashCode * -1521134295 + QuestID.GetHashCode();
hashCode = hashCode * -1521134295 + SessionID.GetHashCode();
hashCode = hashCode * -1521134295 + DialogID.GetHashCode();
hashCode = hashCode * -1521134295 + DialogTitles.Aggregate(170256730, (a, b) => a * -1521134295 + b.GetHashCode());
hashCode = hashCode * -1521134295 + PageText.Aggregate(170256730, (a, b) => a * -1521134295 + b.GetHashCode());
hashCode = hashCode * -1521134295 + Actions.Aggregate(170256730, (a, b) => a * -1521134295 + b.GetHashCode());
return hashCode;
}
}

public interface IQuestDialogData
{
/// <summary>
/// NPC Vendor ID (<see cref="EOLib.IO.Pub.ENFRecord.VendorID" />)
/// </summary>
short VendorID { get; }

/// <summary>
/// Quest ID, for the current dialog being shown (part of quest state in EO+ parser)
/// </summary>
short QuestID { get; }

/// <summary>
/// Session ID, not used by eoserv
/// </summary>
short SessionID { get; }

/// <summary>
/// Dialog ID, not used by eoserv
/// </summary>
short DialogID { get; }

/// <summary>
/// Quest dialog titles for the current character, keyed on <see cref="VendorID" />.
/// </summary>
IReadOnlyDictionary<short, string> DialogTitles { get; }

/// <summary>
/// Text for the quest dialog, one entry per page.
/// </summary>
IReadOnlyList<string> PageText { get; }

/// <summary>
/// Links for the quest dialog, only shown on the last page.
/// </summary>
IReadOnlyList<(short ActionID, string DisplayText)> Actions { get; }

IQuestDialogData WithVendorID(short vendorId);

IQuestDialogData WithQuestID(short questId);

IQuestDialogData WithSessionID(short sessionId);

IQuestDialogData WithDialogID(short dialogId);

IQuestDialogData WithDialogTitles(IReadOnlyDictionary<short, string> dialogTitles);

IQuestDialogData WithPageText(IReadOnlyList<string> pageText);

IQuestDialogData WithActions(IReadOnlyList<(short, string)> actions);
}
}
21 changes: 0 additions & 21 deletions EOLib/Net/API/Message.cs

This file was deleted.

1 change: 0 additions & 1 deletion EOLib/Net/API/PacketAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public PacketAPI(EOClient client)
_createChestMembers();
_createInitMembers();
_createLockerMembers();
_createMessageMembers();
_createMusicMembers();
_createPartyMembers();
_createNPCMembers();
Expand Down
Loading