/rotate took degrees but stored them on NPCs and mobs where the code expects
cos of half the yaw, so the client got NaN and the DB got degrees; /rotate
face passed radians the same way. Buildings used |cos| for w, which mirrored
any angle above 180 (270 became 90). Every kind now takes a yaw in radians
(degrees from the command, the facing angle for "face") through
Quaternion.fromYaw. NPC and mob yaws are wrapped to [0, 2 pi) so the stored
cos reloads to the same angle.
Value changes: /rotate N on NPCs/mobs now faces N degrees (was NaN);
buildings past 180 degrees now face the given angle; npc_rotation and
mob_rotation saved by /rotate hold cos of half the yaw instead of the raw
input.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AbstractWorldObject.rot (a Vector3f holding one quaternion part) becomes a
Quaternion. Building drops its separate w field; Mob and NPC serialize
through AbstractCharacter.getClientYaw. DB columns are unchanged and are
converted at load and save: building (w, rotY) map to the quaternion's w and
y, npc_rotation (cos of half the yaw) becomes fromYaw(2 acos(value)).
Wire values are meant to be unchanged; a parity run follows. Behaviour kept
on purpose:
- mob_rotation is still not loaded, so every mob defaults to yaw pi as before
- the slot rotation still reads the product's sin as a cos
- /rotate on NPCs and mobs still treats its input as cos of half the yaw
Value changes: building serializer writes the quaternion's x and z instead
of literal zeros (both are 0 for every building); /info "Rot" for NPCs and
mobs prints the quaternion y (sin of half the yaw) instead of the stored cos.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Building gets a no-arg constructor, a createBuilding factory (in the style
of Mob.createMob) and persistBuilding, which inserts the row through
building_CREATE, sets the returned UID, caches the object and runs
runAfterLoad. The GET_BUILDINGBYUUID re-select after the insert is gone.
PlaceAssetMsgHandler (createStructure, createBarracks, createWarehouse),
Bane and AddBuildingCmd move onto it.
Value changes:
- floor/level start at -1 and the other unset fields at the obj_building
column defaults, same as the reloaded row.
- upgradeDateTime keeps milliseconds instead of the DATETIME-rounded value
(sub-second difference in the upgrade job time).
- Warehouse placement no longer runs runAfterLoad twice, which submitted
the warehouse upgrade job twice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 14 Magister Captain contracts' powers have no client rune or deed
(Task 3.9), so they live on the MinionType mage entries instead of the
DB fallback in loadAllRunePowers. canCast, mobCast and aiInfoCmd read
them through MinionType.getCaptainPowers.
No value change: same 56 power ids, rank 40, and all 56 DB tokens equal
the powerbase token of their power id (checked on mbdb_108).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each line now has the effects map key (effect UUID or stack type such as
PetBuff), the effect id and name, rank, time left or static, and the
power that applied it. Help and usage strings no longer describe
/printstats.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The command now acts on the selected building and refuses anything
whose blueprint is not a warehouse, or a warehouse that is not its
city's active one, instead of using the city the player stands in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sets every resource in the warehouse of the city the player stands in,
either to an optional amount or, by default, to each resource's
deposit limit. Makes forge and work order testing practical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
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>
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>
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>