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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Grant pair lists now carry name strings ("HLR-021", "Unarmed Combat")
instead of integer hashes, so they are stored as name -> value maps.
Unresolved hashes are kept as their decimal string and logged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
item_user_power_action and item_power_action are keyed by power, so
rune step tables (repeated powers, multiple power_actions entries) are
collapsed to one entry. Nothing reads the rune data today; the new
RuneTemplate keeps the full lists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The validity check compared names case-sensitively, so mixed-case
input such as "Fire" or "Crushing" from static_power_failcondition and
static_power_poweraction, and SourceType names in the fortitude check,
all resolved to NONE. Input is now stripped of spaces, commas and
hyphens and upper-cased before lookup, so "Anti-Siege" maps to
ANTISIEGE as well.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RuneType covers the rune_type values in the client cache data
(CLASS, DISCIPLINE, RACE, TALENT). ANTISIEGE matches the
combat_attack_resist key in the cache data.
RuneTemplate.rune_type and combat_attack_resist on both cache
templates now use these enums.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New engine.parsers package:
- cache: DeedParser and RuneParser load COBJECTS/DEED and COBJECTS/RUNE
JSON straight from the data dir, keyed by file id. CacheParserUtil
holds the shared JSON helpers.
- objects: standalone DeedTemplate and RuneTemplate covering every
field in the client data, plus shared PowerGrant/UserPowerAction.
- wpak: placeholder for upcoming wpak parsers.
Not wired into server startup yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summoned pets (not necro pets) now carry MASK_PET, so pet-targeted
AOE powers can find them. The checkForAggro pet scan is held off with
PET_AGGRO_ENABLED until it checks rune enemy lists, filters targets
and keeps pet targets (plan-mask-pet.md steps 2-4).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
InterestManager cleared the combat target of every live mob loaded for
a new player, so a siege engine firing at a wall dropped its target
whenever someone entered load range. Siege engines now keep it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scan was removed as dead code, but creature runes list Pet in
rune_enemy_monster_types, so monsters are meant to aggro summoned pets.
Restored unchanged; it stays inactive until MASK_PET is set from the
rune data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Guard MASK_SIEGE and regular mob MASK_PET scans were dead code: no mob
ever carries either mask. Removed so they cannot start auto-aggroing
siege engines if the masks are wired up later. Safehold guards no
longer aggro siege engines. Guards still respond to a siege engine
damaging a city building.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Damaging a siege engine on a city grid adds the attacker (or the owning
player, for pets) to that city's outlaw list, unless they are in the
city's nation or an allied nation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>