From ad600ede6f0a4cac94715efb69950ba45ae40741 Mon Sep 17 00:00:00 2001 From: nolt Date: Wed, 17 Jun 2026 17:33:13 +0200 Subject: [PATCH] fix(npc): give Christine the General Goods Merchant a store Christine the General Goods Merchant (545) in the Loren Market has a merchant window but no MerchantStore, so talking to her opens an empty, non-working shop and the click can even fall through. Give her a clone of the general-goods (potion girl) store carried by Thompson the Merchant (231), in both places that set up game data: - the Season 6 NPC initialization, so freshly set up servers have it; - a configuration update (AddMissingMerchantStores), so existing servers get it without a reset. The update is idempotent and optional. Two other NPCs share the empty merchant window but are intentionally left out: Moss The Merchant (492) is the gambler and Market Union Member Julia (547) is a warp NPC, so a general-goods store fits neither of them. --- .../Updates/AddMissingMerchantStoresPlugIn.cs | 88 +++++++++++++++++++ .../Initialization/Updates/UpdateVersion.cs | 5 ++ .../VersionSeasonSix/NpcInitialization.cs | 1 + 3 files changed, 94 insertions(+) create mode 100644 src/Persistence/Initialization/Updates/AddMissingMerchantStoresPlugIn.cs diff --git a/src/Persistence/Initialization/Updates/AddMissingMerchantStoresPlugIn.cs b/src/Persistence/Initialization/Updates/AddMissingMerchantStoresPlugIn.cs new file mode 100644 index 000000000..e8efb1d73 --- /dev/null +++ b/src/Persistence/Initialization/Updates/AddMissingMerchantStoresPlugIn.cs @@ -0,0 +1,88 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Updates; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.PlugIns; + +/// +/// This update gives a merchant store to a merchant NPC which has a merchant window +/// but no items assigned, so talking to it opens an empty (non-working) shop. +/// +/// +/// Affected NPC: Christine the General Goods Merchant (545) in the Loren Market. She is given a +/// clone of the general-goods (potion girl) store carried by Thompson the Merchant (231). +/// Two other NPCs in that window state are intentionally left out: Moss The Merchant (492) is the +/// gambler and Market Union Member Julia (547) is a warp NPC, so a general-goods store would not +/// fit either of them. +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("f78d6e1d-1cb5-45f7-912d-54b2cb1220eb")] +public class AddMissingMerchantStoresPlugIn : UpdatePlugInBase +{ + /// + /// The plug in name. + /// + internal const string PlugInName = "Add missing merchant stores"; + + /// + /// The plug in description. + /// + internal const string PlugInDescription = "Gives a general-goods store to Christine the General Goods Merchant (545), which had a shop window but no items, cloned from Thompson the Merchant (231)."; + + /// + /// The number of the NPC whose store is cloned for the empty merchants. + /// + private const short StoreSourceNpcNumber = 231; // Thompson the Merchant - carries the general-goods (potion girl) store + + /// + /// The numbers of the NPCs which have a merchant window but no store. + /// + private static readonly short[] EmptyMerchantNpcNumbers = [545]; + + /// + public override UpdateVersion Version => UpdateVersion.AddMissingMerchantStores; + + /// + public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override bool IsMandatory => false; + + /// + public override DateTime CreatedAt => new(2026, 06, 17, 0, 0, 0, DateTimeKind.Utc); + + /// + protected override ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + var storeSource = gameConfiguration.Monsters.FirstOrDefault(m => m.Number == StoreSourceNpcNumber); + if (storeSource?.MerchantStore is null) + { + return default; + } + + foreach (var number in EmptyMerchantNpcNumbers) + { + var npc = gameConfiguration.Monsters.FirstOrDefault(m => m.Number == number); + if (npc is null || npc.MerchantStore is not null) + { + continue; + } + + npc.MerchantStore = storeSource.MerchantStore.Clone(gameConfiguration); + npc.MerchantStore.SetGuid(npc.Number); + } + + return default; + } +} diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index 3778ac172..735fa22f8 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -437,4 +437,9 @@ public enum UpdateVersion /// The version of the . /// AddMovementSpeedAttributesSeason6 = 86, + + /// + /// The version of the . + /// + AddMissingMerchantStores = 87, } diff --git a/src/Persistence/Initialization/VersionSeasonSix/NpcInitialization.cs b/src/Persistence/Initialization/VersionSeasonSix/NpcInitialization.cs index b4bcefd2f..d3a887639 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/NpcInitialization.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/NpcInitialization.cs @@ -941,6 +941,7 @@ public override void Initialize() def.Designation = "Christine the General Goods Merchant"; def.NpcWindow = NpcWindow.Merchant; def.ObjectKind = NpcObjectKind.PassiveNpc; + def.MerchantStore = this.CreatePotionGirlItemStorage(def.Number); def.SetGuid(def.Number); this.GameConfiguration.Monsters.Add(def); }