# Health bar для дестройбл-елементів (2026-08-13) Референс-скріншот: прямокутний хелз бар з бордером, синя заливка HP, чорним — вже відняте попередніми ударами, білим — відняте саме поточним ударом (з коротким затуханням у чорне), число нанесеного урону над баром. Нехай 10 урону за удар, 100хп у цілого ящика. ## `BreakablePropC` — перехід з дискретного лічильника хітів на HP `HITS_PER_STAGE`/`hitsOnStage` повністю видалені. `BreakableProp` тепер несе `currentHp`/`maxHp` (`MAX_HP = 100`, `export`-нутий, щоб `HealthBarC` рахував той самий `0..1` дріб без дублювання числа) + `startStageNumber`/ `totalPossibleStages` — реальні номери з `_SN` у назвах стейдж-нод (не вигадані), з яких рахується, скільки HP пошкоджений з коробки крейт вже має "по дефолту" (пошкоджені S2/S3-старт крейти стартують не з 100). `hit()` тепер: відняти `HIT_DAMAGE`, повідомити `HealthBarC.onHit(...)`, знищити на HP<=0 (раніше — "просунувся за останній стейдж"), інакше — перевести `stageIndex` на `Math.floor((1 - currentHp/maxHp) * totalPossibleStages)` (з відступом на `startStageNumber`), змаплений у межі власного (можливо короткого) масиву стейджів. ## `HealthBarC.ts` — новий контролер, world-tracking DOM-бари Одна мапа `BreakableProp -> BarInstance`, живиться з `BreakablePropC.hit()` через `onHit`/`onDestroyed` (аналогічний DI-паттерн до `ResourceC.setFlyTarget`). **Позиціювання**: `position:fixed` + сирі px з `canvasRect` (та ж техніка, що й `HudC.getWorldAnchorPosition`, лише в протилежний бік — тепер винесена в `src/utils/screenWorldProjection.ts`, обидва напрямки). Точка прив'язки — реальний `Box3().setFromObject(prop.root)` (верх+центр X/Z), не вгаданий офсет. **Три кольори — чистий CSS, без пофреймової JS-анімації**: `.health-bar__white` й `.health-bar__blue` — два накладені div. На кожен хіт: білий миттєво (`transition-duration: 0s`) стрибає на ПОПЕРЕДНІЙ `%`, форс-reflow через `offsetWidth`, потім і білий (з `transition-delay`), і синій одразу виставляються на ПОТОЧНИЙ `%` — синій доїжджає без затримки, білий "зависає" на старому значенні й наздоганяє з лагом, показуючи чорний трек під собою в проміжку. Ніякого `requestAnimationFrame`/tween — сама CSS-transition timing і є ефектом. **Авто-приховування**: `idleTime` рахується щокадру, `>= HIDE_DELAY_S` (2с) без нового хіта — `is-hiding` клас + `FADE_OUT_MS` (250мс) opacity-transition, потім видалення з DOM. Скидається на 0 при кожному `onHit`. `onDestroyed` видаляє бар негайно (порожній бар не має лишатись висіти). **Числа урону — кілька раундів фідбеку, фінал**: - Спочатку плавали вгору з затуханням — користувач попросив натомість різку появу непрозорого числа (`-webkit-text-stroke`, не text-shadow-глов) з різким скейл-панчем до 1.5x + ротейтом і поверненням, і тільки ПОТІМ затуханням (не рухом). - Ротейт — послідовно звужувався: спершу довільний напрямок/кут, потім користувач уточнив "тільки по годинниковій стрілці, до 2 години дня" (тобто 0..60°, `--damage-rotate`, рандомізується в JS на кожен спавн, читається в keyframe через `var()`). - **Баг знайдений і виправлений**: затухання на спокої (force=0, у контексті джойстика — не плутати з цим, тут йдеться про сам панч) показувало прозору прогалину замість базового кольору, бо альфа інтерполювалась до 0 — фікс: альфа фіксована, змінюється лише RGB-канал (той самий принцип пізніше повторився і в джойстику, див. нижче). - Тривалість/розмір/швидкість анімації (920мс, +20% розмір шрифту, expo-out крива "швидкий старт — повільний кінець") — прямі числові правки за проханням користувача. ## `src/css/health-bar.css` — новий файл Слідує тому ж house convention, що й `ui.css` (одна `--health-bar-width` змінна, `--lvh` не сирий `vh`) — **окремо зазначено користувачем не чіпати**: файл (як і `ui.css`) свідомо не отримав landscape `@media`-блоку і досі на `vh`, не `rem`/`%` — відомий, прийнятий відступ від Day 8 чекліста, відкладений на потім.