Commit Graph

2686 Commits

Author SHA1 Message Date
Aaron Kimbrell
243b2106bc refactor(entity): destroyables made where the client makes them
The destroyable slot makes only the registry's (with its buff); a
collectible's destroyable comes right after the collectible when there is
none, and a smashable object's (is_smashable) after the walk
(LWODestroyableComponent::Allocator), as in DoObjectComponentLoad. A
quickbuild or model with is_smashable now gets its own defaults first, as
the client's order gives it. Same bytes for every LOT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 17:13:37 -05:00
Aaron Kimbrell
1481bb7b32 feat(entity): movement AI for scripted objects and ones with a path, as the client
ApplyMovementAiIfNeeded gives an object with a script (its registry's or a
custom script) or an attached_path a movement AI component (id 0); 1843 LOTs
gain one. It follows its attached path only when that is a movement path (a
moving platform's path stays the platform's). Not on spawner objects, which
the client never loads. Nothing sent changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 17:07:47 -05:00
Aaron Kimbrell
f3ae06cee6 feat(component): the exhibit component (32) the client's loader leaves dead
1.10.64's DoObjectComponentLoad has an EXHIBIT case that makes nothing;
DLU makes ExhibitComponent there, built like LWOExhibitComponent on the
spawner component: its ExhibitComponent row (model offset and size) and the
exhibit_* level keys. It sends nothing in a construction (the client's
Deserialize is empty), so the 12 exhibit LOTs keep their bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:56:25 -05:00
Aaron Kimbrell
477dd9f3e3 refactor(entity): load an object in the client's two steps
Entity::Initialize now runs DoObjectPreLoad (what the components will be:
the player's own components, markedAsPhantom, model behaviors, the
component whitelist) and DoObjectComponentLoad (the walk in the client's
order and what the client adds after it), as ObjectLoader2 does, with an
ObjectLoadState that answers the registry through the whitelist. Same
components and bytes for every LOT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:52:15 -05:00
Aaron Kimbrell
432f4f6ff9 fix(component): send a script component only when the client makes one
The client makes a script component for a registry script with a non-zero
id, or for a custom script in the config it is sent (LOT 176 never):
ApplyScriptComponentIfHasScript and LWOScriptComponent's allocator. A
character's script and a server-only custom_script_server stay on the
server and send nothing. The 5 LOTs whose registry script id is 0 (e.g. the
AG concert fan, LOT 3002: live sent no config and no script) lose the bit
the client never read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:38:56 -05:00
Aaron Kimbrell
c69b26b661 feat(entity): more of the client's object loading rules
From ObjectLoader2 (DoObjectPreLoad / DoObjectComponentLoad):
- componentWhitelist N keeps only that whitelist's registry components
  (ApplyComponentWhitelist; property models use 1).
- No pet component for an object in an inventory (LWOPetComponent::Factory).
- Havok vehicle physics: use_simple_physics makes simple physics instead,
  objectWorldState 2 none.
- markedAsPhantom (any value) turns the registry's simple physics into
  phantom physics with that row, and doesn't count when the registry has
  phantom physics.
- Vendors get a proximity monitor first when they have none; a mission
  offer's object gets one after the walk (113 LOTs gain one).
