From 9fa3896b5b99f85c071cb3b6824b278b77673834 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Sat, 22 Aug 2026 21:08:45 -0600 Subject: [PATCH] feat(wolfenstein): add level-new map, improve HUD weapon icon contrast, and update e1m1 items - Added new "level-new" level data (33x24 grid) with doors, enemies, items, and objects - Removed one ammo-clip from e1m1 and added two health-large pickups - Introduced shared HUD row anchor constants for consistent positioning - Updated HUD to display level name alongside episode/mission info - Added gray rounded-rect background behind weapon icon in HUD for better contrast against steel-blue bar - Repositioned weapon icon to dynamically sit left of ammo text based on rendered width - Updated WolfensteinArt to generate the new HUD icon background texture --- assets/gamedata/wolfenstein/level-e1m1.json | 15 +- assets/gamedata/wolfenstein/level-e1m2.json | 905 ++++++++++++++++++-- src/games/wolfenstein/WolfensteinArt.js | 20 + src/games/wolfenstein/WolfensteinGame.js | 76 +- 4 files changed, 935 insertions(+), 81 deletions(-) diff --git a/assets/gamedata/wolfenstein/level-e1m1.json b/assets/gamedata/wolfenstein/level-e1m1.json index 08bbbc8..306c9ec 100644 --- a/assets/gamedata/wolfenstein/level-e1m1.json +++ b/assets/gamedata/wolfenstein/level-e1m1.json @@ -1031,11 +1031,6 @@ "x": 17.5, "y": 24.5 }, - { - "type": "ammo-clip", - "x": 16.5, - "y": 24.5 - }, { "type": "ammo-clip", "x": 15.5, @@ -1045,6 +1040,16 @@ "type": "pistol", "x": 4.5, "y": 4.5 + }, + { + "type": "health-large", + "x": 19.5, + "y": 2.5 + }, + { + "type": "health-large", + "x": 16.5, + "y": 24.5 } ], "exit": { diff --git a/assets/gamedata/wolfenstein/level-e1m2.json b/assets/gamedata/wolfenstein/level-e1m2.json index 9e0a7bf..2f89b20 100644 --- a/assets/gamedata/wolfenstein/level-e1m2.json +++ b/assets/gamedata/wolfenstein/level-e1m2.json @@ -1,14 +1,33 @@ { "version": 1, - "id": "e1m2", + "id": "level-new", "name": "The Armory", - "campaignId": "episode1", - "missionIndex": 1, - "width": 14, - "height": 11, + "campaignId": null, + "missionIndex": 0, + "width": 33, + "height": 24, "cellSize": 64, "walls": [ [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, 1, 1, 1, @@ -30,7 +49,6 @@ 0, 0, 0, - 1, 0, 0, 0, @@ -38,15 +56,19 @@ 0, 0, 0, - 1 - ], - [ - 1, 0, 0, 0, 0, - 1, + 0, + 7, + 0, + 7, + 0, + 0, + 0, + 0, + 0, 0, 0, 0, @@ -70,15 +92,18 @@ 0, 0, 0, - 1 - ], - [ - 1, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 7, 0, 0, 0, 0, - 1, 0, 0, 0, @@ -94,11 +119,30 @@ 0, 0, 0, - 1, 0, 0, - 1, - 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 7, + 7, + 7, + 7, + 0, + 0, 0, 0, 0, @@ -110,11 +154,30 @@ 0, 0, 0, - 1, 0, 0, - 1, - 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 7, + 0, + 0, 0, 0, 0, @@ -126,7 +189,131 @@ 0, 0, 0, + 0, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ 1, + 7, + 7, + 7, + 7, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 7, + 7, + 7, + 7, + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ + 1, + 7, + 7, + 7, + 7, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, 0, 0, 0, @@ -142,7 +329,26 @@ 0, 0, 0, - 1, + 0, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, 0, 0, 0, @@ -158,6 +364,36 @@ 0, 0, 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ 1, 0, 0, @@ -166,6 +402,415 @@ 0, 0, 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 7, + 8, + 8, + 8, + 8, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 7, + 0, + 7, + 0, + 8, + 0, + 0, + 8, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 6, + 6, + 0, + 6, + 6, + 8, + 0, + 0, + 8, + 6, + 0, + 0, + 0, + 6, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 6, + 8, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 6, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 6, + 8, + 8, + 8, + 8, + 6, + 0, + 0, + 0, + 6, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 6, + 6, + 6, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 6, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 1 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, 1 ], [ @@ -182,64 +827,224 @@ 1, 1, 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 6, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, 1 ] ], "playerStart": { "x": 1.5, - "y": 1.5, + "y": 7.5, "angle": 0 }, "doors": [ { - "x": 5, - "y": 3, - "orientation": "vertical" + "x": 13, + "y": 7, + "orientation": "vertical", + "color": null + }, + { + "x": 22, + "y": 8, + "orientation": "vertical", + "color": "yellow" + }, + { + "x": 22, + "y": 6, + "orientation": "vertical", + "color": "red" + }, + { + "x": 18, + "y": 13, + "orientation": "vertical", + "color": null + }, + { + "x": 18, + "y": 1, + "orientation": "vertical", + "color": null + } + ], + "secretDoors": [ + { + "x": 25, + "y": 15, + "dir": 180 } ], "enemies": [ { "type": "guard", - "x": 10.5, - "y": 1.5, - "facing": 180 + "x": 20.5, + "y": 5.5, + "facing": 180, + "patrol": [] }, { "type": "guard", - "x": 9.5, - "y": 8.5, + "x": 20.5, + "y": 9.5, + "facing": 180, + "patrol": [] + }, + { + "type": "guard", + "x": 27.5, + "y": 18.5, "facing": 180, "patrol": [ { - "x": 7.5, - "y": 8.5 - }, - { - "x": 12.5, - "y": 8.5 + "x": 14.5, + "y": 18.5 } ] + }, + { + "type": "guard", + "x": 14.5, + "y": 19.5, + "facing": 0, + "patrol": [ + { + "x": 27.5, + "y": 19.5 + } + ] + }, + { + "type": "guard", + "x": 18.5, + "y": 20.5, + "facing": 270, + "patrol": [] } ], "items": [ { - "type": "ammo", - "x": 3.5, - "y": 6.5 + "type": "machinegun", + "x": 21.5, + "y": 7.5 }, { - "type": "health", - "x": 11.5, - "y": 9.5 + "type": "shotgun", + "x": 23.5, + "y": 13.5 + }, + { + "type": "shotgun-shells", + "x": 23.5, + "y": 14.5 + }, + { + "type": "shotgun-shells", + "x": 22.5, + "y": 14.5 + }, + { + "type": "shotgun-shells", + "x": 22.5, + "y": 13.5 + }, + { + "type": "key-red", + "x": 15.5, + "y": 15.5 + } + ], + "wallArt": [ + { + "x": 25, + "y": 15, + "frame": 1 + }, + { + "x": 16, + "y": 15, + "frame": 1 + }, + { + "x": 20, + "y": 15, + "frame": 1 + } + ], + "objects": [ + { + "x": 6.5, + "y": 6.5, + "frame": 4 + }, + { + "x": 6.5, + "y": 8.5, + "frame": 4 + }, + { + "x": 9.5, + "y": 6.5, + "frame": 4 + }, + { + "x": 9.5, + "y": 8.5, + "frame": 4 + }, + { + "x": 12.5, + "y": 6.5, + "frame": 4 + }, + { + "x": 12.5, + "y": 8.5, + "frame": 4 + }, + { + "x": 26.5, + "y": 16.5, + "frame": 6 + }, + { + "x": 26.5, + "y": 14.5, + "frame": 6 + }, + { + "x": 15.5, + "y": 14.5, + "frame": 6 + }, + { + "x": 15.5, + "y": 16.5, + "frame": 6 } ], "exit": { - "x": 12.5, - "y": 9.5, + "x": 25.5, + "y": 5.5, "radius": 0.6 }, - "briefing": [ - "Tougher resistance ahead.", - "The armory door only opens from this side." - ] + "briefing": [] } diff --git a/src/games/wolfenstein/WolfensteinArt.js b/src/games/wolfenstein/WolfensteinArt.js index 8bea1dc..a20c559 100644 --- a/src/games/wolfenstein/WolfensteinArt.js +++ b/src/games/wolfenstein/WolfensteinArt.js @@ -55,6 +55,7 @@ export function ensureSprites(scene) { paintWeaponFists(scene); paintHudBar(scene); paintProfile(scene); + paintHudIconBg(scene); } function paintGuard(scene) { @@ -348,3 +349,22 @@ function paintProfile(scene) { g.generateTexture('wolf-profile', S, S); g.destroy(); } + +// A mid-tone gray rounded-rect backing dropped behind the HUD bar's weapon +// icon (WolfensteinGame._updateHud repositions the Image using this texture +// each frame, hugging the ammo text) — the pickups-sheet icons read poorly +// straight against the steel-blue plate (several are themselves blue-gray +// metal), so this gives them a neutral, higher-contrast plate of their own +// to sit on, with a subtle bevel matching the bar's own raised-plate style. +function paintHudIconBg(scene) { + const S = 60; + const g = scene.make.graphics({ x: 0, y: 0, add: false }); + g.fillStyle(0x6b6f75, 1); + g.fillRoundedRect(0, 0, S, S, 10); + g.lineStyle(2, 0x9a9ea4, 0.8); + g.strokeRoundedRect(1, 1, S - 2, S - 2, 9); + g.lineStyle(2, 0x2c2e31, 0.8); + g.strokeRoundedRect(0, 0, S - 1, S - 1, 10); + g.generateTexture('wolf-hud-icon-bg', S, S); + g.destroy(); +} diff --git a/src/games/wolfenstein/WolfensteinGame.js b/src/games/wolfenstein/WolfensteinGame.js index f92d5dc..d5fe79f 100644 --- a/src/games/wolfenstein/WolfensteinGame.js +++ b/src/games/wolfenstein/WolfensteinGame.js @@ -31,6 +31,12 @@ const FONT = 'm6x11, "Julius Sans One"'; // the shared warm-cream COLORS.textHex, per the "white lettering" ask. const HUD_TEXT_VALUE = '#ffffff'; const HUD_TEXT_LABEL = '#c9def0'; +// Shared row anchors — module-level (not local to _buildHud) so _updateHud +// can reposition the weapon icon relative to the same value-row y each +// frame without redriving the bar-center math a second time. +const HUD_BAR_Y = GAME_HEIGHT - 70; // bar vertical center, screen y=1010 +const HUD_LABEL_Y = HUD_BAR_Y - 38; // ~972 — episode/mission, INVENTORY: + key icons +const HUD_VALUE_Y = HUD_BAR_Y + 32; // ~1042 — health, ammo + weapon icon const SAVE_KEY = 'wolfenstein-save'; const SAVE_SLOT_COUNT = 4; @@ -386,7 +392,8 @@ export default class WolfensteinGame extends Phaser.Scene { _hudEpisodeMissionText() { if (this.meta?.mode === 'campaign') { const idx = this.campaigns.campaigns.findIndex((c) => c.id === this.meta.campaignId); - return `EPISODE ${idx >= 0 ? idx + 1 : '?'} · MISSION ${this.meta.missionIndex + 1}`; + const name = this.state?.levelMeta?.name ? ` · ${this.state.levelMeta.name.toUpperCase()}` : ''; + return `EPISODE ${idx >= 0 ? idx + 1 : '?'} · MISSION ${this.meta.missionIndex + 1}${name}`; } // Test Play (and any future non-campaign mode) has no campaign/mission numbering. return this.state?.levelMeta?.name ? this.state.levelMeta.name.toUpperCase() : 'TEST LEVEL'; @@ -413,18 +420,15 @@ export default class WolfensteinGame extends Phaser.Scene { // cheap no-op. ensureSprites(this); - const y = GAME_HEIGHT - 70; // bar vertical center, screen y=1010 - const labelY = y - 38; // ~972 — episode/mission, INVENTORY: + key icons - const valueY = y + 32; // ~1042 — health, ammo + weapon icon const objs = {}; - objs.bar = this.add.image(GAME_WIDTH / 2, y, 'wolf-hud-bar').setDepth(20); + objs.bar = this.add.image(GAME_WIDTH / 2, HUD_BAR_Y, 'wolf-hud-bar').setDepth(20); - // Left panel: episode/mission (see _hudEpisodeMissionText) above health. - objs.episodeMission = this.add.text(80, labelY, '', { + // Left panel: episode/mission/name (see _hudEpisodeMissionText) above health. + objs.episodeMission = this.add.text(80, HUD_LABEL_Y, '', { fontFamily: FONT, fontSize: '22px', color: HUD_TEXT_LABEL, letterSpacing: 2, }).setOrigin(0, 0.5).setDepth(21); - objs.health = this.add.text(80, valueY, '', { + objs.health = this.add.text(80, HUD_VALUE_Y, '', { fontFamily: FONT, fontSize: '36px', fontStyle: 'bold', color: HUD_TEXT_VALUE, }).setOrigin(0, 0.5).setDepth(21); @@ -434,11 +438,11 @@ export default class WolfensteinGame extends Phaser.Scene { // fallback texture has nothing to swap to. this._profileHasFrames = this.textures.exists('wolfenstein-profile'); objs.portrait = this.add - .image(GAME_WIDTH / 2, y, this._profileHasFrames ? 'wolfenstein-profile' : 'wolf-profile') + .image(GAME_WIDTH / 2, HUD_BAR_Y, this._profileHasFrames ? 'wolfenstein-profile' : 'wolf-profile') .setDisplaySize(HUD_PORTRAIT_SIZE, HUD_PORTRAIT_SIZE).setDepth(21); - // Right panel: INVENTORY: + up to 3 held-key icons above ammo + the - // equipped weapon's icon. Both icon rows reuse wolfenstein-pickups — + // Right panel: INVENTORY: + up to 3 held-key icons above the equipped + // weapon's icon + ammo. Both icon rows reuse wolfenstein-pickups — // already loaded for world pickups, same art the player already // recognizes, no separate HUD-icon asset needed. Frame lookups are // built from rules data (not re-hardcoded numbers) so the HUD can never @@ -453,7 +457,7 @@ export default class WolfensteinGame extends Phaser.Scene { this.rules.items.filter((i) => i.kind === 'key').map((i) => [i.color, i.frame]), ); - objs.inventoryLabel = this.add.text(1725, labelY, 'INVENTORY:', { + objs.inventoryLabel = this.add.text(1725, HUD_LABEL_Y, 'INVENTORY:', { fontFamily: FONT, fontSize: '22px', color: HUD_TEXT_LABEL, letterSpacing: 2, }).setOrigin(1, 0.5).setDepth(21); // Anchored near the right edge (not adjacent to the label's own @@ -463,21 +467,28 @@ export default class WolfensteinGame extends Phaser.Scene { const keyX = { blue: 1751, red: 1785, yellow: 1819 }; for (const color of ['blue', 'red', 'yellow']) { objs[`key_${color}`] = this.add - .image(keyX[color], labelY, 'wolfenstein-pickups', this._keyIconFrame[color]) + .image(keyX[color], HUD_LABEL_Y, 'wolfenstein-pickups', this._keyIconFrame[color]) .setDisplaySize(30, 30).setDepth(21).setVisible(false); } - // Icon sits to the RIGHT of the ammo text's right-edge anchor, so a - // longer ammo count (up to "AMMO 200") can only grow further left, - // never toward — let alone under — the icon. Starts hidden (like the - // key icons above) rather than showing frame 0's pistol icon for one - // tick regardless of what's actually equipped — _updateHud's first - // pass sets both the correct frame and visibility together before this - // is ever actually seen. - objs.weaponIcon = this.add.image(1815, valueY, 'wolfenstein-pickups').setDisplaySize(48, 48).setDepth(21).setVisible(false); - objs.ammo = this.add.text(1790, valueY, '', { + // Ammo stays right-anchored at a fixed edge (unchanged behavior, just a + // repositioned x); the weapon icon — on its own mid-gray rounded plate + // for contrast against the steel-blue bar — sits immediately to its + // LEFT. Because ammo text length varies ("" for melee up to "AMMO 200"), + // the icon+plate are repositioned every _updateHud tick relative to the + // ammo text's own actual rendered width, rather than a second fixed + // coordinate that a long count could grow into. Both start hidden (like + // the key icons above) rather than showing frame 0's pistol icon for + // one tick regardless of what's actually equipped — _updateHud's first + // pass sets the correct frame, position, and visibility together before + // either is ever actually seen. Background created first so it renders + // behind the icon at the same depth (Phaser draws same-depth objects in + // creation order). + objs.ammo = this.add.text(1840, HUD_VALUE_Y, '', { fontFamily: FONT, fontSize: '36px', fontStyle: 'bold', color: HUD_TEXT_VALUE, }).setOrigin(1, 0.5).setDepth(21); + objs.weaponIconBg = this.add.image(1720, HUD_VALUE_Y, 'wolf-hud-icon-bg').setDepth(21).setVisible(false); + objs.weaponIcon = this.add.image(1720, HUD_VALUE_Y, 'wolfenstein-pickups').setDisplaySize(44, 44).setDepth(21).setVisible(false); objs.toast = this.add.text(GAME_WIDTH / 2, GAME_HEIGHT - 170, '', { fontFamily: FONT, fontSize: '26px', color: COLORS.goldHex }).setOrigin(0.5).setDepth(21).setAlpha(0); objs.crosshair = this.add.text(GAME_WIDTH / 2, VIEW_H / 2, '+', { fontSize: '38px', color: COLORS.textHex }).setOrigin(0.5).setDepth(15); @@ -500,10 +511,11 @@ export default class WolfensteinGame extends Phaser.Scene { this.hud.key_blue.setVisible(false); this.hud.key_red.setVisible(false); this.hud.key_yellow.setVisible(false); - // Same reasoning — would otherwise flash frame 0's pistol icon for a - // tick regardless of what's actually equipped (e.g. a fresh - // fists-only campaign start). + // Same reasoning — would otherwise flash frame 0's pistol icon (and + // its background plate) for a tick regardless of what's actually + // equipped (e.g. a fresh fists-only campaign start). this.hud.weaponIcon.setVisible(false); + this.hud.weaponIconBg.setVisible(false); } } @@ -526,7 +538,19 @@ export default class WolfensteinGame extends Phaser.Scene { const iconFrame = this._weaponIconFrame[p.weapon]; const showWeaponIcon = this._pickupsTextureOk && iconFrame != null; this.hud.weaponIcon.setVisible(showWeaponIcon); - if (showWeaponIcon) this.hud.weaponIcon.setFrame(iconFrame); + this.hud.weaponIconBg.setVisible(showWeaponIcon); + if (showWeaponIcon) { + this.hud.weaponIcon.setFrame(iconFrame); + // Icon + its background plate sit immediately left of the ammo + // text's own rendered left edge — recomputed every tick since that + // edge moves with the ammo count's actual digit width (e.g. "AMMO 8" + // vs "AMMO 200"), rather than a second fixed coordinate a long count + // could grow into and collide with. + const ammoLeftEdge = this.hud.ammo.x - this.hud.ammo.width; + const iconX = ammoLeftEdge - 16 - this.hud.weaponIconBg.displayWidth / 2; + this.hud.weaponIconBg.setX(iconX); + this.hud.weaponIcon.setX(iconX); + } this.hud.lockHint.setVisible(!this._locked); this.hud.key_blue.setVisible(p.keys.includes('blue'));