TS / Gaming / bejson_input.ts

System
TS
Family
Gaming
API Density
1

[ CLONE REPO ON GITHUB ]

Public API Surface

  • method constructor

Full Source Implementation

FILE // bejson_input.ts
/**
 * Library:      bejson_input.ts
 * Family:       Gaming
 * Jurisdiction: ["BEJSON_LIBRARIES", "TS"]
 * Status:       OFFICIAL
 * Author:       Elton Boehnen
 * Version:      2.0 OFFICIAL
 * MFDB Version: 1.31
 * Format_Creator: Elton Boehnen
 * Date:         2026-05-18
 * Description:  User input mapping and handling for interactive BEJSON applications.
 */

// bejson_input.ts
export class BEJSONInput {
  public deadzone: number;
  public keys: Record<string, boolean>;
  public justPressed: Record<string, boolean>;
  public touch: { active: boolean; x: number; y: number; vector: { x: number; y: number } };
  public controls: any;

  constructor(options: any = {}) {
    this.deadzone = options.deadzone || 12;
    this.keys = {};
    this.justPressed = {};
    this.touch = { active: false, x: 0, y: 0, vector: { x: 0, y: 0 } };
    
    this.controls = {
      joystick: { x: 80, y: 0, radius: 50, active: false, handleX: 0, handleY: 0 },
      btnA: { x: 0, y: 0, radius: 30, pressed: false },
      btnB: { x: 0, y: 0, radius: 30, pressed: false }
    };

    if (typeof window !== 'undefined') {
      window.addEventListener('keydown', (e) => this._onKey(e, true));
      window.addEventListener('keyup', (e) => this._onKey(e, false));
      window.addEventListener('touchstart', (e) => this._onTouch(e, true), { passive: false });
      window.addEventListener('touchmove', (e) => this._onTouch(e, true), { passive: false });
      window.addEventListener('touchend', (e) => this._onTouch(e, false));
    }
  }

  private _onKey(e: KeyboardEvent, isDown: boolean) {
    if (isDown && !this.keys[e.key]) this.justPressed[e.key] = true;
    this.keys[e.key] = isDown;
  }

  private _onTouch(e: TouchEvent, isActive: boolean) {
    if (!isActive) {
      this.touch.active = false;
      this.controls.joystick.active = false;
      this.controls.joystick.handleX = 0;
      this.controls.joystick.handleY = 0;
      this.controls.btnA.pressed = false;
      this.controls.btnB.pressed = false;
      return;
    }

    const canvas = document.querySelector('canvas');
    if (!canvas) return;
    const rect = canvas.getBoundingClientRect();
    for (let i = 0; i < e.touches.length; i++) {
      const t = e.touches[i];
      const tx = t.clientX - rect.left;
      const ty = t.clientY - rect.top;

      // Check Joystick (Floating Joystick / Dynamic Anchor fix)
      const j = this.controls.joystick;
      const distJ = Math.sqrt((tx - j.x)**2 + (ty - j.y)**2);
      
      if (distJ < j.radius * 2 || j.active) {
        j.active = true;
        let dx = tx - j.x;
        let dy = ty - j.y;
        let dist = Math.sqrt(dx*dx + dy*dy);
        const limit = j.radius;

        if (dist > limit) {
          const angle = Math.atan2(dy, dx);
          j.x = tx - Math.cos(angle) * limit;
          j.y = ty - Math.sin(angle) * limit;
          dx = tx - j.x;
          dy = ty - j.y;
          dist = limit;
        }

        j.handleX = dx;
        j.handleY = dy;
        this.touch.vector.x = j.handleX / limit;
        this.touch.vector.y = j.handleY / limit;
      }

      // Check Buttons
      if (this._checkCircle(tx, ty, this.controls.btnA)) {
        if (!this.controls.btnA.pressed) this.justPressed['action'] = true;
        this.controls.btnA.pressed = true;
      }
      if (this._checkCircle(tx, ty, this.controls.btnB)) {
        if (!this.controls.btnB.pressed) this.justPressed['cancel'] = true;
        this.controls.btnB.pressed = true;
      }
    }
    if (e.cancelable) e.preventDefault();
  }

  private _checkCircle(tx: number, ty: number, circle: any) {
    const dist = Math.sqrt((tx - circle.x)**2 + (ty - circle.y)**2);
    return dist < circle.radius;
  }

  private _isBoundDown(action: string) {
    const bindings: Record<string, string[]> = {
      up: ['ArrowUp', 'w', 'W'], down: ['ArrowDown', 's', 'S'],
      left: ['ArrowLeft', 'a', 'A'], right: ['ArrowRight', 'd', 'D'],
      action: ['Enter', ' '], cancel: ['Escape', 'x', 'X'], menu: ['m', 'M', 'Tab']
    };
    return bindings[action].some(k => this.keys[k]);
  }

  public getVector() {
    let vx = 0, vy = 0;
    if (this._isBoundDown('left')) vx -= 1;
    if (this._isBoundDown('right')) vx += 1;
    if (this._isBoundDown('up')) vy -= 1;
    if (this._isBoundDown('down')) vy += 1;

    if (this.controls.joystick.active) {
      vx += this.touch.vector.x;
      vy += this.touch.vector.y;
    }

    const mag = Math.sqrt(vx * vx + vy * vy);
    if (mag > 1) { vx /= mag; vy /= mag; }
    return { x: vx, y: vy, action: this.justPressed['action'], cancel: this.justPressed['cancel'] };
  }

  public update() {
    this.justPressed = {};
  }

  public renderControls(renderer: any) {
    const j = this.controls.joystick;
    const canvas = renderer.canvas;
    const h = canvas.height / renderer.dpr;
    const w = canvas.width / renderer.dpr;

    j.y = h - 80;
    
    renderer.drawCircle(j.x, j.y, j.radius, "rgba(255,255,255,0.2)", true);
    renderer.drawCircle(j.x + j.handleX, j.y + j.handleY, j.radius/2, "rgba(255,255,255,0.5)", true);

    const bA = this.controls.btnA;
    const bB = this.controls.btnB;
    bA.x = w - 60; bA.y = h - 100;
    bB.x = w - 120; bB.y = h - 60;

    renderer.drawCircle(bA.x, bA.y, bA.radius, bA.pressed ? "#f56565" : "rgba(255,255,255,0.3)", true);
    renderer.drawText("A", bA.x - 5, bA.y + 5, { isHUD: true });

    renderer.drawCircle(bB.x, bB.y, bB.radius, bB.pressed ? "#4299e1" : "rgba(255,255,255,0.3)", true);
    renderer.drawText("B", bB.x - 5, bB.y + 5, { isHUD: true });
  }
}
built from BEJSON HTML3 Libraries 2.0