Same bytes for every LOT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:32:07 -05:00
Aaron Kimbrell
b24661710b refactor(component): controllable physics places its object itself
Where an object starts (where the level put it) moves into
ControllablePhysicsComponent::LoadConfigData, and where a character comes
into the world (its save, then its respawn point, the target scene or the
zone's spawn when it came from elsewhere) into LoadFromSaveData, out of
Entity::Initialize. Same placement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:05:04 -05:00
Aaron Kimbrell
4ea1762e0a refactor(component): the script component picks its own script
The ScriptComponent row (server and client script) loads in
LoadDataFromTemplate and custom_script_server / custom_script_client in
LoadConfigData, out of Entity::Initialize, which only decides whether there
is one (a registry entry, a character or a custom script), as the client's
allocator does. Same scripts and bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 16:03:00 -05:00
Aaron Kimbrell
bff3fe6363 refactor(component): moving platform subcomponents like the client's
PlatformSubComponent (LWOPlatform: the state between waypoints and its
serialization) is the base of MoverSubComponent (4), SimpleMoverSubComponent
(5) and the new RotatorSubComponent (6), as LWOPlatformMover,
LWOPlatformSimpleMover and LWOPlatformRotator are built on LWOPlatform. The
platform keeps one subcomponent per type the client makes
(LWOMovingPlatformComponent::SetupPlatform: one per platformIs* setting that
is true) and sends them all; the first is the one the server runs. Same bytes
for every platform with one subcomponent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 15:59:45 -05:00
Aaron Kimbrell
083c071853 refactor(entity): each vendor type in its own place in the client's load order
Vendor (16), donation vendor (100) and achievement vendor (102) are separate
entries in the client's component walk; no LOT has two of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 15:49:05 -05:00
Aaron Kimbrell
54adbcdce8 refactor(component): proximity monitor and movement AI load their own rows
The ProximityMonitorComponent row's radii and the MovementAIComponent row
(with the level's wanderRadius over it) move out of Entity::Initialize into
each component's LoadDataFromTemplate / LoadConfigData. Same components and
bytes for every LOT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 15:47:10 -05:00
Aaron Kimbrell
c710e1d3a9 refactor(component): vendors share the client's base vendor component
LWOBaseVendorComponent is the base of the client's vendor, donation vendor and
achievement vendor; DLU's vendor logic becomes BaseVendorComponent with
VendorComponent, DonationVendorComponent and AchievementVendorComponent all
built on it (the last two derived from VendorComponent before). Nothing sent
changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 15:43:13 -05:00
Aaron Kimbrell
9a464ac35f refactor(component): group the component files by the client's families
Physics/, Activity/ (activity, scripted activity, shooting gallery,
quickbuild, minigame control, racing control base, racing control, gate rush),
Vendor/, Character/ (what the client makes with a character, plus ghost,
minifig and mission), Sound/ and Property/; the rest stays where it was.
File moves only; DLU_COMPONENT_INCLUDE_DIRS gives every target the folders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 15:37:45 -05:00
Aaron Kimbrell
8f09121ad4 feat(entity): a character's components as the client makes them
LWOCharacterComponent::Factory makes possession control, mount control, pet
creator, level progression and forced movement, then the character, all with
componentID -1; DLU now makes the same set in the same order (mount control
and pet creator are new, empty). The ghost is added after the component walk,
where the client adds it, for a character with physics. Nothing sent changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 15:30:20 -05:00
Aaron Kimbrell
b0dd69cf0d feat(entity): player forced movement (106) from the registry, written where the client reads it
The client makes 106 from the registry; the only non-character LOT with it
(16684, a test skateboard mount) now has one and writes it between the pet
and the character, the client's place for it. A character still writes it
with its other parts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:49:13 -05:00
Aaron Kimbrell
a7c8556cb5 feat(entity): the client's HF light direction gadget component (66)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:46:45 -05:00
Aaron Kimbrell
0138d45c58 fix(component): the zone control object constructs as on live
Minigame control (50) is an activity component in the client
(LWOMinigameComponent) and is made from the registry, so the zone control
object (LOT 2365, registry id 0) has one; it sends what an activity sends
(dirty, no players), not a fixed 0x40000000 (which nothing sent: the old
id > 0 check never made it). The zone's script on that object is server
only: the client makes no script component for it, so it sends nothing.
DLU's zone control construction now equals live's byte for byte (209
captures, e.g. GruntMonkey world2); the decoder reads 50 as an activity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:29:02 -05:00
Aaron Kimbrell
0e611bc76c refactor(component): racing control and gate rush share the client's racing control base
LWOBaseRacingControlComponent (an activity component) is the base of the
client's racing control (71) and gate rush control (103); DLU gets the same
BaseRacingControlComponent. Nothing sent changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:23:38 -05:00
Aaron Kimbrell
4bfdeec369 refactor(component): the quickbuild is an activity component, as in the client
LWOQuickBuildComponent uses the activity component's functions; DLU's
QuickBuildComponent now derives ActivityComponent. Its RebuildComponent row
(the activity among it) loads in LoadDataFromTemplate and the level's
overrides in LoadConfigData, out of Entity::Initialize. It still writes its
own replica data (same construction and update bytes) and spawns its
activator as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:19:49 -05:00
Aaron Kimbrell
ab8b944f2f refactor(component): vehicle physics (46) and physics system (47) are physics components
As in the client, where both use LWOBasePhysComponent's functions (projectile
physics, 18, does not and stays a plain component). They now answer the
object's position like the other physics components (their LOTs have no other
physics); nothing they send changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:56:49 -05:00
Aaron Kimbrell
d786290803 refactor(entity): models get their physics in the client's model case
ObjectLoader2::LoadModelBehaviorsComponent: a model without physics gets simple
physics 4247 (with behaviors: controllable 4246 for modelType 0, else simple,
or the registry's id, which the simple physics case leaves to it), and
modelType 0 gets pathfinding control. This replaces the BBB model (LOT 14)
special case, which now loads through the registry like any object (same
bytes as live LOT 14 constructions); the 17 other model LOTs without
physics in the registry now get the simple physics the client makes for them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:49:38 -05:00
Aaron Kimbrell
91942b16a2 fix(spawner): spawner objects are never sent to clients, as on live
Live constructed no spawner object (LOT 176) in any capture, property
building included; spawners made while players are in the world (property
models) were sent to everyone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:43:14 -05:00
Aaron Kimbrell
2d7b5cca13 refactor(spawner): the spawner object's SpawnerComponent owns its spawner
The zone manager keeps an index of spawners by object ID; the spawner now
belongs to the SpawnerComponent of its LOT 176 object and goes with it (the
component tells the zone manager to stop indexing it). Spawning, timers and
the update order are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:31:18 -05:00
Aaron Kimbrell
9da7b11456 feat(entity): the client's spawner component (10) on spawner objects
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:19:18 -05:00
Aaron Kimbrell
2f01b85d8a feat(entity): combat AI objects get the client's proximity monitor and pathfinding control
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:39:52 -05:00
Aaron Kimbrell
aea4949cf3 feat(entity): script objects get the client's proximity monitor and pathfinding control
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:38:46 -05:00
Aaron Kimbrell
4a546607f7 feat(entity): the client's pathfinding control component (86) on pets
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:37:17 -05:00
Aaron Kimbrell
c65f559b39 feat(entity): the client's physics system component (47)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:01:25 -05:00
Aaron Kimbrell
5f77dca7e7 feat(entity): the client's vehicle physics component (46)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:01:25 -05:00
Aaron Kimbrell
589a91a1f0 feat(entity): the client's projectile physics component (18)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:01:25 -05:00
Aaron Kimbrell
029e601e07 refactor(component): the destroyable loads itself in the client's phases
The DestructibleComponent row moves from Entity::Initialize into
LoadDataFromTemplate and the level keys into LoadConfigData; a character's
save loads after both. Like the client (LWODestroyableComponent::LoadConfigData
reads them for every destroyable), the destroyables added for models and
quickbuilds now also read set_faction, is_smashable and the level loot matrix;
their live defaults become their template. Nothing changes for objects without
those keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:52:10 -05:00
Aaron Kimbrell
e1c3c19c9a refactor(component): the quickbuild reads its level config and spawns its activator in LoadConfigData
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:37:42 -05:00
Aaron Kimbrell
db99190c31 refactor(component): moving platforms load in the client's phases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:36:08 -05:00
Aaron Kimbrell
43b99b730f refactor(component): physics components load in the client's phases
The physics row's collision group in LoadDataFromTemplate and the level's
CollisionGroupID in LoadConfigData; each physics type then reads its own level
keys and makes its physics after that, in the same order as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:34:28 -05:00
Aaron Kimbrell
a4f1b974c8 refactor(component): combat AI loads its row, skills and level radii in the client's phases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:21:32 -05:00
Aaron Kimbrell
1cdac362ff refactor(component): an object's starting inventory loads in LoadDataFromTemplate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:19:07 -05:00
Aaron Kimbrell
2b8b682a0b refactor(component): activities load their activity in the client's phases
The component ID's activity in LoadDataFromTemplate, the level's activityID in
LoadConfigData; racing control picks its world's activity after both, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:15:58 -05:00
Aaron Kimbrell
14a11ac6ce refactor(component): movement AI reads its attached path in LoadConfigData
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:12:32 -05:00
Aaron Kimbrell
3d1f7cecb8 refactor(component): the model reads its user model ID in LoadConfigData
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:12:32 -05:00
Aaron Kimbrell
da5ce99f5b refactor(component): the render component loads its animation groups in LoadDataFromTemplate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:10:56 -05:00
Aaron Kimbrell
7a0013696b refactor(component): the sound trigger reads its level config in LoadConfigData
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:10:56 -05:00
Aaron Kimbrell
e5607ff0c7 refactor(component): the collectible loads its row in LoadDataFromTemplate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:10:56 -05:00
Aaron Kimbrell
3dbbf46876 refactor(component): the bouncer reads its level config in LoadConfigData
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:10:56 -05:00
Aaron Kimbrell
6fc19c114d refactor(component): the pet loads its row and preconditions in the client's phases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:08:11 -05:00
Aaron Kimbrell
12efcd259c refactor(component): the switch reads its reset time and factions in LoadConfigData
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:08:11 -05:00
Aaron Kimbrell
90c7293189 refactor(component): the mission offer loads its missions in LoadDataFromTemplate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:08:11 -05:00
Aaron Kimbrell
060517920f refactor(component): the donation vendor reads its goal and activity in LoadConfigData
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:08:11 -05:00
Aaron Kimbrell
818ad152ca refactor(component): vendors load their stock in LoadDataFromTemplate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:08:11 -05:00
Aaron Kimbrell
8ef3e323fe refactor(component): the property entrance loads its row in LoadDataFromTemplate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:06:12 -05:00
Aaron Kimbrell
87f0e730c8 refactor(component): the multi-zone entrance reads its worlds in LoadConfigData
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:06:12 -05:00