diff --git a/src/configUIParams/globalSettings.ts b/src/configUIParams/globalSettings.ts index 9aad6d6..25e2cfc 100644 --- a/src/configUIParams/globalSettings.ts +++ b/src/configUIParams/globalSettings.ts @@ -88,7 +88,7 @@ export const globalSettings: ConfigUiParamsCategories[] = [ values: [ 30, 150, - 30 + 33 ] }, { @@ -106,7 +106,7 @@ export const globalSettings: ConfigUiParamsCategories[] = [ [ 0, 15, - 9 + 8 ], [ -10, @@ -125,7 +125,7 @@ export const globalSettings: ConfigUiParamsCategories[] = [ [ -360, 360, - 5 + 7 ], [ -360, diff --git a/src/controllers/Map/GatherC.ts b/src/controllers/Map/GatherC.ts index 0c0c1bf..1b217d1 100644 --- a/src/controllers/Map/GatherC.ts +++ b/src/controllers/Map/GatherC.ts @@ -2,6 +2,7 @@ import { EasyEvent, JoystickC, UpdateController } from "@24tools/playable_templa import { Vector3 } from "three"; import { PhysicsTriggerC } from "./PhysicsTriggerC"; import { PropC, PropRegistry } from "./PropC"; +import { PropHpUIC } from "./PropHpUIC"; import { Player } from "../Presets/Player"; export class GatherC { @@ -119,6 +120,7 @@ export class GatherC { if (Player.isMoving() || Player.isCombatBusy()) return; const targetPosition = this.getTargetPosition(props); + PropHpUIC.showFor(props); Player.startAutoAttack( () => this.onAttackStrike(), () => {}, diff --git a/src/controllers/Map/InteractiveZoneC.ts b/src/controllers/Map/InteractiveZoneC.ts index 8340270..01e19ed 100644 --- a/src/controllers/Map/InteractiveZoneC.ts +++ b/src/controllers/Map/InteractiveZoneC.ts @@ -57,7 +57,12 @@ export class InteractiveZoneC { if ("color" in cloned) { this.baseColors.set(cloned, (cloned.color as Color).clone()); } + cloned.depthWrite = true; + cloned.polygonOffset = true; + cloned.polygonOffsetFactor = 2; + cloned.polygonOffsetUnits = 2; mesh.material = cloned; + mesh.renderOrder = 0; }); this.refreshFillVisual(); diff --git a/src/controllers/Map/Map.ts b/src/controllers/Map/Map.ts index 55f3701..2cf27a9 100644 --- a/src/controllers/Map/Map.ts +++ b/src/controllers/Map/Map.ts @@ -22,12 +22,18 @@ export class Map { mapObject.position.set(0, 0, 0); ThreeC.setShadowsStateForChildren(mapObject, true, true); ThreeC.addToScene(mapObject); + this.hideReferenceHpUi(mapObject); PhysicsTriggerC.init(); this.buildPhysics(mapObject); InteractiveZoneC.init(mapObject); } + private static hideReferenceHpUi(mapObject: Object3D) { + const ui = mapObject.getObjectByName("UI"); + if (ui) ui.visible = false; + } + private static buildPhysics(mapObject: Object3D) { for (const layerName of MAP_PHYSICS_LAYERS) { const layer = mapObject.getObjectByName(layerName); diff --git a/src/controllers/Map/PropHpBar.ts b/src/controllers/Map/PropHpBar.ts index a6be40f..a81ffba 100644 --- a/src/controllers/Map/PropHpBar.ts +++ b/src/controllers/Map/PropHpBar.ts @@ -1,42 +1,108 @@ -import { Box3, Mesh, Object3D, Vector3 } from "three"; -import { CameraC } from "../CameraC"; +import { + Box3, + Color, + DoubleSide, + Material, + Matrix4, + Mesh, + MeshBasicMaterial, + MeshStandardMaterial, + Object3D, + Quaternion, + Vector3, +} from "three"; +import { ResourcesC } from "@24tools/playable_template"; +import { GLTF } from "three/examples/jsm/loaders/GLTFLoader"; +import { ThreeC } from "../ThreeC"; +import { ResourcesType } from "../Presets/Enums/ResourcesType"; import { PropC } from "./PropC"; -const BAR_Y_OFFSET = 0.8; -const BAR_X_OFFSET = -0.3; +const HP_BAR_MESH = "hpBar"; +const NODE_UI = "UI"; +const NODE_BACKGROUND = "UI_Background"; +const NODE_FOREGROUND = "UI_Foreground"; +const NODE_MIDDLEGROUND = "UI_Middleground"; + +const BAR_Y_OFFSET = 0.55; +const BAR_X_OFFSET = -0.2; const CATCHUP_SPEED = 2.5; +const BAR_WORLD_SCALE = 1; + +/** Transparent pass + renderOrder above all map decals. */ +export const OVERLAY_RENDER_ORDER = 10000; +const RENDER_ORDER_BACKGROUND = OVERLAY_RENDER_ORDER; +const RENDER_ORDER_MIDDLEGROUND = OVERLAY_RENDER_ORDER + 1; +const RENDER_ORDER_FOREGROUND = OVERLAY_RENDER_ORDER + 2; + +type FillPart = { + node: Object3D; + baseScaleX: number; + basePosX: number; + /** Local mesh min.x (negative); keeps left edge fixed when scaling. */ + anchorMinX: number; +}; export class PropHpBar { + static readonly OVERLAY_RENDER_ORDER = OVERLAY_RENDER_ORDER; + private static readonly identityMatrix = new Matrix4(); + private static readonly tempMatrix = new Matrix4(); + private static readonly tempPosition = new Vector3(); + private static readonly tempQuaternion = new Quaternion(); + private static readonly tempScale = new Vector3(); + private static readonly worldAnchor = new Vector3(); + private readonly prop: PropC; - private readonly root: HTMLElement; - private readonly currentEl: HTMLElement; - private readonly delayedEl: HTMLElement; - private readonly anchor = new Vector3(); + private readonly root: Object3D; + private readonly foreground: FillPart | null; + private readonly middleground: FillPart | null; private readonly bounds = new Box3(); private displayedHealth: number; private visible = false; - constructor(prop: PropC, parent: HTMLElement) { + static getPrefab(): Object3D | null { + const fromScene = ThreeC.getObject(HP_BAR_MESH); + if (fromScene) return fromScene; + + const gltf = ResourcesC.getResource(ResourcesType.Mesh, HP_BAR_MESH); + return gltf?.scene ?? null; + } + + constructor(prop: PropC, parent: Object3D) { + const template = PropHpBar.getPrefab(); + if (!template) { + throw new Error(`HP bar mesh resource not found: ${HP_BAR_MESH}`); + } + + const sourceUi = template.getObjectByName(NODE_UI); + if (!sourceUi) { + throw new Error(`HP bar node not found: ${NODE_UI}`); + } + this.prop = prop; this.displayedHealth = prop.health; - this.root = document.createElement("div"); - this.root.className = "prop-hp-bar"; + this.root = new Object3D(); + this.root.visible = false; + this.root.scale.setScalar(BAR_WORLD_SCALE); + this.root.frustumCulled = false; + this.root.renderOrder = RENDER_ORDER_FOREGROUND; - const track = document.createElement("div"); - track.className = "prop-hp-bar__track"; + const ui = sourceUi.clone(true); + this.bakeMatrices(ui); + this.centerGroup(ui); + this.prepareMeshes(ui); - this.delayedEl = document.createElement("div"); - this.delayedEl.className = "prop-hp-bar__delayed"; + this.root.add(ui); + parent.add(this.root); - this.currentEl = document.createElement("div"); - this.currentEl.className = "prop-hp-bar__current"; + const fgNode = ui.getObjectByName(NODE_FOREGROUND); + const mgNode = ui.getObjectByName(NODE_MIDDLEGROUND); + this.foreground = fgNode ? this.createFillPart(fgNode) : null; + this.middleground = mgNode ? this.createFillPart(mgNode) : null; - track.appendChild(this.delayedEl); - track.appendChild(this.currentEl); - this.root.appendChild(track); - parent.appendChild(this.root); + const background = ui.getObjectByName(NODE_BACKGROUND); + if (background) background.visible = true; this.applyWidths(); } @@ -61,32 +127,68 @@ export class PropHpBar { ); } - this.delayedEl.style.width = `${this.getHealthRatio(this.displayedHealth) * 100}%`; + if (this.middleground) { + this.applyFill(this.middleground, this.getHealthRatio(this.displayedHealth)); + } if (!this.visible) return; - this.updatePosition(); + this.updateTransform(); } setVisible(value: boolean) { this.visible = value; - this.root.classList.toggle("is-visible", value); + this.root.visible = value; if (value) { this.displayedHealth = this.prop.health; this.applyWidths(); - this.updatePosition(); + this.updateTransform(); } } destroy() { - this.root.remove(); + this.root.removeFromParent(); } private applyWidths() { - const ratio = this.getHealthRatio(this.prop.health); - this.currentEl.style.width = `${ratio * 100}%`; - this.delayedEl.style.width = `${this.getHealthRatio(this.displayedHealth) * 100}%`; + if (this.foreground) { + this.applyFill(this.foreground, this.getHealthRatio(this.prop.health)); + } + if (this.middleground) { + this.applyFill(this.middleground, this.getHealthRatio(this.displayedHealth)); + } + } + + private applyFill(part: FillPart, ratio: number) { + const clamped = Math.max(0, Math.min(1, ratio)); + part.node.scale.x = part.baseScaleX * clamped; + part.node.position.x = + part.basePosX + part.anchorMinX * part.baseScaleX * (1 - clamped); + } + + private createFillPart(node: Object3D): FillPart { + const meshMinX = this.getMeshMinX(node); + const isForeground = node.name.includes(NODE_FOREGROUND); + + node.renderOrder = isForeground + ? RENDER_ORDER_FOREGROUND + : RENDER_ORDER_MIDDLEGROUND; + + return { + node, + baseScaleX: node.scale.x, + basePosX: node.position.x, + anchorMinX: meshMinX, + }; + } + + private getMeshMinX(node: Object3D) { + const mesh = node as Mesh; + if (!mesh.isMesh || !mesh.geometry) return -0.935; + + mesh.geometry.computeBoundingBox(); + return mesh.geometry.boundingBox?.min.x ?? -0.935; } private getHealthRatio(health: number) { @@ -94,78 +196,138 @@ export class PropHpBar { return Math.max(health, 0) / this.prop.maxHealth; } - private updatePosition() { + private updateTransform() { if (!this.computeVisualBounds()) { - this.root.style.display = "none"; + this.root.visible = false; return; } - this.bounds.getCenter(this.anchor); - this.anchor.x += BAR_X_OFFSET; - this.anchor.y = this.bounds.max.y + BAR_Y_OFFSET; + PropHpBar.worldAnchor.set( + (this.bounds.min.x + this.bounds.max.x) * 0.5 + BAR_X_OFFSET, + this.bounds.max.y + BAR_Y_OFFSET, + (this.bounds.min.z + this.bounds.max.z) * 0.5, + ); - const camera = CameraC.camera; - const projected = this.anchor.project(camera); - - if (projected.z > 1) { - this.root.style.display = "none"; - return; - } - - const screenPos = PropHpBar.worldToLayerPosition(projected, this.root.parentElement); - if (!screenPos) { - this.root.style.display = "none"; - return; - } - - this.root.style.display = ""; - this.root.style.left = `${screenPos.x}px`; - this.root.style.top = `${screenPos.y}px`; - } - - private static worldToLayerPosition( - projected: Vector3, - layer: HTMLElement | null, - ): { x: number; y: number } | null { - const canvas = document.querySelector("canvas"); - if (!canvas || !layer) return null; - - const canvasRect = canvas.getBoundingClientRect(); - const layerRect = layer.getBoundingClientRect(); - - return { - x: - (projected.x * 0.5 + 0.5) * canvasRect.width + - canvasRect.left - - layerRect.left, - y: - (-projected.y * 0.5 + 0.5) * canvasRect.height + - canvasRect.top - - layerRect.top, - }; + this.root.position.copy(PropHpBar.worldAnchor); + this.root.quaternion.set(0, 0, 0, 1); + this.root.visible = this.visible; } private computeVisualBounds() { this.prop.object.updateWorldMatrix(true, true); this.bounds.makeEmpty(); - const includeMesh = (node: Object3D) => { - if (!(node as Mesh).isMesh) return; + this.prop.object.traverse((child) => { + if (!(child as Mesh).isMesh) return; - const name = node.name.toLowerCase(); + const name = child.name.toLowerCase(); if (name.includes("collider") || name.includes("trigger") || name.includes("gathertrigger")) { return; } - this.bounds.expandByObject(node); - }; - - includeMesh(this.prop.object); - this.prop.object.traverse((child) => includeMesh(child)); + this.bounds.expandByObject(child); + }); if (!this.bounds.isEmpty()) return true; this.bounds.setFromObject(this.prop.object); return !this.bounds.isEmpty(); } + + private bakeMatrices(ui: Object3D) { + this.decomposeNodeMatrix(ui); + ui.traverse((child) => { + if (child === ui) return; + this.decomposeNodeMatrix(child); + }); + } + + private decomposeNodeMatrix(node: Object3D) { + node.updateMatrix(); + PropHpBar.tempMatrix.copy(node.matrix); + if (PropHpBar.tempMatrix.equals(PropHpBar.identityMatrix)) return; + + PropHpBar.tempMatrix.decompose( + PropHpBar.tempPosition, + PropHpBar.tempQuaternion, + PropHpBar.tempScale, + ); + + node.matrix.identity(); + node.position.copy(PropHpBar.tempPosition); + node.quaternion.copy(PropHpBar.tempQuaternion); + node.scale.copy(PropHpBar.tempScale); + node.matrixAutoUpdate = true; + } + + private centerGroup(group: Object3D) { + const box = new Box3().setFromObject(group); + const center = new Vector3(); + box.getCenter(center); + group.position.sub(center); + } + + private prepareMeshes(root: Object3D) { + root.traverse((child) => { + child.frustumCulled = false; + + if (!(child as Mesh).isMesh) return; + + const mesh = child as Mesh; + mesh.castShadow = false; + mesh.receiveShadow = false; + + if (child.name.includes(NODE_BACKGROUND)) { + child.renderOrder = RENDER_ORDER_BACKGROUND; + } else if (child.name.includes(NODE_MIDDLEGROUND)) { + child.renderOrder = RENDER_ORDER_MIDDLEGROUND; + } else if (child.name.includes(NODE_FOREGROUND)) { + child.renderOrder = RENDER_ORDER_FOREGROUND; + } + + if (Array.isArray(mesh.material)) { + mesh.material = mesh.material.map((material) => this.toVisibleMaterial(material)); + } else { + mesh.material = this.toVisibleMaterial(mesh.material); + } + }); + } + + /** Keeps GLB map/color; unlit basic material renders reliably in playable. */ + private toVisibleMaterial(source: Material) { + if (source instanceof MeshBasicMaterial) { + const cloned = source.clone(); + cloned.side = DoubleSide; + cloned.depthTest = false; + cloned.depthWrite = false; + cloned.toneMapped = false; + cloned.transparent = true; + return cloned; + } + + const src = source as MeshStandardMaterial; + const material = new MeshBasicMaterial({ + side: DoubleSide, + depthTest: false, + depthWrite: false, + toneMapped: false, + transparent: true, + opacity: src.opacity ?? 1, + }); + + if (src.map) { + material.map = src.map; + } + if (src.color) { + material.color.copy(src.color); + } else { + material.color = new Color(0xffffff); + } + if (src.alphaTest) { + material.alphaTest = src.alphaTest; + material.transparent = true; + } + + return material; + } } diff --git a/src/controllers/Map/PropHpUIC.ts b/src/controllers/Map/PropHpUIC.ts index 7f4e961..9568662 100644 --- a/src/controllers/Map/PropHpUIC.ts +++ b/src/controllers/Map/PropHpUIC.ts @@ -1,4 +1,6 @@ +import { Object3D } from "three"; import { UpdateController } from "@24tools/playable_template"; +import { ThreeC } from "../ThreeC"; import { PropC } from "./PropC"; import { PropHpBar } from "./PropHpBar"; @@ -7,20 +9,15 @@ const DEBUG_ALWAYS_VISIBLE_HP_BARS = false; export class PropHpUIC { private static inited = false; - private static root: HTMLElement | null = null; + private static overlayLayer: Object3D | null = null; private static bars = new Map(); static init() { if (this.inited) return; this.inited = true; - const uiRoot = document.getElementById("ui"); - if (!uiRoot) return; - - this.root = document.createElement("div"); - this.root.id = "prop-hp-layer"; - this.root.className = "prop-hp-layer"; - uiRoot.appendChild(this.root); + this.hideSceneTemplate(); + this.ensureOverlayLayer(); UpdateController.Instance.onUpdate.addDelegate((delta) => { for (const bar of this.bars.values()) { @@ -29,10 +26,31 @@ export class PropHpUIC { }); } - static createBar(prop: PropC): PropHpBar | undefined { - if (!this.root || this.bars.has(prop)) return this.bars.get(prop); + /** Call after map/scene objects are added so overlay renders last. */ + static bringOverlayToFront() { + this.ensureOverlayLayer(); + const layer = this.overlayLayer; + if (!layer) return; - const bar = new PropHpBar(prop, this.root); + let parent = layer.parent; + if (!parent) { + ThreeC.addToScene(layer); + parent = layer.parent; + if (!parent) return; + } + + parent.remove(layer); + parent.add(layer); + } + + static createBar(prop: PropC): PropHpBar | undefined { + if (!PropHpBar.getPrefab()) { + console.warn("HP bar prefab not loaded"); + return undefined; + } + if (this.bars.has(prop)) return this.bars.get(prop); + + const bar = new PropHpBar(prop, this.getOverlayLayer()); this.bars.set(prop, bar); if (DEBUG_ALWAYS_VISIBLE_HP_BARS) { @@ -79,4 +97,27 @@ export class PropHpUIC { bar.destroy(); this.bars.delete(prop); } + + private static hideSceneTemplate() { + const template = PropHpBar.getPrefab(); + if (!template || !template.parent) return; + + template.visible = false; + template.removeFromParent(); + } + + private static ensureOverlayLayer() { + if (!this.overlayLayer) { + this.overlayLayer = new Object3D(); + this.overlayLayer.name = "prop-hp-overlay"; + this.overlayLayer.frustumCulled = false; + this.overlayLayer.renderOrder = PropHpBar.OVERLAY_RENDER_ORDER; + ThreeC.addToScene(this.overlayLayer); + } + } + + private static getOverlayLayer() { + this.ensureOverlayLayer(); + return this.overlayLayer!; + } } diff --git a/src/controllers/TestSceneC.ts b/src/controllers/TestSceneC.ts index 1ea824f..3a50ff2 100644 --- a/src/controllers/TestSceneC.ts +++ b/src/controllers/TestSceneC.ts @@ -9,17 +9,21 @@ import { ResourceDepositC } from "./Resources/ResourceDepositC"; import { PropHpUIC } from "./Map/PropHpUIC"; import { PropVfxC } from "./Map/PropVfxC"; import { InvasionProgressUIC } from "./UI/InvasionProgressUIC"; +import { PreGameplayUIC } from "./UI/PreGameplayUIC"; export class TestSceneC { static init() { InvasionProgressUIC.init(); ResourceUIC.init(); + PreGameplayUIC.init(); ResourceFlyC.init(); PropHpUIC.init(); PropVfxC.Init(); Map.init(); + PropHpUIC.bringOverlayToFront(); GatherC.init(); Player.init(); + PropHpUIC.bringOverlayToFront(); const mapObject = ThreeC.getObject("map"); if (mapObject) { diff --git a/src/controllers/UI/PreGameplayUIC.ts b/src/controllers/UI/PreGameplayUIC.ts new file mode 100644 index 0000000..6a32156 --- /dev/null +++ b/src/controllers/UI/PreGameplayUIC.ts @@ -0,0 +1,45 @@ +export class PreGameplayUIC { + private static root: HTMLElement | null = null; + + static init() { + const ui = document.getElementById("ui"); + if (!ui) return; + + ui.classList.add("is-pre-gameplay"); + + const root = document.createElement("div"); + root.className = "pre-gameplay"; + + const hurry = document.createElement("p"); + hurry.className = "pre-gameplay__hurry"; + hurry.textContent = "HURRY UP"; + + const bottom = document.createElement("div"); + bottom.className = "pre-gameplay__bottom"; + + const drag = document.createElement("p"); + drag.className = "pre-gameplay__drag"; + drag.textContent = "DRAG TO MOVE"; + + const joystick = document.createElement("div"); + joystick.className = "pre-gameplay-joystick"; + + const base = document.createElement("div"); + base.className = "pre-gameplay-joystick__base"; + + const knob = document.createElement("div"); + knob.className = "pre-gameplay-joystick__knob"; + + joystick.append(base, knob); + bottom.append(drag, joystick); + root.append(hurry, bottom); + ui.appendChild(root); + this.root = root; + } + + static dismiss() { + document.getElementById("ui")?.classList.remove("is-pre-gameplay"); + this.root?.remove(); + this.root = null; + } +} diff --git a/src/css/preGameplay.css b/src/css/preGameplay.css new file mode 100644 index 0000000..0c81e0b --- /dev/null +++ b/src/css/preGameplay.css @@ -0,0 +1,154 @@ +#ui.is-pre-gameplay .hud-invasion-title, +#ui.is-pre-gameplay .hud-progress, +#ui.is-pre-gameplay .hud-top-right { + display: none !important; +} + +.pre-gameplay { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 11; +} + +.pre-gameplay__hurry { + position: absolute; + top: 14vh; + left: 50%; + transform: translateX(-50%); + margin: 0; + font-size: 5vh; + font-weight: 700; + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.04em; + text-align: center; + line-height: 1; + white-space: nowrap; + -webkit-text-stroke: 0.14vh #000000; + paint-order: stroke fill; + animation: pre-gameplay-pulse 1.2s ease-in-out infinite; +} + +.pre-gameplay__bottom { + position: absolute; + left: 50%; + bottom: 10vh; + transform: translateX(-50%); + display: flex; + flex-direction: column; + align-items: center; + gap: 2.5vh; +} + +.pre-gameplay__drag { + margin: 0; + font-size: 4.2vh; + font-weight: 700; + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.04em; + text-align: center; + line-height: 1; + white-space: nowrap; + -webkit-text-stroke: 0.12vh #000000; + paint-order: stroke fill; +} + +.pre-gameplay-joystick { + position: relative; + width: 22vh; + height: 22vh; +} + +.pre-gameplay-joystick__base { + position: absolute; + inset: 0; + border-radius: 50%; + border: 0.35vh solid rgba(255, 255, 255, 0.55); + background: rgba(255, 255, 255, 0.12); + box-sizing: border-box; +} + +.pre-gameplay-joystick__knob { + position: absolute; + left: 50%; + top: 50%; + width: 8vh; + height: 8vh; + margin-left: -4vh; + margin-top: -4vh; + border-radius: 50%; + background: #ffffff; + animation: pre-gameplay-joystick-knob 2.4s ease-in-out infinite; +} + +@keyframes pre-gameplay-pulse { + 0%, + 100% { + transform: translateX(-50%) scale(1); + } + 50% { + transform: translateX(-50%) scale(1.06); + } +} + +@keyframes pre-gameplay-joystick-knob { + 0%, + 100% { + transform: translate(0, 0); + } + 25% { + transform: translate(5vh, 0); + } + 50% { + transform: translate(0, -3.5vh); + } + 75% { + transform: translate(-5vh, 0); + } +} + +@media (orientation: landscape) { + .pre-gameplay__hurry { + top: 8vh; + font-size: 4vh; + } + + .pre-gameplay__bottom { + bottom: 6vh; + gap: 1.5vh; + } + + .pre-gameplay__drag { + font-size: 3.2vh; + } + + .pre-gameplay-joystick { + width: 18vh; + height: 18vh; + } + + .pre-gameplay-joystick__knob { + width: 6.5vh; + height: 6.5vh; + margin-left: -3.25vh; + margin-top: -3.25vh; + } + + @keyframes pre-gameplay-joystick-knob { + 0%, + 100% { + transform: translate(0, 0); + } + 25% { + transform: translate(4vh, 0); + } + 50% { + transform: translate(0, -2.8vh); + } + 75% { + transform: translate(-4vh, 0); + } + } +} diff --git a/src/css/ui.css b/src/css/ui.css index 290e6b1..49ba7be 100644 --- a/src/css/ui.css +++ b/src/css/ui.css @@ -184,58 +184,6 @@ z-index: 20; } -.prop-hp-layer { - position: absolute; - inset: 0; - pointer-events: none; -} - -.prop-hp-bar { - position: absolute; - width: 8vh; - height: 1.2vh; - transform: translate(-50%, -100%); - opacity: 0; - visibility: hidden; - pointer-events: none; - z-index: 15; - transition: opacity 0.15s ease; -} - -.prop-hp-bar.is-visible { - opacity: 1; - visibility: visible; -} - -.prop-hp-bar__track { - position: relative; - width: 100%; - height: 100%; - background: #000000; - border-radius: 0.25vh; - overflow: hidden; -} - -.prop-hp-bar__delayed, -.prop-hp-bar__current { - position: absolute; - left: 0; - top: 0; - height: 100%; - border-radius: 0.25vh; - width: 100%; -} - -.prop-hp-bar__delayed { - background: #ffffff; - z-index: 1; -} - -.prop-hp-bar__current { - background: #3b8bff; - z-index: 2; -} - @media (orientation: landscape) { .hud-top-center { top: 1.2vh; diff --git a/src/index.html b/src/index.html index 7b6c863..9b05361 100644 --- a/src/index.html +++ b/src/index.html @@ -12,6 +12,7 @@ + diff --git a/src/resources/meshes/hpBar.glb b/src/resources/meshes/hpBar.glb new file mode 100644 index 0000000..40538bd Binary files /dev/null and b/src/resources/meshes/hpBar.glb differ diff --git a/src/resources/meshes/meshes.ts b/src/resources/meshes/meshes.ts index cf774d9..32faa4c 100644 --- a/src/resources/meshes/meshes.ts +++ b/src/resources/meshes/meshes.ts @@ -17,6 +17,10 @@ export const meshes : ConvertResourceType = { name: "wood", value: ConvertToBase64WhenRelease("./wood.glb"), }, + { + name: "hpBar", + value: ConvertToBase64WhenRelease("./hpBar.glb"), + }, ], loader: Template3d.meshLoader } \ No newline at end of file diff --git a/src/templateConfig/firstClickCb.ts b/src/templateConfig/firstClickCb.ts index e15cb6b..4a55e57 100644 --- a/src/templateConfig/firstClickCb.ts +++ b/src/templateConfig/firstClickCb.ts @@ -1,5 +1,7 @@ import { InvasionProgressUIC } from "../controllers/UI/InvasionProgressUIC"; +import { PreGameplayUIC } from "../controllers/UI/PreGameplayUIC"; export const firstClickCb: () => void = () => { + PreGameplayUIC.dismiss(); InvasionProgressUIC.startTimer(); -}; +}; \ No newline at end of file diff --git a/stats.html b/stats.html index 204e14d..a18ed10 100644 --- a/stats.html +++ b/stats.html @@ -4929,7 +4929,7 @@ var drawChart = (function (exports) {