Follows CharCreate_AdjustAttribute (0xBAC140): a stat must stay at 1 or
more and can't be raised past the racial maximum. Checked after the rune
bonuses and max adjustments but before the rune penalties, since Giant's
Blood lowers the INT/DEX max by 10 but the stat by only 5.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rune count limit drops from 16 to the client's 10 slots, race and class
included (CharCreate_ApplyRune 0xBA8C80), and more than 8 talents is
refused (ArcCharacter_AddRune 0x4695D0).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Item.template is now the cache template from CObjectTemplate.lookup
instead of the legacy DB ItemTemplate, so item.template.cacheTemplate.X
becomes item.template.X. MobLoot, LootManager, ItemManager.isTwoHanded/
isShield and CombatManager.getSwingAnimation take a CObjectTemplate.
No value changes: every getter already read the cache template through
the bridge, and the lookup resolves the same 7586 ids as
static_item_templates with the same item_type (offline parity p17.py,
0 differences).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CombatManager shield checks, weapon skill/mastery lookups in
AbstractCharacter, armor defense and PlayerCharacter.validEquip read
getItemSkillUsed/getItemSkillMasteryUsed. Offline parity (p15d.py, 7586
rows): 0 mastery differences; skill used differs only where the legacy
loader blanked it.
The cache keeps "Cloth" (226 ARMOR templates), which the legacy loader
turned into "". Cloth is a placeholder in Skills.cfg that no character
has, so getArmorDefense now checks for it explicitly and still returns the
flat defense rating. No weapon uses Cloth and no power equip prereq names
it, so the other sites behave the same.
Value changes:
- 195340 Fortified Dragon Plate Helm of the Lion: skill used is the
unresolved hash "2624663" instead of "". Defense is still the flat
rating (no such skill), but players wearing it now hit the
"armor equipped without the necessary skill" error log.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Range, speed, damage list and swing anims now come from
engine.parsers.objects.ItemTemplate.getWeapon(templateID). Non-weapons and
missing templates get an empty block, matching the legacy zero defaults.
Offline parity (scripts/p15b.py, 811 WEAPON rows vs COBJECTS/ITEM): 0
differences in speed, range, projectile, idle anim, damage list or either
anim list. No duplicate damage types exist, so sum vs first is moot; first
entry is used as before. 26 weapons (ammo, lutes, no-stats crossbows) have an
empty damage list, same as the DB. No value changes. No comparator removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
item_defense_rating, item_bulk_factor and item_primary_attr are
Item-only fields, not on CObjectTemplate, so their consumers read
engine.parsers.objects.ItemTemplate._items by templateID, like the
RuneTemplate and DeedTemplate consumers. The primary attr checks
compare with == because the cache template leaves the attr null
(legacy: None) on non-weapons.
Parity (offline, scripts/p14.py, 7586 templates): 0 differences in
item_defense_rating, item_bulk_factor and item_primary_attr. Every DB
ARMOR and WEAPON template is in the ITEM folder, so both sides gate
these fields on the same templates. No value changes expected.
JSON-Refactor-Plan Task 1.4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every consumer of the legacy ItemTemplate item_type now reads
template.cacheTemplate.getItemType(). Left on the legacy field: the
type count in dbItemHandler.LOAD_ITEM_TEMPLATES, which runs before the
bridge is linked. The charter checks already read DeedTemplate.
Parity (offline, static_item_templates vs the template
CObjectIndex.lookup picks, scripts/p14.py, 7586 templates: ITEM 4011,
DEED 880, RUNE 2695): 0 differences in item_type. No value changes
expected.
JSON-Refactor-Plan Task 1.4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner death, logout or invisibility dismissed a commanded siege engine
by clearing its target and marking it dead. Released engines now revert
to MOBILE in place, stay alive and keep their current target so any
nation member can re-command them with a Rod of Command. Commanding an
engine no longer clears the target of the old or new engine.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>