import { Vector3 } from "three"; import { CameraC_internal, UpdateController } from "@hitplay/playable_template"; import { ThreeC } from "./ThreeC"; import { resourceCounterBgSrc } from "../resources/images/images"; import { projectScreenPointToWorldRay } from "../utils/screenWorldProjection"; // See HudC's identical constant — same reasoning, this is a second instance // of the same anchor technique for a second counter. const ANCHOR_DISTANCE = 2.5; // Second resource counter, stacked directly below HudC's own wood counter // (see .resource-hud--pistol in ui.css). The zombie-part chunks dropped by // kills (see ZombiePartC, spent on the pistol's own progression — see // PistolLevelC / its Pay Zone) get the exact same fly-to-a-HUD-counter // treatment wood does, just their own plate and their own live balance. // // Reuses HudC's plate art rather than shipping a second bespoke asset — a // CSS hue-rotate on the plate (see ui.css) is what reads it as a different // currency. export class PistolResourceHudC { private static initialized = false; private static plateEl: HTMLElement; private static countTextEl: HTMLElement; private static lastDisplayedCount = -1; private static balanceGetter: (() => number) | null = null; static init() { if (this.initialized) return; this.initialized = true; this.buildDom(); UpdateController.Instance.onUpdate.addDelegate(() => this.update()); } // Same technique as HudC.getWorldAnchorPosition — see its own comment. static getWorldAnchorPosition(): Vector3 { const canvasRect = ThreeC.renderer.domElement.getBoundingClientRect(); const plateRect = this.plateEl.getBoundingClientRect(); const screenX = plateRect.right; const screenY = plateRect.top + plateRect.height / 2; const camera = CameraC_internal.getCamera(); const { origin, direction } = projectScreenPointToWorldRay(camera, canvasRect, screenX, screenY); return origin.addScaledVector(direction, ANCHOR_DISTANCE); } // Injected rather than importing ZombiePartC/PayZoneC directly, matching // HudC.setBalanceGetter. static setBalanceGetter(getBalance: () => number) { this.balanceGetter = getBalance; } private static buildDom() { const holder = document.createElement("div"); holder.className = "resource-hud resource-hud--pistol"; const plate = document.createElement("div"); plate.className = "resource-hud__plate"; plate.style.backgroundImage = `url(${resourceCounterBgSrc})`; const count = document.createElement("span"); count.className = "resource-hud__count"; const countText = document.createElement("span"); countText.className = "gamefont-x"; countText.textContent = "0"; count.appendChild(countText); plate.appendChild(count); holder.appendChild(plate); const uiRoot = document.getElementById("ui-safe-area") ?? document.body; uiRoot.appendChild(holder); this.plateEl = plate; this.countTextEl = countText; } private static update() { if (!this.balanceGetter) return; const count = this.balanceGetter(); if (count === this.lastDisplayedCount) return; this.lastDisplayedCount = count; this.countTextEl.textContent = String(count); this.plateEl.classList.remove("is-bumping"); void this.plateEl.offsetWidth; this.plateEl.classList.add("is-bumping"); } }