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
This commit is contained in:
Christian Semmler 2026-02-13 16:37:15 -08:00
parent f482898a72
commit f796ea4299
No known key found for this signature in database
GPG Key ID: 086DAA1360BEEE5C
5 changed files with 25 additions and 77 deletions

View File

@ -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);
}

View File

@ -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;

View File

@ -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) {

View File

@ -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'
});

View File

@ -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;