Skip to content
Open
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
75 changes: 75 additions & 0 deletions src/GameServer/MessageHandler/EnterMarketPlaceHandlerPlugIn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// <copyright file="EnterMarketPlaceHandlerPlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.GameServer.MessageHandler;

using System.Runtime.InteropServices;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.GameServer.MessageHandler.MuHelper;
using MUnique.OpenMU.Network.Packets.ClientToServer;
using MUnique.OpenMU.PlugIns;

/// <summary>
/// Handler for the <see cref="EnterMarketPlaceRequest"/>, sent by the 'Warp' button of the
/// Market Union Member Julia window. It warps the player between Lorencia and the Loren Market,
/// depending on which side the player is currently on.
/// </summary>
[PlugIn]
[Display(Name = PlugInName, Description = PlugInDescription)]
[Guid("5e028dcf-a5af-40a0-b958-2deb38aae4bc")]
[BelongsToGroup(MuHelperGroupHandler.GroupKey)]
internal class EnterMarketPlaceHandlerPlugIn : ISubPacketHandlerPlugIn
{
private const string PlugInName = "Enter Market Place Handler";

private const string PlugInDescription = "Handler which warps a player between Lorencia and the Loren Market when using the 'Warp' button of Market Union Member Julia.";

/// <summary>
/// The map number of the Loren Market.
/// </summary>
private const short LorenMarketMapNumber = 79;

/// <summary>
/// The map number of Lorencia, where the player is warped back to.
/// </summary>
private const short LorenciaMapNumber = 0;

/// <inheritdoc/>
public bool IsEncryptionExpected => false;

/// <inheritdoc/>
public byte Key => EnterMarketPlaceRequest.SubCode;

/// <inheritdoc/>
public async ValueTask HandlePacketAsync(Player player, Memory<byte> packet)
{
if (packet.Length < EnterMarketPlaceRequest.Length)
{
return;
}

// Only allow the warp through the actual Julia window, so a crafted packet can't be used
// as a free teleport from anywhere.
if (player.OpenedNpc?.Definition.NpcWindow != NpcWindow.JuliaWarpMarketServer)
{
return;
}

// Julia warps both ways: from the Loren Market back to Lorencia, and from anywhere else
// (her counterpart in Lorencia) into the Loren Market.
var targetMapNumber = player.CurrentMap?.Definition.Number == LorenMarketMapNumber
? LorenciaMapNumber
: LorenMarketMapNumber;

var targetMap = await player.GameContext.GetMapAsync((ushort)targetMapNumber).ConfigureAwait(false);
if (targetMap?.SafeZoneSpawnGate is not { } targetGate)
{
return;
}

player.OpenedNpc = null;
await player.WarpToAsync(targetGate).ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// <copyright file="AddLorenMarketJuliaWarpPlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.Persistence.Initialization.Updates;

using System.Runtime.InteropServices;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps;
using MUnique.OpenMU.PlugIns;

/// <summary>
/// This update turns Market Union Member Julia (547) into a working warp NPC for the Loren Market,
/// on both sides. It changes her NPC window so that talking to her opens the warp dialog instead of
/// an empty merchant shop, and spawns a second instance of her in Lorencia, which is the entrance.
/// </summary>
/// <remarks>
/// The actual warp is performed server-side by the <c>EnterMarketPlaceHandlerPlugIn</c> when the
/// player uses the 'Warp' button of the (client-side) Julia window.
/// </remarks>
[PlugIn]
[Display(Name = PlugInName, Description = PlugInDescription)]
[Guid("ed2f1728-b35c-4a3d-810e-eab5b6e12a82")]
public class AddLorenMarketJuliaWarpPlugIn : UpdatePlugInBase
{
/// <summary>
/// The plug in name.
/// </summary>
internal const string PlugInName = "Add Loren Market Julia warp";

/// <summary>
/// The plug in description.
/// </summary>
internal const string PlugInDescription = "Makes Market Union Member Julia (547) a working warp NPC between Lorencia and the Loren Market, on both sides, instead of an empty merchant.";

private const short JuliaNpcNumber = 547;

/// <inheritdoc />
public override UpdateVersion Version => UpdateVersion.AddLorenMarketJuliaWarp;

/// <inheritdoc />
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;

/// <inheritdoc />
public override string Name => PlugInName;

/// <inheritdoc />
public override string Description => PlugInDescription;

/// <inheritdoc />
public override bool IsMandatory => false;

/// <inheritdoc />
public override DateTime CreatedAt => new(2026, 06, 24, 0, 0, 0, DateTimeKind.Utc);

/// <inheritdoc />
protected override ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
{
var julia = gameConfiguration.Monsters.FirstOrDefault(m => m.Number == JuliaNpcNumber);
if (julia is null)
{
return default;
}

julia.NpcWindow = NpcWindow.JuliaWarpMarketServer;
julia.MerchantStore = null;

var lorencia = gameConfiguration.Maps.FirstOrDefault(m => m.Number == Lorencia.Number);
if (lorencia is null
|| lorencia.MonsterSpawns.Any(s => s.MonsterDefinition?.Number == JuliaNpcNumber))
{
return default;
}

var juliaSpawn = context.CreateNew<MonsterSpawnArea>();
lorencia.MonsterSpawns.Add(juliaSpawn);
juliaSpawn.SetGuid(JuliaNpcNumber);
juliaSpawn.GameMap = lorencia;
juliaSpawn.MonsterDefinition = julia;
juliaSpawn.SpawnTrigger = SpawnTrigger.Automatic;
juliaSpawn.Direction = Direction.SouthEast;
juliaSpawn.X1 = 139;
juliaSpawn.X2 = 139;
juliaSpawn.Y1 = 138;
juliaSpawn.Y2 = 138;

return default;
}
}
5 changes: 5 additions & 0 deletions src/Persistence/Initialization/Updates/UpdateVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -439,4 +439,9 @@ public enum UpdateVersion
/// The version of the <see cref="AddMovementSpeedAttributesPlugInSeason6"/>.
/// </summary>
AddMovementSpeedAttributesSeason6 = 86,

/// <summary>
/// The version of the <see cref="AddLorenMarketJuliaWarpPlugIn"/>.
/// </summary>
AddLorenMarketJuliaWarp = 87,
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ protected override IEnumerable<MonsterSpawnArea> CreateNpcSpawns()
yield return this.CreateMonsterSpawn(24, this.NpcDictionary[543], 141, 143, Direction.South);
yield return this.CreateMonsterSpawn(25, this.NpcDictionary[371], 130, 126, Direction.SouthEast);
yield return this.CreateMonsterSpawn(26, this.NpcDictionary[568], 131, 139, Direction.South, SpawnTrigger.Wandering); // Wandering Merchant Zyro
yield return this.CreateMonsterSpawn(27, this.NpcDictionary[547], 139, 138, Direction.SouthEast); // Market Union Member Julia (warps to the Loren Market)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,7 @@ public override void Initialize()
var def = this.Context.CreateNew<MonsterDefinition>();
def.Number = 547;
def.Designation = "Market Union Member Julia";
def.NpcWindow = NpcWindow.Merchant;
def.NpcWindow = NpcWindow.JuliaWarpMarketServer;
def.ObjectKind = NpcObjectKind.PassiveNpc;
def.SetGuid(def.Number);
this.GameConfiguration.Monsters.Add(def);
Expand Down