From f796ea42990be1ef7050ef51ef477628e29f9290 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Fri, 13 Feb 2026 16:37:15 -0800 Subject: [PATCH] Clean up actor editor branch: DRY, dead code, CSS - Extract buildNodeToPartGroupMap() in ActorRenderer to deduplicate map-building logic in loadAnimationForActor and playClickAnimation - Refactor updateMissionScore() to use getMissionScoreOffset() instead of duplicating offset calculation - Remove unused ActorPartLabels export from actorConstants - Make fetchBitmap module-private (only used by fetchBitmapAsURL) - Merge duplicate .globe-btn CSS blocks in LightPositionEditor --- src/core/assetLoader.js | 2 +- src/core/formats/SaveGameSerializer.js | 38 ++---------------- src/core/rendering/ActorRenderer.js | 40 +++++++++---------- src/core/savegame/actorConstants.js | 15 ------- .../save-editor/LightPositionEditor.svelte | 7 +--- 5 files changed, 25 insertions(+), 77 deletions(-) diff --git a/src/core/assetLoader.js b/src/core/assetLoader.js index 815a00c..3119c3f 100644 --- a/src/core/assetLoader.js +++ b/src/core/assetLoader.js @@ -36,7 +36,7 @@ export async function fetchTexture(name) { return getAsset('textures', name); } -export async function fetchBitmap(name) { +async function fetchBitmap(name) { await loadBundle(); return getAsset('bitmaps', name); } diff --git a/src/core/formats/SaveGameSerializer.js b/src/core/formats/SaveGameSerializer.js index f8cdb1b..5c0aa5c 100644 --- a/src/core/formats/SaveGameSerializer.js +++ b/src/core/formats/SaveGameSerializer.js @@ -209,41 +209,9 @@ export class SaveGameSerializer { return null; } - const view = new DataView(workingBuffer); - - // Calculate offset based on mission type - let offset; - - if (missionType === 'pizza') { - // Pizza: 5 actors * 8 bytes (unk(2) + counter(2) + score(2) + hiScore(2)) - const actorIndex = actorId - Actor.PEPPER; // 0-4 - const entryOffset = stateLocation.dataOffset + (actorIndex * 8); - if (scoreType === 'score') { - offset = entryOffset + 4; // Skip unk + counter - } else { - offset = entryOffset + 6; // Skip unk + counter + score - } - } else if (missionType === 'carRace' || missionType === 'jetskiRace') { - // Race: 5 actors * 5 bytes (id(1) + lastScore(2) + highScore(2)) - const actorIndex = actorId - Actor.PEPPER; - const entryOffset = stateLocation.dataOffset + (actorIndex * 5); - if (scoreType === 'score') { - offset = entryOffset + 1; // Skip id - } else { - offset = entryOffset + 3; // Skip id + lastScore - } - } else if (missionType === 'towTrack' || missionType === 'ambulance') { - // Score mission: 5 scores then 5 high scores (all S16) - const actorIndex = actorId - Actor.PEPPER; - if (scoreType === 'score') { - offset = stateLocation.dataOffset + (actorIndex * 2); - } else { - offset = stateLocation.dataOffset + 10 + (actorIndex * 2); // Skip 5 scores - } - } - - if (offset !== undefined) { - view.setInt16(offset, value, true); + const offset = this.getMissionScoreOffset(missionType, actorId, scoreType); + if (offset !== null) { + new DataView(workingBuffer).setInt16(offset, value, true); } return workingBuffer; diff --git a/src/core/rendering/ActorRenderer.js b/src/core/rendering/ActorRenderer.js index c1da496..a4d29e1 100644 --- a/src/core/rendering/ActorRenderer.js +++ b/src/core/rendering/ActorRenderer.js @@ -493,17 +493,7 @@ export class ActorRenderer extends BaseRenderer { const animData = await this.fetchAnimationByName(animName); if (!animData || !this.modelGroup) return; - // Build animation node name → part group lookup - const nodeToPartGroup = new Map(); - for (let i = 0; i < this.partGroups.length; i++) { - const pg = this.partGroups[i]; - if (!pg) continue; - const lodName = pg.userData.lodName; - const animNodeName = PART_NAME_TO_ANIM_NODE[lodName]; - if (animNodeName) { - nodeToPartGroup.set(animNodeName.toLowerCase(), pg); - } - } + const nodeToPartGroup = this.buildNodeToPartGroupMap(); // Map vehicle animation nodes if in vehicle mode if (vehicleInfo && this.vehicleGroup) { @@ -522,6 +512,23 @@ export class ActorRenderer extends BaseRenderer { } } + /** + * Build a lookup mapping animation node names to part groups. + */ + buildNodeToPartGroupMap() { + const map = new Map(); + for (let i = 0; i < this.partGroups.length; i++) { + const pg = this.partGroups[i]; + if (!pg) continue; + const lodName = pg.userData.lodName; + const animNodeName = PART_NAME_TO_ANIM_NODE[lodName]; + if (animNodeName) { + map.set(animNodeName.toLowerCase(), pg); + } + } + return map; + } + /** * Map vehicle animation tree nodes to the vehicle group. * Scans the animation tree for nodes whose name (stripped of trailing @@ -566,16 +573,7 @@ export class ActorRenderer extends BaseRenderer { return; } - const nodeToPartGroup = new Map(); - for (let i = 0; i < this.partGroups.length; i++) { - const pg = this.partGroups[i]; - if (!pg) continue; - const lodName = pg.userData.lodName; - const animNodeName = PART_NAME_TO_ANIM_NODE[lodName]; - if (animNodeName) { - nodeToPartGroup.set(animNodeName.toLowerCase(), pg); - } - } + const nodeToPartGroup = this.buildNodeToPartGroupMap(); const tracks = this.buildHierarchicalTracks(animData, nodeToPartGroup); if (tracks.length === 0) { diff --git a/src/core/savegame/actorConstants.js b/src/core/savegame/actorConstants.js index da97e0b..1615040 100644 --- a/src/core/savegame/actorConstants.js +++ b/src/core/savegame/actorConstants.js @@ -713,18 +713,3 @@ export const CharacterFieldOffsets = Object.freeze({ export const CHARACTER_RECORD_SIZE = 16; -/** - * Part labels for display - */ -export const ActorPartLabels = Object.freeze({ - 0: 'Body', - 1: 'Hat', - 2: 'Torso Emblem', - 3: 'Head', - 4: 'Left Arm', - 5: 'Right Arm', - 6: 'Left Claw', - 7: 'Right Claw', - 8: 'Left Leg', - 9: 'Right Leg' -}); diff --git a/src/lib/save-editor/LightPositionEditor.svelte b/src/lib/save-editor/LightPositionEditor.svelte index 539e441..9957cab 100644 --- a/src/lib/save-editor/LightPositionEditor.svelte +++ b/src/lib/save-editor/LightPositionEditor.svelte @@ -85,6 +85,8 @@ .globe-btn { padding: 4px; + min-width: 56px; + min-height: 56px; background: var(--color-bg-input); border: 2px solid var(--color-border-medium); border-radius: 6px; @@ -100,11 +102,6 @@ border-color: var(--color-primary); } - .globe-btn { - min-width: 56px; - min-height: 56px; - } - .globe-btn img { width: 48px; height: 48px;