Files
onboarding-project/src/controllers/PistolResourceHudC.ts
T
Oleksandr Vlasiuk 1d38990d7f some changes
2026-08-17 12:36:56 +03:00

93 lines
3.3 KiB
TypeScript

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