refactor(entity): each component type has an allocator, as the client

DoObjectComponentLoad walks the client's load order and calls the type's
allocator from a table, like ObjectLoader2, instead of one 76-case switch.
Components with rules of their own have a static Allocator (the client's
LWOxComponent::Allocator); the rest share a registry allocator. The load
state moves to ObjectLoadState.h so the allocators can take it.

Construction bytes are identical for all 15,962 LOTs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-10-02 08:11:58 -05:00
parent 3a79286666
commit 260e6cb1b1
52 changed files with 634 additions and 684 deletions

View File

@@ -50,6 +50,8 @@
#include "CDComponentsRegistryTable.h"
#include "InventoryMessages.h"
#include "Entity.h"
#include "ObjectLoadState.h"
Implementation<bool, const Entity*> DestroyableComponent::IsEnemyImplentation;
Implementation<bool, const Entity*> DestroyableComponent::IsFriendImplentation;
@@ -1406,3 +1408,21 @@ void DestroyableComponent::Resurrect(Entity& entityRef) {
resurrect.bRezImmediately = false;
resurrect.Send(UNASSIGNED_SYSTEM_ADDRESS);
}
void DestroyableComponent::Allocator(Entity& entity, ObjectLoadState& state) {
/**
* The destroyable component. The client makes one (with a buff component in front of it) for a DESTROYABLE
* registry entry, whose id is the DestructibleComponent row. Collectibles and smashable (is_smashable) objects
* without that entry get one too, with no row.
*/
const auto destroyableComponentID = state.GetComponentID(eReplicaComponentType::DESTROYABLE);
if (destroyableComponentID > 0) {
entity.AddComponent<BuffComponent>(destroyableComponentID);
}
// A collectible's (right after it) and a smashable object's (after the walk) come later, as the client makes them
if (destroyableComponentID > 0) {
// The row, the level config and a character's save, in the client's order
entity.AddComponent<DestroyableComponent>(destroyableComponentID);
}
}