From d85056ce9b3aef3836fbffd69215acfe5a2f4ea1 Mon Sep 17 00:00:00 2001 From: nolt Date: Thu, 18 Jun 2026 00:34:11 +0200 Subject: [PATCH] Seed default potion hotkeys for new characters A newly created character received an all-zero KeyConfiguration. The game client maps the four potion quick-slots (Q/W/E/R) from that blob, and a zero value binds the apple (potion group offset 0, which the client treats as a healing item) to every slot, so all four act as a health potion. Seed a sensible default instead: Q -> healing potion, W -> mana potion, E and R unbound. --- .../Character/CreateCharacterAction.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/GameLogic/PlayerActions/Character/CreateCharacterAction.cs b/src/GameLogic/PlayerActions/Character/CreateCharacterAction.cs index a90a5f13f..a24c4cb23 100644 --- a/src/GameLogic/PlayerActions/Character/CreateCharacterAction.cs +++ b/src/GameLogic/PlayerActions/Character/CreateCharacterAction.cs @@ -43,6 +43,32 @@ public async ValueTask CreateCharacterAsync(Player player, string characterName, await player.InvokeViewPlugInAsync(p => p.ShowCharacterCreationFailedAsync()).ConfigureAwait(false); } + /// + /// Creates the default key configuration for a newly created character. + /// + /// The default key configuration. + /// + /// The key configuration is an opaque blob which is interpreted by the game client. Within it, + /// the potion quick-slots Q, W, E and R are stored as offsets into the potion item group, at + /// byte indices 21 (Q), 22 (W), 23 (E) and 25 (R). We bind Q to the healing potion and W to the + /// mana potion; E and R stay unbound. An all-zero configuration would otherwise make the client + /// bind offset 0 (the apple, which it treats as a healing item) to all four slots, so each one + /// would act as a health potion. + /// + private static byte[] CreateDefaultKeyConfiguration() + { + const byte healingPotion = 1; + const byte manaPotion = 4; + const byte unbound = 0xFF; + + var keyConfiguration = new byte[30]; + keyConfiguration[21] = healingPotion; // Q + keyConfiguration[22] = manaPotion; // W + keyConfiguration[23] = unbound; // E + keyConfiguration[25] = unbound; // R + return keyConfiguration; + } + private async ValueTask CreateCharacterAsync(Player player, string name, CharacterClass characterClass) { var account = player.Account; @@ -76,7 +102,7 @@ public async ValueTask CreateCharacterAsync(Player player, string characterName, character.Name = name; character.CharacterSlot = freeSlot.Value; character.CreateDate = DateTime.UtcNow; - character.KeyConfiguration = new byte[30]; + character.KeyConfiguration = CreateDefaultKeyConfiguration(); var attributes = character.CharacterClass.StatAttributes.Select(a => player.PersistenceContext.CreateNew(a.Attribute, a.BaseValue)).ToList(); attributes.ForEach(character.Attributes.Add); character.CurrentMap = characterClass.HomeMap;