From bd57c2a3f935bb1ef15ab60b4dfc51f6cce2f820 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Thu, 22 Oct 2026 07:09:38 -0400 Subject: [PATCH] Refuse duplicate creation runes by id and talents sharing a client sub type Character creation now checks duplicates by rune id instead of relying on RuneBase object identity, and groups talents by rune_sub_type (RuneBase.getSubTypeToken) instead of the DB SubType byte. Creation talents are rank 0, so two in the same group block each other, as in ArcCharacter_AddRune (0x4695D0). New groups over the DB (scripts/p26b.py): - weapon proficiencies 250080-250087: only one at creation - Blood Gift: Blood of the Dragon, Giant's Blood, Taint of Madness, Taint of Chaos - Juggernaut + Tough Hide; Tireless + Tough as Nails - Childhood gains Witch Sight 250112; Dark Whispers and Divine Touch join the Background group Co-Authored-By: Claude Opus 5.5 --- src/engine/objects/PlayerCharacter.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/engine/objects/PlayerCharacter.java b/src/engine/objects/PlayerCharacter.java index b43de6bf..9203a28a 100644 --- a/src/engine/objects/PlayerCharacter.java +++ b/src/engine/objects/PlayerCharacter.java @@ -866,7 +866,8 @@ public class PlayerCharacter extends AbstractCharacter { int[] characterRunes = msg.getRunes(); - HashSet usedRunesSubType = new HashSet<>(); + HashSet usedRuneIDs = new HashSet<>(); + HashSet usedSubTypeTokens = new HashSet<>(); HashSet usedRunes = new HashSet<>(); // So that all the penalties can be added at the end. @@ -922,24 +923,19 @@ public class PlayerCharacter extends AbstractCharacter { return null; } - int previous_size = usedRunes.size(); - int previous_subtype = usedRunesSubType.size(); - - usedRunes.add(runeBase); - usedRunesSubType.add(runeBase.getSubtype()); - - // Duplicate Rune check - if (usedRunes.size() <= previous_size) { + if (!usedRuneIDs.add(runeBase.getObjectUUID())) { Logger.info("Duplicate RuneBase"); return null; } - // Duplicate Subtype check - if (runeBase.getSubtype() != 0 && usedRunesSubType.size() <= previous_subtype) { + // Creation talents are rank 0, so two of the same sub type block each other (client 0x4695D0) + if (runeBase.getSubTypeToken() != 0 && !usedSubTypeTokens.add(runeBase.getSubTypeToken())) { Logger.info("Duplicate RuneBase Subtype"); return null; } + usedRunes.add(runeBase); + int maxValue = 0; // Every attempt is made to load MIN_NEEDED_ATTRIBUTES first.