Commit Graph
3314 Commits
Author SHA1 Message Date
MagicBotandClaude Opus 5.5 1ac522a2e0 Fix /makeitem failing to persist items
MakeItemCmd left the container at NONE, which item_CREATE's cont
parameter rejects, so PERSIST returned null and addItemToInventory
threw a NullPointerException. Set the container to INVENTORY as
CreateItemCmd does, and report an error if PERSIST still fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:27:47 -04:00
MagicBotandClaude Opus 5.5 566870de79 Move the remaining legacy item template lookups to CObjectTemplate
Dev commands, BuildingManager, ChatManager, ForgeManager, NPCManager,
NPC, Warehouse, WorkOrder and ManageNPCMsg now call
CObjectTemplate.lookup instead of ItemTemplate.templates.get. Forge
production cost reads getItemResourceCosts(). Only the DB loader and
linkCacheTemplates still use the legacy class (removed in Task 1.8).

Removes the static_item_templates ParityCheck comparator. Final parity
(offline p17.py, 7586 rows): every id has a cache template, 0 item_type
differences, 0 item_resource_costs differences (84 ITEMs have costs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:56:08 -04:00
MagicBotandClaude Opus 5.5 6b4e9815e0 Switch Item.template and MobLoot to CObjectTemplate
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>
2026-10-20 07:55:28 -04:00
MagicBotandClaude Opus 5.5 f39e35ac23 Apply the dual-wield damage cut only when both hands hold weapons
The 30% cut never applied: each hand was passed itself as the other
hand, and the check compared the template to ItemType.WEAPON. The
client (0x466EB0 / 0x46D200) multiplies min and max damage by 0.7 when
both held slots contain an item_type WEAPON. Value change: players
wielding two weapons now deal 70% damage per hand; single weapon,
weapon + shield and unarmed are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:54:02 -04:00
MagicBotandClaude Opus 5.5 70ea9b3bc9 Move the vendor mod table off the legacy item template
LOAD_TEMPLATE_MODTABLES now returns a templateID -> modTable map that
ForgeManager owns (vendorModTables). ForgeManager.forgeItem, WorkOrder
validation and the ManageNPCMsg forge item list read it through
ForgeManager.getVendorModTable(), and ItemTemplate.modTable is removed.

Values are unchanged: static_vendor_items has 5089 rows, one per
templateID, all present in static_item_templates, modTable 0-30.
Templates not in the table still read 0. The old loader stopped at the
first templateID missing from ItemTemplate.templates; there are none.
No comparator added or removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:37:54 -04:00
MagicBotandClaude Opus 5.5 2b4c1a73d1 Read item user power actions from the cache template
The magical name color check and loadTemplateEnchantments read
getItemUserPowerAction(). The list keeps duplicates, so the baked-in
enchantments collapse a repeated power to its last entry, as the legacy
HashMap did; applying it twice would add its name to effectNames twice and
double its share of calcMagicValue.

Offline parity (p15e.py, 7586 rows): 0 differences in the lists (power and
arguments, in order). Duplicates exist only on 81 RUNE templates (mob,
guard and siege runes, plus discipline rune 3006 Blade Weaver with two
identical AMBIDEXTERITY entries); none on ITEM or DEED.

Value changes: none. Enchantments are now applied in file order instead
of HashMap order; effects are keyed by effect id and calcMagicValue is a
sum, so the result is the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:35:38 -04:00
MagicBotandClaude Opus 5.5 a37de9fc31 Read the item power grant from the cache template
The BUCKET/POTION/SCROLL use path takes the first item_power_grant entry
in file order from getItemPowerGrant() instead of the first key of the
legacy HashMap.

Offline parity (p15e.py, 7586 rows): 0 differences in the grant lists.
The legacy HashMap order picks a different power on 664 templates, all
RUNE folder runes with item_type RUNE, which take the RUNE case and never
reach this path. Every ITEM-folder grant (scrolls, potions, bucket, rod,
gift rings) has exactly one entry and no DEED has one, so the power and
value used are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:35:19 -04:00
MagicBotandClaude Opus 5.5 81d4ab7b99 Read item skill used and mastery from the cache template
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>
2026-10-19 14:29:01 -04:00
MagicBotandClaude Opus 5.5 7abc63ffc3 Read item skill requirements from the cache template
validForSkills, isShield, the guard attack rating and the NPC vendor skill
filter read getItemSkillReq. Offline parity (p15d.py, 7586 rows): 0
differences. No template lists "Cloth" in item_skill_req and none has more
than one entry, so file order vs HashMap order is moot.

Value changes: none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:28:32 -04:00
MagicBotandClaude Opus 5.5 5b3c511b55 Read item eq slots from the cache template
isTwoHanded, validForSlot, getSwingAnimation and the mob equipment set
read getItemEqSlotsOr/And, which drop the exporter's NONE. Offline parity
(p15c.py, 7586 rows): 0 differences once NONE is ignored.

Value changes:
- validForSlot: items whose only slot is NONE no longer pass for slot NONE.
- getSwingAnimation: the 7 slotless weapons (Rock 19297, Arrow 25650,
  siege ammo 29000/29010/29020/29030/29350) return the default 75 instead
  of indexing their empty left-hand anim list.
- MobBase equipment sets skip slotless items instead of keying them under
  NONE. No EQUIP booty entry references one (the 104 sets that do are ITEM
  booty sets).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:23:41 -04:00
MagicBotandClaude Opus 5.5 73947f3dc8 Drop NONE from rune and deed eq slots
Matches ItemParser, so every CObjectTemplate returns an empty slot set for
the exporter's ["NONE"] mask. All 2695 RUNE and 880 DEED templates have
["NONE"] only; nothing reads these sets yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:22:57 -04:00
MagicBotandClaude Opus 5.5 c7d8483d55 Read item race/class/disc requirements from the cache template
ValidRace/ValidClass/ValidDiscipline use the single req set plus the
restrict flag (true = blacklist, false = whitelist) instead of the legacy
req/res split. Offline parity (p15c.py, 7586 rows): 0 differences in
restrict or set for race, class and disc.

Value changes: none for equip checks. The legacy disc blacklist branch was
inverted (passed only when a listed disc was held); the new check passes
when none is held. No ITEM has a disc blacklist (only 19 RUNEs), and
ValidDiscipline has no callers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:22:36 -04:00
MagicBotandClaude Opus 5.5 0688cf231f Fix isTwoHanded always returning false
EnumSet.contains(EnumSet) is never true; use containsAll so the 350
weapons with item_eq_slots_and {LHELD, RHELD} block the other hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:22:09 -04:00
MagicBotandClaude Opus 5.5 6b2f000434 Read the weapon block from the cache template item_weapon
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>
2026-10-19 14:17:24 -04:00
MagicBotandClaude Opus 5.5 441a935bd9 Read armor resists from the cache template EnumMap
Resists.getArmorResists and NPCManager.applyEquipmentResists now read
cacheTemplate.getCombatAttackResist() with DamageType keys instead of the
legacy String-keyed map, and default a missing key to 0 instead of an
unboxing NPE.

Offline parity (scripts/p15a.py, all 7586 static_item_templates rows vs
the template CObjectIndex.lookup picks): 0 differences in the full
combat_attack_resist map. Every ITEM/DEED/RUNE template carries all 16
keys, so no value changes. No comparator added or removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:11:21 -04:00
MagicBotandClaude Opus 5.5 b63826a7b7 Import DeedTemplate in PlaceAssetMsgHandler
c7747467 used DeedTemplate._deeds in the deed placement check without
importing it, which broke the build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:52:22 -04:00
MagicBotandClaude Opus 5.5 2774d8a510 Read offering info and bane rank through the bridge
Shrine boons read getItemOfferingInfo() and the banestone and bane
production paths read getItemBaneRank() on template.cacheTemplate.

The legacy parser only kept item_bane_rank when ACTIONRESPONSE is
4250517122; the cache parsers always read it. Parity shows this makes
no difference: every template outside that gate has bane rank 0.

Parity (offline, scripts/p14.py, 7586 templates): 0 differences in
item_offering_info and item_bane_rank. No value changes expected.

JSON-Refactor-Plan Task 1.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:46:14 -04:00
MagicBotandClaude Opus 5.5 9a10574e59 Read defense, bulk and primary attr from the cache ItemTemplate
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>
2026-10-19 12:45:59 -04:00
MagicBotandClaude Opus 5.5 d197f62c1f Read template health, charges and flags through the bridge
Consumers of the legacy ItemTemplate combat_health_full,
item_initial_charges and item_flags now read getCombatHealthFull(),
getItemInitialCharges() and getItemFlags() on template.cacheTemplate.
No consumer read the template's combat_health_current; the
combat_health_current reads are the Item's own field.

Parity (offline, scripts/p14.py, 7586 templates): 0 differences in
combat_health_current, combat_health_full, item_initial_charges and
item_flags ("None" dropped on both sides). No value changes expected.

JSON-Refactor-Plan Task 1.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:45:17 -04:00
MagicBotandClaude Opus 5.5 dc4a9ee08a Read item weight, value and base name through the bridge
Consumers of the legacy ItemTemplate item_wt, item_value and
item_base_name now read getItemWt(), getItemValue() and
getItemBaseName() on template.cacheTemplate.

Parity (offline, scripts/p14.py, 7586 templates): 0 differences in
item_wt, item_value and item_base_name. No value changes expected.

JSON-Refactor-Plan Task 1.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:44:35 -04:00
MagicBotandClaude Opus 5.5 b8ac8cce3e Read item_type through the cache template bridge
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>
2026-10-19 12:43:57 -04:00
MagicBotandClaude Opus 5.5 6825f94f1e Check charters through DeedTemplate
GuildCreationFinalizeHandler and UseCharterMsg now match charters by
DeedTemplate._deeds instead of the shared item_type getter, like the
other deed paths.

Value change: template 545 "Charter" (ITEM folder, CHARTER) no longer
counts as a charter. It is a base template that players can't get: its
only DB reference is static_vendor_items with vendorType 0, it has no
loot rows, and it is not in GuildCharterType.templateLookup, so guild
creation already rejected it.

JSON-Refactor-Plan Task 1.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:47:09 -04:00
MagicBotandClaude Opus 5.5 c774746736 Read deed consumers from DeedTemplate
Deed placement (ObjectActionMsgHandler, PlaceAssetMsgHandler) reads
deed_structure_id from DeedTemplate._deeds. The deed, employment
contract and charter type checks (PlaceAssetMsgHandler,
ActivateNPCMsgHandler, GuildCreationFinalizeHandler, UseCharterMsg,
Item.serialize) read item_type through the cache template bridge.
Charters go through the shared getter rather than _deeds because
template 545 "Charter" is a CHARTER in the ITEM folder.

Parity (offline, static_item_templates vs COBJECTS, scripts/p13.py,
881 templates: all 880 DEED plus ITEM 545): 0 differences in
item_type and deed_structure_id. Every DB DEED, EMPLOYMENTCONTRACT,
PETTOTEM and FURNITUREDEED id is in the DEED folder. No value changes
expected. No pet totem or furniture deed consumer reads template
fields, so neither changes.

JSON-Refactor-Plan Task 1.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:42:54 -04:00
MagicBotandClaude Opus 5.5 3e087bd1ac Read mob rune damage and natural attacks from RuneTemplate
AbstractCharacter mob damage/ATR and CombatManager.getNaturalAttack now
read RuneTemplate._runes instead of the legacy ItemTemplate map.

Parity (offline, static_item_templates vs COBJECTS/RUNE, 2695 runes):
0 differences in rune_min_damage, rune_max_damage, rune_attack and
rune_naturalattacks. All 2366 runes with natural attacks use the
10-value layout. No value changes expected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:35:02 -04:00
MagicBotandClaude Opus 5.5 765cb9edb6 Bridge legacy ItemTemplate to its cache template
Each engine.objects.ItemTemplate gets a cacheTemplate field, filled by
linkCacheTemplates() right after CObjectIndex.build() on both servers,
so consumers can move to CObjectTemplate one field at a time.

ParityCheck gains a static_item_templates comparator: a null bridge is
reported as missing in json, and obj_name and item_type are compared
through the interface to catch a wrong folder pick.

Import convention until the legacy class is removed (Task 1.8): the
legacy engine.objects.ItemTemplate keeps the plain import; the cache
class is always written engine.parsers.objects.ItemTemplate. Migrated
consumers should depend on CObjectTemplate instead.

JSON-Refactor-Plan Task 1.1. No consumers change; no values change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:21:47 -04:00
MagicBotandClaude Opus 5.5 4765e7993f Add shared obj/combat/item getters to CObjectTemplate
The ITEM, DEED and RUNE cache templates implement the getters for the
65 fields all three define. The Item-only fields (weapon, primary and
secondary attr, bulk, defense rating, sheath, stub flag/id) stay off the
interface. CObjectTemplate.lookup delegates to CObjectIndex.lookup.

JSON-Refactor-Plan Task 1.1 (D1). No consumers change; no values change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:20:33 -04:00
MagicBotandClaude Opus 5.5 6ef987b7c6 Block all non-siege building placement while the city has a bane
Moves the warehouse-only check into validateBuildingPlacement, which every
city asset placement except siege equipment and trees passes through.
Matches client error 74, "Only buildings of war may be placed during a bane".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 10:15:54 -04:00
MagicBotandClaude Opus 5.5 9db194889f Use client error 74 for warehouse placement during a bane
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 10:14:37 -04:00
MagicBotandClaude Opus 5.5 c97b4d7c3d Declare the bane local at the top of placeWarehouse
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 10:12:56 -04:00
MagicBotandClaude Opus 5.5 e450224902 Block warehouse placement while the city has a bane
Placing a new warehouse discards the destroyed warehouse's rubble loot,
so defenders could deny attackers the loot by rebuilding mid-siege.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 10:09:31 -04:00
MagicBotandClaude Opus 5.5 875809efe0 Look up resource templates at loot time instead of caching them on ResourceType
ResourceType.template was filled in the enum constructor, which can run
before ItemTemplate.templates is loaded, leaving it null. lootBuilding was
its only reader and threw on the first non-gold resource.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:38:55 -04:00
MagicBotandClaude Opus 5.5 ef6fe4aa5b Skip rubble when locating a city's warehouse building at load
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:21:57 -04:00
MagicBotandClaude Opus 5.5 145bb15232 Skip the warehouse delete when the owner destroys warehouse rubble
city.warehouse is already null after a combat destruction, which threw in
DELETE_WAREHOUSE. If a new warehouse was placed it would have deleted that
one instead, so only a live (rank != -1) warehouse clears city.warehouse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:20:21 -04:00
MagicBotandClaude Opus 5.5 e64d64bf18 Keep destroyed warehouse contents in memory so the rubble can be looted
destroyOrDerank nulled city.warehouse on destruction, so the loot click
found no warehouse and returned silently. The destroyed Warehouse is now
held in Warehouse.destroyedWarehouses keyed by the rubble building UUID
until it is looted empty, the rubble is destroyed, or the city places a
new warehouse. Contents are not persisted across a reboot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:19:40 -04:00
MagicBotandClaude Opus 5.5 0865a7be74 Log only the JSON error and workorder ID when a workorder fails to load
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 08:24:04 -04:00
MagicBotandClaude Opus 5.5 a079deef8d Log only the JSON error and city UUID when a warehouse fails to load
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 08:23:18 -04:00
MagicBotandClaude Opus 5.5 963130565e Make MB_MINION_SPAWN_TIME a required ConfigManager entry
0 or blank keeps the normal guard/siege minion spawn timers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 08:00:53 -04:00
MagicBotandClaude Opus 5.5 becab380d2 Add optional MB_MINION_SPAWN_TIME override for guard and siege minion spawns
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 07:41:16 -04:00
MagicBotandClaude Opus 5.5 5c78bc7fa5 Skip the shopkeeper rune when applying an NPC rune set
applyRunesForNPC already adds 252620, and most rune sets repeat it, so
NPCs with a set listed the shopkeeper rune twice after the runeSet fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:30:18 -04:00
MagicBotandClaude Opus 5.5 4e30a04619 Fix /makeitem looking up the template by string instead of template id
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:06:10 -04:00
MagicBotandClaude Opus 5.5 42a1c8a532 Fix Warehouse.loot looking up the template by enum instead of template id
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:06:10 -04:00
MagicBotandClaude Opus 5.5 2a2c528981 Fix NPC rune sets storing the set id instead of the rune id
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:06:10 -04:00
MagicBotandClaude Opus 5.5 58e0fa32fb Add dev-only DB/JSON parity check at world boot
ParityCheck compares DB-built objects with the cache JSON and logs
table/id/field differences plus a per-table summary. It runs only when
the optional env var MB_PARITY_CHECK=true is set; it is not added to the
ConfigManager enum because init() aborts on any missing enum entry.

First comparator: static_rune_baseclass name vs RUNE obj_name.
No gameplay values change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:51:22 -04:00
MagicBotandClaude Opus 5.5 62b4a2a897 Skip cache JSON files whose name is not a 32-bit template id
The client cache holds RUNE/10788957847552 (2512 << 32), which would
overflow Integer.parseInt and abort the rune load. Such files are now
skipped with a warning. The file is not in MBDATA yet, so the
880/2695/4011 counts are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:44:45 -04:00
MagicBotandClaude Opus 5.5 d9d1c41d9e Add CObjectIndex lookups over the cache templates
Built at login and world bootstrap after the three parsers load (Task 0.1):
- lookup(id) across ITEM, then DEED, then RUNE
- race (MonsterType) -> creation race rune ids, male and female
- ClassType -> class rune id, DisciplineType -> discipline rune id
- SBStringHash(rune_sub_type) token -> runes
- skill and power name <-> SBStringHash token, from every template field
  that names one

No consumers yet; no values change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:33:35 -04:00
MagicBotandClaude Opus 5.5 cb427de440 Add CObjectTemplate marker interface for cache templates
ITEM, DEED and RUNE templates share one id space (JSON-Refactor-Plan D1).
Empty for now; the shared getters come with Task 1.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:33:34 -04:00
MagicBotandClaude Opus 5.5 74828c620d Load item cache JSON at login and world server bootstrap
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 09:51:20 -04:00
MagicBotandClaude Opus 5.5 fbd38732dc Add ITEM cache JSON parser
ItemTemplate follows ArcItemTemplate_Load (sb.exe 0x4AABD0): weapon and
armor blocks keyed by item type, sheath and stub blocks, and empty equip
slot masks read as empty sets. Adds SheathSlot and readHashLabel, which
keeps unresolved hashes such as item_skill_used in 195340.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 09:51:20 -04:00
MagicBot 75e10f613f Reporting within parser 2026-10-17 08:46:23 -04:00
MagicBot 783d2d4dc9 Bootstrap count reporting for DEED/RUNE 2026-10-17 08:40:37 -04:00